<commit_before>// Copyright 2014 The gocui 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 gocui

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"strings"

	"github.com/nsf/termbox-go"
)

// A View is a window. It maintains its own internal buffer and cursor
// position.
type View struct {
	name           string
	x0, y0, x1, y1 int
	ox, oy         int
	cx, cy         int
	lines          [][]rune
	overwrite      bool // overwrite in edit mode
	readOffset     int
	readCache      string

	// BgColor and FgColor allow to configure the background and foreground
	// colors of the View.
	BgColor, FgColor Attribute

	// SelBgColor and SelFgColor are used to configure the background and
	// foreground colors of the selected line, when it is highlighted.
	SelBgColor, SelFgColor Attribute

	// If Editable is true, keystrokes will be added to the view's internal
	// buffer at the cursor position.
	Editable bool

	// If Highlight is true, Sel{Bg,Fg}Colors will be used
	// for the line under the cursor position.
	Highlight bool

	// If Frame is true, a border will be drawn around the view
	Frame bool

	// If Wrap is true, the content that is written to this View is
	// automatically wrapped when it is longer than its width
	Wrap bool

	// If Wrap is true, each wrapping line is prefixed with this prefix.
	WrapPrefix string
}

// newView returns a new View object.
func newView(name string, x0, y0, x1, y1 int) *View {
	v := &View{
		name:  name,
		x0:    x0,
		y0:    y0,
		x1:    x1,
		y1:    y1,
		Frame: true,
	}
	return v
}

// Size returns the number of visible columns and rows in the View.
func (v *View) Size() (x, y int) {
	return v.x1 - v.x0 - 1, v.y1 - v.y0 - 1
}

// Name returns the name of the view.
func (v *View) Name() string {
	return v.name
}

// setRune writes a rune at the given point, relative to the view. It
// checks if the position is valid and applies the view's colors, taking
// into account if the cell must be highlighted.
func (v *View) setRune(x, y int, ch rune) error {
	maxX, maxY := v.Size()
	if x < 0 || x >= maxX || y < 0 || y >= maxY {
		return errors.New("invalid point")
	}

	var fgColor, bgColor Attribute
	if v.Highlight && y == v.cy {
		fgColor = v.SelFgColor
		bgColor = v.SelBgColor
	} else {
		fgColor = v.FgColor
		bgColor = v.BgColor
	}
	termbox.SetCell(v.x0+x+1, v.y0+y+1, ch,
		termbox.Attribute(fgColor), termbox.Attribute(bgColor))
	return nil
}

// SetCursor sets the cursor position of the view at the given point,
// relative to the view. It checks if the position is valid.
func (v *View) SetCursor(x, y int) error {
	maxX, maxY := v.Size()
	if x < 0 || x >= maxX || y < 0 || y >= maxY {
		return errors.New("invalid point")
	}
	v.cx = x
	v.cy = y
	return nil
}

// Cursor returns the cursor position of the view.
func (v *View) Cursor() (x, y int) {
	return v.cx, v.cy
}

// SetOrigin sets the origin position of the view's internal buffer,
// so the buffer starts to be printed from this point, which means that
// it is linked with the origin point of view. It can be used to
// implement Horizontal and Vertical scrolling with just incrementing
// or decrementing ox and oy.
func (v *View) SetOrigin(x, y int) error {
	if x < 0 || y < 0 {
		return errors.New("invalid point")
	}
	v.ox = x
	v.oy = y
	return nil
}

// Origin returns the origin position of the view.
func (v *View) Origin() (x, y int) {
	return v.ox, v.oy
}

// Write appends a byte slice into the view's internal buffer. Because
// View implements the io.Writer interface, it can be passed as parameter
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
// be called to clear the view's buffer.
func (v *View) Write(p []byte) (n int, err error) {
	r := bytes.NewReader(p)
	s := bufio.NewScanner(r)
	for s.Scan() {
		line := bytes.Runes(s.Bytes())
		v.lines = append(v.lines, line)
	}
	if err := s.Err(); err != nil {
		return 0, err
	}
	return len(p), nil
}

// Read reads data into p. It returns the number of bytes read into p.
// At EOF, err will be io.EOF. Calling Read() after Rewind() makes the
// cache to be refreshed with the contents of the view.
func (v *View) Read(p []byte) (n int, err error) {
	if v.readOffset == 0 {
		v.readCache = v.Buffer()
	}
	if v.readOffset < len(v.readCache) {
		n = copy(p, v.readCache[v.readOffset:])
		v.readOffset += n
	} else {
		err = io.EOF
	}
	return
}

// Rewind sets the offset for the next Read to 0, which also refresh the
// read cache.
func (v *View) Rewind() {
	v.readOffset = 0
}

// draw re-draws the view's contents.
func (v *View) draw() error {
	maxX, maxY := v.Size()
	y := 0
	for i, line := range v.lines {
		if i < v.oy {
			continue
		}
		x := 0
		for j, ch := range line {
			if j < v.ox {
				continue
			}
			if x == maxX && v.Wrap {
				x = 0
				y++
				for _, p := range v.WrapPrefix + string(ch) {
					if x >= maxX || y >= maxY {
						break
					}
					if err := v.setRune(x, y, p); err != nil {
						return err
					}
					x++
				}
			} else if x < maxX && y < maxY {
				if err := v.setRune(x, y, ch); err != nil {
					return err
				}
				x++
			}
		}
		y++
	}
	return nil
}

// Clear empties the view's internal buffer.
func (v *View) Clear() {
	v.lines = nil
	v.clearRunes()
}

// clearRunes erases all the cells in the view.
func (v *View) clearRunes() {
	maxX, maxY := v.Size()
	for x := 0; x < maxX; x++ {
		for y := 0; y < maxY; y++ {
			termbox.SetCell(v.x0+x+1, v.y0+y+1, ' ',
				termbox.Attribute(v.FgColor), termbox.Attribute(v.BgColor))
		}
	}
}

// writeRune writes a rune into the view's internal buffer, at the
// position corresponding to the point (x, y). The length of the internal
// buffer is increased if the point is out of bounds. Overwrite mode is
// governed by the value of View.overwrite.
func (v *View) writeRune(x, y int, ch rune) error {
	x = v.ox + x
	y = v.oy + y

	if x < 0 || y < 0 {
		return errors.New("invalid point")
	}

	if y >= len(v.lines) {
		if y >= cap(v.lines) {
			s := make([][]rune, y+1, (y+1)*2)
			copy(s, v.lines)
			v.lines = s
		} else {
			v.lines = v.lines[:y+1]
		}
	}
	if v.lines[y] == nil {
		v.lines[y] = make([]rune, x+1, (x+1)*2)
	} else if x >= len(v.lines[y]) {
		if x >= cap(v.lines[y]) {
			s := make([]rune, x+1, (x+1)*2)
			copy(s, v.lines[y])
			v.lines[y] = s
		} else {
			v.lines[y] = v.lines[y][:x+1]
		}
	}
	if !v.overwrite {
		v.lines[y] = append(v.lines[y], ' ')
		copy(v.lines[y][x+1:], v.lines[y][x:])
	}
	v.lines[y][x] = ch
	return nil
}

// deleteRune removes a rune from the view's internal buffer, at the
// position corresponding to the point (x, y).
func (v *View) deleteRune(x, y int) error {
	x = v.ox + x
	y = v.oy + y

	if x < 0 || y < 0 || y >= len(v.lines) || v.lines[y] == nil || x >= len(v.lines[y]) {
		return errors.New("invalid point")
	}
	copy(v.lines[y][x:], v.lines[y][x+1:])
	v.lines[y][len(v.lines[y])-1] = ' '
	return nil
}

// addLine adds a line into the view's internal buffer at the position
// corresponding to the point (x, y).
func (v *View) addLine(y int) error {
	y = v.oy + y

	if y < 0 || y >= len(v.lines) {
		return errors.New("invalid point")
	}
	v.lines = append(v.lines, nil)
	copy(v.lines[y+1:], v.lines[y:])
	v.lines[y] = nil
	return nil
}

// Buffer returns a string with the contents of the view's internal
// buffer
func (v *View) Buffer() string {
	str := ""
	for _, l := range v.lines {
		str += string(l) + "\n"
	}
	return strings.Replace(str, "\x00", " ", -1)
}

// Line returns a string with the line of the view's internal buffer
// at the position corresponding to the point (x, y).
func (v *View) Line(y int) (string, error) {
	y = v.oy + y

	if y < 0 || y >= len(v.lines) {
		return "", errors.New("invalid point")
	}
	return string(v.lines[y]), nil
}

// Word returns a string with the word of the view's internal buffer
// at the position corresponding to the point (x, y).
func (v *View) Word(x, y int) (string, error) {
	x = v.ox + x
	y = v.oy + y

	if y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
		return "", errors.New("invalid point")
	}
	l := string(v.lines[y])
	nl := strings.LastIndexFunc(l[:x], indexFunc)
	if nl == -1 {
		nl = 0
	} else {
		nl = nl + 1
	}
	nr := strings.IndexFunc(l[x:], indexFunc)
	if nr == -1 {
		nr = len(l)
	} else {
		nr = nr + x
	}
	return string(l[nl:nr]), nil
}

// indexFunc allows to split lines by words taking into account spaces
// and 0
func indexFunc(r rune) bool {
	return r == ' ' || r == 0
}
<commit_msg>Fix typos.<commit_after>// Copyright 2014 The gocui 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 gocui

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"strings"

	"github.com/nsf/termbox-go"
)

// A View is a window. It maintains its own internal buffer and cursor
// position.
type View struct {
	name           string
	x0, y0, x1, y1 int
	ox, oy         int
	cx, cy         int
	lines          [][]rune
	overwrite      bool // overwrite in edit mode
	readOffset     int
	readCache      string

	// BgColor and FgColor allow to configure the background and foreground
	// colors of the View.
	BgColor, FgColor Attribute

	// SelBgColor and SelFgColor are used to configure the background and
	// foreground colors of the selected line, when it is highlighted.
	SelBgColor, SelFgColor Attribute

	// If Editable is true, keystrokes will be added to the view's internal
	// buffer at the cursor position.
	Editable bool

	// If Highlight is true, Sel{Bg,Fg}Colors will be used
	// for the line under the cursor position.
	Highlight bool

	// If Frame is true, a border will be drawn around the view.
	Frame bool

	// If Wrap is true, the content that is written to this View is
	// automatically wrapped when it is longer than its width.
	Wrap bool

	// If Wrap is true, each wrapping line is prefixed with this prefix.
	WrapPrefix string
}

// newView returns a new View object.
func newView(name string, x0, y0, x1, y1 int) *View {
	v := &View{
		name:  name,
		x0:    x0,
		y0:    y0,
		x1:    x1,
		y1:    y1,
		Frame: true,
	}
	return v
}

// Size returns the number of visible columns and rows in the View.
func (v *View) Size() (x, y int) {
	return v.x1 - v.x0 - 1, v.y1 - v.y0 - 1
}

// Name returns the name of the view.
func (v *View) Name() string {
	return v.name
}

// setRune writes a rune at the given point, relative to the view. It
// checks if the position is valid and applies the view's colors, taking
// into account if the cell must be highlighted.
func (v *View) setRune(x, y int, ch rune) error {
	maxX, maxY := v.Size()
	if x < 0 || x >= maxX || y < 0 || y >= maxY {
		return errors.New("invalid point")
	}

	var fgColor, bgColor Attribute
	if v.Highlight && y == v.cy {
		fgColor = v.SelFgColor
		bgColor = v.SelBgColor
	} else {
		fgColor = v.FgColor
		bgColor = v.BgColor
	}
	termbox.SetCell(v.x0+x+1, v.y0+y+1, ch,
		termbox.Attribute(fgColor), termbox.Attribute(bgColor))
	return nil
}

// SetCursor sets the cursor position of the view at the given point,
// relative to the view. It checks if the position is valid.
func (v *View) SetCursor(x, y int) error {
	maxX, maxY := v.Size()
	if x < 0 || x >= maxX || y < 0 || y >= maxY {
		return errors.New("invalid point")
	}
	v.cx = x
	v.cy = y
	return nil
}

// Cursor returns the cursor position of the view.
func (v *View) Cursor() (x, y int) {
	return v.cx, v.cy
}

// SetOrigin sets the origin position of the view's internal buffer,
// so the buffer starts to be printed from this point, which means that
// it is linked with the origin point of view. It can be used to
// implement Horizontal and Vertical scrolling with just incrementing
// or decrementing ox and oy.
func (v *View) SetOrigin(x, y int) error {
	if x < 0 || y < 0 {
		return errors.New("invalid point")
	}
	v.ox = x
	v.oy = y
	return nil
}

// Origin returns the origin position of the view.
func (v *View) Origin() (x, y int) {
	return v.ox, v.oy
}

// Write appends a byte slice into the view's internal buffer. Because
// View implements the io.Writer interface, it can be passed as parameter
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
// be called to clear the view's buffer.
func (v *View) Write(p []byte) (n int, err error) {
	r := bytes.NewReader(p)
	s := bufio.NewScanner(r)
	for s.Scan() {
		line := bytes.Runes(s.Bytes())
		v.lines = append(v.lines, line)
	}
	if err := s.Err(); err != nil {
		return 0, err
	}
	return len(p), nil
}

// Read reads data into p. It returns the number of bytes read into p.
// At EOF, err will be io.EOF. Calling Read() after Rewind() makes the
// cache to be refreshed with the contents of the view.
func (v *View) Read(p []byte) (n int, err error) {
	if v.readOffset == 0 {
		v.readCache = v.Buffer()
	}
	if v.readOffset < len(v.readCache) {
		n = copy(p, v.readCache[v.readOffset:])
		v.readOffset += n
	} else {
		err = io.EOF
	}
	return
}

// Rewind sets the offset for the next Read to 0, which also refresh the
// read cache.
func (v *View) Rewind() {
	v.readOffset = 0
}

// draw re-draws the view's contents.
func (v *View) draw() error {
	maxX, maxY := v.Size()
	y := 0
	for i, line := range v.lines {
		if i < v.oy {
			continue
		}
		x := 0
		for j, ch := range line {
			if j < v.ox {
				continue
			}
			if x == maxX && v.Wrap {
				x = 0
				y++
				for _, p := range v.WrapPrefix + string(ch) {
					if x >= maxX || y >= maxY {
						break
					}
					if err := v.setRune(x, y, p); err != nil {
						return err
					}
					x++
				}
			} else if x < maxX && y < maxY {
				if err := v.setRune(x, y, ch); err != nil {
					return err
				}
				x++
			}
		}
		y++
	}
	return nil
}

// Clear empties the view's internal buffer.
func (v *View) Clear() {
	v.lines = nil
	v.clearRunes()
}

// clearRunes erases all the cells in the view.
func (v *View) clearRunes() {
	maxX, maxY := v.Size()
	for x := 0; x < maxX; x++ {
		for y := 0; y < maxY; y++ {
			termbox.SetCell(v.x0+x+1, v.y0+y+1, ' ',
				termbox.Attribute(v.FgColor), termbox.Attribute(v.BgColor))
		}
	}
}

// writeRune writes a rune into the view's internal buffer, at the
// position corresponding to the point (x, y). The length of the internal
// buffer is increased if the point is out of bounds. Overwrite mode is
// governed by the value of View.overwrite.
func (v *View) writeRune(x, y int, ch rune) error {
	x = v.ox + x
	y = v.oy + y

	if x < 0 || y < 0 {
		return errors.New("invalid point")
	}

	if y >= len(v.lines) {
		if y >= cap(v.lines) {
			s := make([][]rune, y+1, (y+1)*2)
			copy(s, v.lines)
			v.lines = s
		} else {
			v.lines = v.lines[:y+1]
		}
	}
	if v.lines[y] == nil {
		v.lines[y] = make([]rune, x+1, (x+1)*2)
	} else if x >= len(v.lines[y]) {
		if x >= cap(v.lines[y]) {
			s := make([]rune, x+1, (x+1)*2)
			copy(s, v.lines[y])
			v.lines[y] = s
		} else {
			v.lines[y] = v.lines[y][:x+1]
		}
	}
	if !v.overwrite {
		v.lines[y] = append(v.lines[y], ' ')
		copy(v.lines[y][x+1:], v.lines[y][x:])
	}
	v.lines[y][x] = ch
	return nil
}

// deleteRune removes a rune from the view's internal buffer, at the
// position corresponding to the point (x, y).
func (v *View) deleteRune(x, y int) error {
	x = v.ox + x
	y = v.oy + y

	if x < 0 || y < 0 || y >= len(v.lines) || v.lines[y] == nil || x >= len(v.lines[y]) {
		return errors.New("invalid point")
	}
	copy(v.lines[y][x:], v.lines[y][x+1:])
	v.lines[y][len(v.lines[y])-1] = ' '
	return nil
}

// addLine adds a line into the view's internal buffer at the position
// corresponding to the point (x, y).
func (v *View) addLine(y int) error {
	y = v.oy + y

	if y < 0 || y >= len(v.lines) {
		return errors.New("invalid point")
	}
	v.lines = append(v.lines, nil)
	copy(v.lines[y+1:], v.lines[y:])
	v.lines[y] = nil
	return nil
}

// Buffer returns a string with the contents of the view's internal
// buffer
func (v *View) Buffer() string {
	str := ""
	for _, l := range v.lines {
		str += string(l) + "\n"
	}
	return strings.Replace(str, "\x00", " ", -1)
}

// Line returns a string with the line of the view's internal buffer
// at the position corresponding to the point (x, y).
func (v *View) Line(y int) (string, error) {
	y = v.oy + y

	if y < 0 || y >= len(v.lines) {
		return "", errors.New("invalid point")
	}
	return string(v.lines[y]), nil
}

// Word returns a string with the word of the view's internal buffer
// at the position corresponding to the point (x, y).
func (v *View) Word(x, y int) (string, error) {
	x = v.ox + x
	y = v.oy + y

	if y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
		return "", errors.New("invalid point")
	}
	l := string(v.lines[y])
	nl := strings.LastIndexFunc(l[:x], indexFunc)
	if nl == -1 {
		nl = 0
	} else {
		nl = nl + 1
	}
	nr := strings.IndexFunc(l[x:], indexFunc)
	if nr == -1 {
		nr = len(l)
	} else {
		nr = nr + x
	}
	return string(l[nl:nr]), nil
}

// indexFunc allows to split lines by words taking into account spaces
// and 0
func indexFunc(r rune) bool {
	return r == ' ' || r == 0
}
<|endoftext|>


<commit_before>// Copyright 2015 The etcd 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 compactor

import (
	"reflect"
	"testing"
	"time"

	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/testutil"
	"github.com/jonboulle/clockwork"
	"golang.org/x/net/context"
)

func TestPeriodic(t *testing.T) {
	fc := clockwork.NewFakeClock()
	compactable := &fakeCompactable{testutil.NewRecorderStream()}
	tb := &Periodic{
		clock:        fc,
		periodInHour: 1,
		rg:           &fakeRevGetter{},
		c:            compactable,
	}

	tb.Run()
	defer tb.Stop()

	n := int(time.Hour / checkCompactionInterval)
	for i := 0; i < 3; i++ {
		for j := 0; j < n; j++ {
			time.Sleep(5 * time.Millisecond)
			fc.Advance(checkCompactionInterval)
		}

		a, err := compactable.Wait(1)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(a[0].Params[0], &pb.CompactionRequest{Revision: int64(i*n) + 1}) {
			t.Errorf("compact request = %v, want %v", a[0].Params[0], &pb.CompactionRequest{Revision: int64(i*n) + 1})
		}
	}
}

func TestPeriodicPause(t *testing.T) {
	fc := clockwork.NewFakeClock()
	compactable := &fakeCompactable{testutil.NewRecorderStream()}
	tb := &Periodic{
		clock:        fc,
		periodInHour: 1,
		rg:           &fakeRevGetter{},
		c:            compactable,
	}

	tb.Run()
	tb.Pause()

	n := int(time.Hour / checkCompactionInterval)
	for i := 0; i < 3*n; i++ {
		time.Sleep(5 * time.Millisecond)
		fc.Advance(checkCompactionInterval)
	}

	select {
	case a := <-compactable.Chan():
		t.Fatalf("unexpected action %v", a)
	case <-time.After(10 * time.Millisecond):
	}

	tb.Resume()
	fc.Advance(checkCompactionInterval)

	a, err := compactable.Wait(1)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(a[0].Params[0], &pb.CompactionRequest{Revision: int64(2*n) + 2}) {
		t.Errorf("compact request = %v, want %v", a[0].Params[0], &pb.CompactionRequest{Revision: int64(2*n) + 2})
	}
}

type fakeCompactable struct {
	testutil.Recorder
}

func (fc *fakeCompactable) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) {
	fc.Record(testutil.Action{Name: "c", Params: []interface{}{r}})
	return &pb.CompactionResponse{}, nil
}

type fakeRevGetter struct {
	rev int64
}

func (fr *fakeRevGetter) Rev() int64 {
	fr.rev++
	return fr.rev
}
<commit_msg>compactor: make tests deterministic<commit_after>// Copyright 2015 The etcd 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 compactor

import (
	"reflect"
	"testing"
	"time"

	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
	"github.com/coreos/etcd/pkg/testutil"
	"github.com/jonboulle/clockwork"
	"golang.org/x/net/context"
)

func TestPeriodic(t *testing.T) {
	fc := clockwork.NewFakeClock()
	rg := &fakeRevGetter{testutil.NewRecorderStream(), 0}
	compactable := &fakeCompactable{testutil.NewRecorderStream()}
	tb := &Periodic{
		clock:        fc,
		periodInHour: 1,
		rg:           rg,
		c:            compactable,
	}

	tb.Run()
	defer tb.Stop()

	n := int(time.Hour / checkCompactionInterval)
	for i := 0; i < 3; i++ {
		for j := 0; j < n; j++ {
			rg.Wait(1)
			fc.Advance(checkCompactionInterval)
		}

		a, err := compactable.Wait(1)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(a[0].Params[0], &pb.CompactionRequest{Revision: int64(i*n) + 1}) {
			t.Errorf("compact request = %v, want %v", a[0].Params[0], &pb.CompactionRequest{Revision: int64(i*n) + 1})
		}
	}
}

func TestPeriodicPause(t *testing.T) {
	fc := clockwork.NewFakeClock()
	compactable := &fakeCompactable{testutil.NewRecorderStream()}
	rg := &fakeRevGetter{testutil.NewRecorderStream(), 0}
	tb := &Periodic{
		clock:        fc,
		periodInHour: 1,
		rg:           rg,
		c:            compactable,
	}

	tb.Run()
	tb.Pause()

	n := int(time.Hour / checkCompactionInterval)
	for i := 0; i < 3*n; i++ {
		rg.Wait(1)
		fc.Advance(checkCompactionInterval)
	}

	select {
	case a := <-compactable.Chan():
		t.Fatalf("unexpected action %v", a)
	case <-time.After(10 * time.Millisecond):
	}

	tb.Resume()
	rg.Wait(1)
	fc.Advance(checkCompactionInterval)

	a, err := compactable.Wait(1)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(a[0].Params[0], &pb.CompactionRequest{Revision: int64(2*n) + 2}) {
		t.Errorf("compact request = %v, want %v", a[0].Params[0], &pb.CompactionRequest{Revision: int64(2*n) + 2})
	}
}

type fakeCompactable struct {
	testutil.Recorder
}

func (fc *fakeCompactable) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) {
	fc.Record(testutil.Action{Name: "c", Params: []interface{}{r}})
	return &pb.CompactionResponse{}, nil
}

type fakeRevGetter struct {
	testutil.Recorder
	rev int64
}

func (fr *fakeRevGetter) Rev() int64 {
	fr.Record(testutil.Action{Name: "g"})
	fr.rev++
	return fr.rev
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 apiserver

import (
	"net/http"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/sets"
	genericapiserver "k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"
	"k8s.io/client-go/pkg/version"

	"k8s.io/kube-aggregator/pkg/apis/apiregistration"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
	aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
	"k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/kube-aggregator/pkg/client/informers/internalversion"
	listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion"
	openapicontroller "k8s.io/kube-aggregator/pkg/controllers/openapi"
	statuscontrollers "k8s.io/kube-aggregator/pkg/controllers/status"
	apiservicerest "k8s.io/kube-aggregator/pkg/registry/apiservice/rest"
)

func init() {
	// we need to add the options (like ListOptions) to empty v1
	metav1.AddToGroupVersion(aggregatorscheme.Scheme, schema.GroupVersion{Group: "", Version: "v1"})

	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	aggregatorscheme.Scheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

// legacyAPIServiceName is the fixed name of the only non-groupified API version
const legacyAPIServiceName = "v1."

type ExtraConfig struct {
	// ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	ProxyClientCert []byte
	ProxyClientKey  []byte

	// If present, the Dial method will be used for dialing out to delegate
	// apiservers.
	ProxyTransport *http.Transport

	// Mechanism by which the Aggregator will resolve services. Required.
	ServiceResolver ServiceResolver
}

type Config struct {
	GenericConfig *genericapiserver.RecommendedConfig
	ExtraConfig   ExtraConfig
}

type completedConfig struct {
	GenericConfig genericapiserver.CompletedConfig
	ExtraConfig   *ExtraConfig
}

type CompletedConfig struct {
	// Embed a private pointer that cannot be instantiated outside of this package.
	*completedConfig
}

// APIAggregator contains state for a Kubernetes cluster master/api server.
type APIAggregator struct {
	GenericAPIServer *genericapiserver.GenericAPIServer

	delegateHandler http.Handler

	// proxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	proxyClientCert []byte
	proxyClientKey  []byte
	proxyTransport  *http.Transport

	// proxyHandlers are the proxy handlers that are currently registered, keyed by apiservice.name
	proxyHandlers map[string]*proxyHandler
	// handledGroups are the groups that already have routes
	handledGroups sets.String

	// lister is used to add group handling for /apis/<group> aggregator lookups based on
	// controller state
	lister listers.APIServiceLister

	// provided for easier embedding
	APIRegistrationInformers informers.SharedInformerFactory

	// Information needed to determine routing for the aggregator
	serviceResolver ServiceResolver

	openAPIAggregationController *openapicontroller.AggregationController
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (cfg *Config) Complete() CompletedConfig {
	c := completedConfig{
		cfg.GenericConfig.Complete(),
		&cfg.ExtraConfig,
	}

	// the kube aggregator wires its own discovery mechanism
	// TODO eventually collapse this by extracting all of the discovery out
	c.GenericConfig.EnableDiscovery = false
	version := version.Get()
	c.GenericConfig.Version = &version

	return CompletedConfig{&c}
}

// New returns a new instance of APIAggregator from the given config.
func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.DelegationTarget) (*APIAggregator, error) {
	// Prevent generic API server to install OpenAPI handler. Aggregator server
	// has its own customized OpenAPI handler.
	openApiConfig := c.GenericConfig.OpenAPIConfig
	c.GenericConfig.OpenAPIConfig = nil

	genericServer, err := c.GenericConfig.New("kube-aggregator", delegationTarget)
	if err != nil {
		return nil, err
	}

	apiregistrationClient, err := internalclientset.NewForConfig(c.GenericConfig.LoopbackClientConfig)
	if err != nil {
		return nil, err
	}
	informerFactory := informers.NewSharedInformerFactory(
		apiregistrationClient,
		5*time.Minute, // this is effectively used as a refresh interval right now.  Might want to do something nicer later on.
	)

	s := &APIAggregator{
		GenericAPIServer: genericServer,
		delegateHandler:  delegationTarget.UnprotectedHandler(),
		proxyClientCert:  c.ExtraConfig.ProxyClientCert,
		proxyClientKey:   c.ExtraConfig.ProxyClientKey,
		proxyTransport:   c.ExtraConfig.ProxyTransport,
		proxyHandlers:    map[string]*proxyHandler{},
		handledGroups:    sets.String{},
		lister:           informerFactory.Apiregistration().InternalVersion().APIServices().Lister(),
		APIRegistrationInformers: informerFactory,
		serviceResolver:          c.ExtraConfig.ServiceResolver,
	}

	apiGroupInfo := apiservicerest.NewRESTStorage(c.GenericConfig.MergedResourceConfig, c.GenericConfig.RESTOptionsGetter)
	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	apisHandler := &apisHandler{
		codecs: aggregatorscheme.Codecs,
		lister: s.lister,
	}
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", apisHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle("/apis/", apisHandler)

	apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), s)
	availableController := statuscontrollers.NewAvailableConditionController(
		informerFactory.Apiregistration().InternalVersion().APIServices(),
		c.GenericConfig.SharedInformerFactory.Core().V1().Services(),
		c.GenericConfig.SharedInformerFactory.Core().V1().Endpoints(),
		apiregistrationClient.Apiregistration(),
		c.ExtraConfig.ProxyTransport,
		c.ExtraConfig.ProxyClientCert,
		c.ExtraConfig.ProxyClientKey,
		s.serviceResolver,
	)

	s.GenericAPIServer.AddPostStartHook("start-kube-aggregator-informers", func(context genericapiserver.PostStartHookContext) error {
		informerFactory.Start(context.StopCh)
		c.GenericConfig.SharedInformerFactory.Start(context.StopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("apiservice-registration-controller", func(context genericapiserver.PostStartHookContext) error {
		go apiserviceRegistrationController.Run(context.StopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("apiservice-status-available-controller", func(context genericapiserver.PostStartHookContext) error {
		// if we end up blocking for long periods of time, we may need to increase threadiness.
		go availableController.Run(5, context.StopCh)
		return nil
	})

	if openApiConfig != nil {
		specDownloader := openapicontroller.NewDownloader()
		openAPIAggregator, err := openapicontroller.BuildAndRegisterAggregator(
			&specDownloader,
			delegationTarget,
			s.GenericAPIServer.Handler.GoRestfulContainer.RegisteredWebServices(),
			openApiConfig,
			s.GenericAPIServer.Handler.NonGoRestfulMux)
		if err != nil {
			return nil, err
		}
		s.openAPIAggregationController = openapicontroller.NewAggregationController(&specDownloader, openAPIAggregator)

		s.GenericAPIServer.AddPostStartHook("apiservice-openapi-controller", func(context genericapiserver.PostStartHookContext) error {
			go s.openAPIAggregationController.Run(context.StopCh)
			return nil
		})
	}

	return s, nil
}

// AddAPIService adds an API service.  It is not thread-safe, so only call it on one thread at a time please.
// It's a slow moving API, so its ok to run the controller on a single thread
func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) error {
	// if the proxyHandler already exists, it needs to be updated. The aggregation bits do not
	// since they are wired against listers because they require multiple resources to respond
	if proxyHandler, exists := s.proxyHandlers[apiService.Name]; exists {
		proxyHandler.updateAPIService(apiService)
		if s.openAPIAggregationController != nil {
			s.openAPIAggregationController.UpdateAPIService(proxyHandler, apiService)
		}
		return nil
	}

	proxyPath := "/apis/" + apiService.Spec.Group + "/" + apiService.Spec.Version
	// v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
	if apiService.Name == legacyAPIServiceName {
		proxyPath = "/api"
	}

	// register the proxy handler
	proxyHandler := &proxyHandler{
		localDelegate:   s.delegateHandler,
		proxyClientCert: s.proxyClientCert,
		proxyClientKey:  s.proxyClientKey,
		proxyTransport:  s.proxyTransport,
		serviceResolver: s.serviceResolver,
	}
	proxyHandler.updateAPIService(apiService)
	if s.openAPIAggregationController != nil {
		s.openAPIAggregationController.AddAPIService(proxyHandler, apiService)
	}
	s.proxyHandlers[apiService.Name] = proxyHandler
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(proxyPath, proxyHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandlePrefix(proxyPath+"/", proxyHandler)

	// this exists to proxy /oapi
	if apiService.Spec.Group == "apps.openshift.io" {
		s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/oapi", proxyHandler)
		s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandlePrefix("/oapi/", proxyHandler)
	}

	// if we're dealing with the legacy group, we're done here
	if apiService.Name == legacyAPIServiceName {
		return nil
	}

	// if we've already registered the path with the handler, we don't want to do it again.
	if s.handledGroups.Has(apiService.Spec.Group) {
		return nil
	}

	// it's time to register the group aggregation endpoint
	groupPath := "/apis/" + apiService.Spec.Group
	groupDiscoveryHandler := &apiGroupHandler{
		codecs:    aggregatorscheme.Codecs,
		groupName: apiService.Spec.Group,
		lister:    s.lister,
		delegate:  s.delegateHandler,
	}
	// aggregation is protected
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(groupPath, groupDiscoveryHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
	s.handledGroups.Insert(apiService.Spec.Group)
	return nil
}

// RemoveAPIService removes the APIService from being handled.  It is not thread-safe, so only call it on one thread at a time please.
// It's a slow moving API, so its ok to run the controller on a single thread.
func (s *APIAggregator) RemoveAPIService(apiServiceName string) {
	version := apiregistration.APIServiceNameToGroupVersion(apiServiceName)

	proxyPath := "/apis/" + version.Group + "/" + version.Version
	// v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
	if apiServiceName == legacyAPIServiceName {
		proxyPath = "/api"
	}
	s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(proxyPath)
	s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(proxyPath + "/")
	if s.openAPIAggregationController != nil {
		s.openAPIAggregationController.RemoveAPIService(apiServiceName)
	}
	delete(s.proxyHandlers, apiServiceName)

	// TODO unregister group level discovery when there are no more versions for the group
	// We don't need this right away because the handler properly delegates when no versions are present
}

func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
	ret := serverstorage.NewResourceConfig()
	// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
	ret.EnableVersions(
		v1.SchemeGroupVersion,
		v1beta1.SchemeGroupVersion,
	)

	return ret
}
<commit_msg>UPSTREAM: <carry>: remove the aggregator oapi handling<commit_after>/*
Copyright 2016 The Kubernetes 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 apiserver

import (
	"net/http"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/sets"
	genericapiserver "k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"
	"k8s.io/client-go/pkg/version"

	"k8s.io/kube-aggregator/pkg/apis/apiregistration"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
	aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
	"k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/kube-aggregator/pkg/client/informers/internalversion"
	listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/internalversion"
	openapicontroller "k8s.io/kube-aggregator/pkg/controllers/openapi"
	statuscontrollers "k8s.io/kube-aggregator/pkg/controllers/status"
	apiservicerest "k8s.io/kube-aggregator/pkg/registry/apiservice/rest"
)

func init() {
	// we need to add the options (like ListOptions) to empty v1
	metav1.AddToGroupVersion(aggregatorscheme.Scheme, schema.GroupVersion{Group: "", Version: "v1"})

	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	aggregatorscheme.Scheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

// legacyAPIServiceName is the fixed name of the only non-groupified API version
const legacyAPIServiceName = "v1."

type ExtraConfig struct {
	// ProxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	ProxyClientCert []byte
	ProxyClientKey  []byte

	// If present, the Dial method will be used for dialing out to delegate
	// apiservers.
	ProxyTransport *http.Transport

	// Mechanism by which the Aggregator will resolve services. Required.
	ServiceResolver ServiceResolver
}

type Config struct {
	GenericConfig *genericapiserver.RecommendedConfig
	ExtraConfig   ExtraConfig
}

type completedConfig struct {
	GenericConfig genericapiserver.CompletedConfig
	ExtraConfig   *ExtraConfig
}

type CompletedConfig struct {
	// Embed a private pointer that cannot be instantiated outside of this package.
	*completedConfig
}

// APIAggregator contains state for a Kubernetes cluster master/api server.
type APIAggregator struct {
	GenericAPIServer *genericapiserver.GenericAPIServer

	delegateHandler http.Handler

	// proxyClientCert/Key are the client cert used to identify this proxy. Backing APIServices use
	// this to confirm the proxy's identity
	proxyClientCert []byte
	proxyClientKey  []byte
	proxyTransport  *http.Transport

	// proxyHandlers are the proxy handlers that are currently registered, keyed by apiservice.name
	proxyHandlers map[string]*proxyHandler
	// handledGroups are the groups that already have routes
	handledGroups sets.String

	// lister is used to add group handling for /apis/<group> aggregator lookups based on
	// controller state
	lister listers.APIServiceLister

	// provided for easier embedding
	APIRegistrationInformers informers.SharedInformerFactory

	// Information needed to determine routing for the aggregator
	serviceResolver ServiceResolver

	openAPIAggregationController *openapicontroller.AggregationController
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (cfg *Config) Complete() CompletedConfig {
	c := completedConfig{
		cfg.GenericConfig.Complete(),
		&cfg.ExtraConfig,
	}

	// the kube aggregator wires its own discovery mechanism
	// TODO eventually collapse this by extracting all of the discovery out
	c.GenericConfig.EnableDiscovery = false
	version := version.Get()
	c.GenericConfig.Version = &version

	return CompletedConfig{&c}
}

// New returns a new instance of APIAggregator from the given config.
func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.DelegationTarget) (*APIAggregator, error) {
	// Prevent generic API server to install OpenAPI handler. Aggregator server
	// has its own customized OpenAPI handler.
	openApiConfig := c.GenericConfig.OpenAPIConfig
	c.GenericConfig.OpenAPIConfig = nil

	genericServer, err := c.GenericConfig.New("kube-aggregator", delegationTarget)
	if err != nil {
		return nil, err
	}

	apiregistrationClient, err := internalclientset.NewForConfig(c.GenericConfig.LoopbackClientConfig)
	if err != nil {
		return nil, err
	}
	informerFactory := informers.NewSharedInformerFactory(
		apiregistrationClient,
		5*time.Minute, // this is effectively used as a refresh interval right now.  Might want to do something nicer later on.
	)

	s := &APIAggregator{
		GenericAPIServer: genericServer,
		delegateHandler:  delegationTarget.UnprotectedHandler(),
		proxyClientCert:  c.ExtraConfig.ProxyClientCert,
		proxyClientKey:   c.ExtraConfig.ProxyClientKey,
		proxyTransport:   c.ExtraConfig.ProxyTransport,
		proxyHandlers:    map[string]*proxyHandler{},
		handledGroups:    sets.String{},
		lister:           informerFactory.Apiregistration().InternalVersion().APIServices().Lister(),
		APIRegistrationInformers: informerFactory,
		serviceResolver:          c.ExtraConfig.ServiceResolver,
	}

	apiGroupInfo := apiservicerest.NewRESTStorage(c.GenericConfig.MergedResourceConfig, c.GenericConfig.RESTOptionsGetter)
	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	apisHandler := &apisHandler{
		codecs: aggregatorscheme.Codecs,
		lister: s.lister,
	}
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", apisHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle("/apis/", apisHandler)

	apiserviceRegistrationController := NewAPIServiceRegistrationController(informerFactory.Apiregistration().InternalVersion().APIServices(), s)
	availableController := statuscontrollers.NewAvailableConditionController(
		informerFactory.Apiregistration().InternalVersion().APIServices(),
		c.GenericConfig.SharedInformerFactory.Core().V1().Services(),
		c.GenericConfig.SharedInformerFactory.Core().V1().Endpoints(),
		apiregistrationClient.Apiregistration(),
		c.ExtraConfig.ProxyTransport,
		c.ExtraConfig.ProxyClientCert,
		c.ExtraConfig.ProxyClientKey,
		s.serviceResolver,
	)

	s.GenericAPIServer.AddPostStartHook("start-kube-aggregator-informers", func(context genericapiserver.PostStartHookContext) error {
		informerFactory.Start(context.StopCh)
		c.GenericConfig.SharedInformerFactory.Start(context.StopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("apiservice-registration-controller", func(context genericapiserver.PostStartHookContext) error {
		go apiserviceRegistrationController.Run(context.StopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("apiservice-status-available-controller", func(context genericapiserver.PostStartHookContext) error {
		// if we end up blocking for long periods of time, we may need to increase threadiness.
		go availableController.Run(5, context.StopCh)
		return nil
	})

	if openApiConfig != nil {
		specDownloader := openapicontroller.NewDownloader()
		openAPIAggregator, err := openapicontroller.BuildAndRegisterAggregator(
			&specDownloader,
			delegationTarget,
			s.GenericAPIServer.Handler.GoRestfulContainer.RegisteredWebServices(),
			openApiConfig,
			s.GenericAPIServer.Handler.NonGoRestfulMux)
		if err != nil {
			return nil, err
		}
		s.openAPIAggregationController = openapicontroller.NewAggregationController(&specDownloader, openAPIAggregator)

		s.GenericAPIServer.AddPostStartHook("apiservice-openapi-controller", func(context genericapiserver.PostStartHookContext) error {
			go s.openAPIAggregationController.Run(context.StopCh)
			return nil
		})
	}

	return s, nil
}

// AddAPIService adds an API service.  It is not thread-safe, so only call it on one thread at a time please.
// It's a slow moving API, so its ok to run the controller on a single thread
func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) error {
	// if the proxyHandler already exists, it needs to be updated. The aggregation bits do not
	// since they are wired against listers because they require multiple resources to respond
	if proxyHandler, exists := s.proxyHandlers[apiService.Name]; exists {
		proxyHandler.updateAPIService(apiService)
		if s.openAPIAggregationController != nil {
			s.openAPIAggregationController.UpdateAPIService(proxyHandler, apiService)
		}
		return nil
	}

	proxyPath := "/apis/" + apiService.Spec.Group + "/" + apiService.Spec.Version
	// v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
	if apiService.Name == legacyAPIServiceName {
		proxyPath = "/api"
	}

	// register the proxy handler
	proxyHandler := &proxyHandler{
		localDelegate:   s.delegateHandler,
		proxyClientCert: s.proxyClientCert,
		proxyClientKey:  s.proxyClientKey,
		proxyTransport:  s.proxyTransport,
		serviceResolver: s.serviceResolver,
	}
	proxyHandler.updateAPIService(apiService)
	if s.openAPIAggregationController != nil {
		s.openAPIAggregationController.AddAPIService(proxyHandler, apiService)
	}
	s.proxyHandlers[apiService.Name] = proxyHandler
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(proxyPath, proxyHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandlePrefix(proxyPath+"/", proxyHandler)

	// if we're dealing with the legacy group, we're done here
	if apiService.Name == legacyAPIServiceName {
		return nil
	}

	// if we've already registered the path with the handler, we don't want to do it again.
	if s.handledGroups.Has(apiService.Spec.Group) {
		return nil
	}

	// it's time to register the group aggregation endpoint
	groupPath := "/apis/" + apiService.Spec.Group
	groupDiscoveryHandler := &apiGroupHandler{
		codecs:    aggregatorscheme.Codecs,
		groupName: apiService.Spec.Group,
		lister:    s.lister,
		delegate:  s.delegateHandler,
	}
	// aggregation is protected
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(groupPath, groupDiscoveryHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandle(groupPath+"/", groupDiscoveryHandler)
	s.handledGroups.Insert(apiService.Spec.Group)
	return nil
}

// RemoveAPIService removes the APIService from being handled.  It is not thread-safe, so only call it on one thread at a time please.
// It's a slow moving API, so its ok to run the controller on a single thread.
func (s *APIAggregator) RemoveAPIService(apiServiceName string) {
	version := apiregistration.APIServiceNameToGroupVersion(apiServiceName)

	proxyPath := "/apis/" + version.Group + "/" + version.Version
	// v1. is a special case for the legacy API.  It proxies to a wider set of endpoints.
	if apiServiceName == legacyAPIServiceName {
		proxyPath = "/api"
	}
	s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(proxyPath)
	s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(proxyPath + "/")
	if s.openAPIAggregationController != nil {
		s.openAPIAggregationController.RemoveAPIService(apiServiceName)
	}
	delete(s.proxyHandlers, apiServiceName)

	// TODO unregister group level discovery when there are no more versions for the group
	// We don't need this right away because the handler properly delegates when no versions are present
}

func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
	ret := serverstorage.NewResourceConfig()
	// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
	ret.EnableVersions(
		v1.SchemeGroupVersion,
		v1beta1.SchemeGroupVersion,
	)

	return ret
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"runtime/debug"
)

func prepareHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if h := r.Header.Get("X-Forwarded-Host"); h != "" {
			baseUrl, _ = url.Parse("http://" + h)
		} else {
			baseUrl, _ = url.Parse("http://" + r.Host)
		}
		fn(w, r)
	}
}

func myHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				fmt.Fprintf(os.Stderr, "%+v", err)
				debug.PrintStack()
				http.Error(w, http.StatusText(500), 500)
			}
		}()
		prepareHandler(fn)(w, r)
	}
}

func pathURIEscape(s string) string {
	return (&url.URL{Path: s}).String()
}

func notFound(w http.ResponseWriter) {
	code := http.StatusNotFound
	http.Error(w, http.StatusText(code), code)
}

func badRequest(w http.ResponseWriter) {
	code := http.StatusBadRequest
	http.Error(w, http.StatusText(code), code)
}

func forbidden(w http.ResponseWriter) {
	code := http.StatusForbidden
	http.Error(w, http.StatusText(code), code)
}

func panicIf(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>simple logging in application<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"runtime/debug"
)

func prepareHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if h := r.Header.Get("X-Forwarded-Host"); h != "" {
			baseUrl, _ = url.Parse("http://" + h)
		} else {
			baseUrl, _ = url.Parse("http://" + r.Host)
		}
		fmt.Println(r.Method, r.URL)
		fn(w, r)
	}
}

func myHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				fmt.Fprintf(os.Stderr, "%+v", err)
				debug.PrintStack()
				http.Error(w, http.StatusText(500), 500)
			}
		}()
		prepareHandler(fn)(w, r)
	}
}

func pathURIEscape(s string) string {
	return (&url.URL{Path: s}).String()
}

func notFound(w http.ResponseWriter) {
	code := http.StatusNotFound
	http.Error(w, http.StatusText(code), code)
}

func badRequest(w http.ResponseWriter) {
	code := http.StatusBadRequest
	http.Error(w, http.StatusText(code), code)
}

func forbidden(w http.ResponseWriter) {
	code := http.StatusForbidden
	http.Error(w, http.StatusText(code), code)
}

func panicIf(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package facebook

import (
	"os"
	"time"
)

type Post struct {
	// The post ID
	ID string
	// An object containing the ID and name of the user who posted the message
	From Object
	// A list of the profiles mentioned or targeted in this post
	To []Object
	// The message
	Message string
	// If available, a link to the picture included with this post
	Picture *Picture
	// The link attached to this post
	Link string
	// The name of the link
	Name string
	// The caption of the link (appears beneath the link name)
	Caption string
	// A description of the link (appears beneath the link caption)
	Description string
	// If available, the source of the stream attachment to add to this post (for example, a flash file or image)
	Source string
	// A link to an icon representing the type of this post
	Icon string
	// A string indicating which application was used to create this post
	Attribution string
	/* A list of available actions on the post (including commenting, liking, and an optional app-specified action),
	 * encoded as objects with keys for the 'name' and 'link'.
	 */
	Actions []Link
	/*
	 * An object that defines the privacy setting for a post, video, or album. It contains the following fields:
	 * value(string) : The privacy value for the object, specify one of EVERYONE, CUSTOM, ALL_FRIENDS, NETWORKS_FRIENDS, FRIENDS_OF_FRIENDS.
	 * friends(string) : For CUSTOM settings, this indicates which users can see the object. Can be one of EVERYONE, NETWORKS_FRIENDS (when the object can be seen by networks and friends), FRIENDS_OF_FRIENDS, ALL_FRIENDS, SOME_FRIENDS, SELF, or NO_FRIENDS (when the object can be seen by a network only).
	 * networks(string) : For CUSTOM settings, specify a comma-separated list of network IDs that can see the object, or 1 for all of a user's networks.
	 * allow(string) : When friends is set to SOME_FRIENDS, specify a comma-separated list of user IDs and friend list IDs that 'can' see the post.
	 * deny(string) : When friends is set to SOME_FRIENDS, specify a comma-separated list of user IDs and friend list IDs that 'cannot' see the post.
	 *
	 * Note: This privacy setting only applies to posts to the current or specified user's own Wall; Facebook ignores this setting for targeted Wall posts (when the user is writing on the Wall of a friend, Page, event, group connected to the user). Consistent with behavior on Facebook, all targeted posts are viewable by anyone who can see the target's Wall.
	 * Privacy Policy: Any non-default privacy setting must be intentionally chosen by the user. You may not set a custom privacy setting unless the user has proactively specified that they want this non-default setting.
	 */
	Privacy Object //?
	// The number of likes on this post
	Likes float64
	// The time the post was initially published
	CreatedTime *time.Time
	// The time of the last comment on this post
	UpdatedTime *time.Time

	// Connections
	// All of the comments on this post (this is no real connection, data is passed with the post)
	Comments []Comment
}

func GetPost(ID string) (post Post, err os.Error) {
	b, err := fetchBody(ID)
	data, err := getJsonMap(b)
	err = post.parseData(data)
	return
}

func GetPosts(url string) (posts []Post, err os.Error) {
	b, err := fetchPage(url)
	if err != nil {
		return
	}
	m, err := getJsonMap(b)
	if err != nil {
		return
	}
	data := m["data"].([]interface{})
	posts = make([]Post, len(data))
	for i, v := range data {
		err = posts[i].parseData(v.(map[string]interface{}))
	}
	return
}

func (p *Post) parseData(value map[string]interface{}) (err os.Error) {
	for key, val := range value {
		switch key {
		case "id":
			p.ID = val.(string)
		case "from":
			p.From = parseObject(val.(map[string]interface{}))
		case "to":
			data := val.(map[string]interface{})
			p.To = parseObjects(data["data"].([]interface{}))
		case "message":
			p.Message = val.(string)
		case "picture":
			p.Picture = NewPicture(val.(string))
		case "link":
			p.Link = val.(string)
		case "name":
			p.Name = val.(string)
		case "caption":
			p.Caption = val.(string)
		case "description":
			p.Description = val.(string)
		case "source":
			p.Source = val.(string)
		case "icon":
			p.Icon = val.(string)
		case "attribution":
			p.Attribution = val.(string)
		case "actions":
			p.Actions = parseLinks(val.([]interface{}))
		case "privacy":
			// TODO: Privacy
		case "likes":
			p.Likes = val.(float64)
		case "created_time":
			p.CreatedTime, err = parseTime(val.(string))
		case "updated_time":
			p.UpdatedTime, err = parseTime(val.(string))
		// Connections
		case "comments":
			data := val.(map[string]interface{})
			p.Comments, _ = parseComments(data)
		}
	}
	return
}
<commit_msg>Documentation.<commit_after>package facebook

import (
	"os"
	"time"
)

/*
 * An individual entry in a profile's feed.
 * The read_stream extended permission is required to access any information in a profile's feed that is not shared with everyone.
 */
type Post struct {
	// The post ID
	ID string
	// An object containing the ID and name of the user who posted the message
	From Object
	// A list of the profiles mentioned or targeted in this post
	To []Object
	// The message
	Message string
	// If available, a link to the picture included with this post
	Picture *Picture
	// The link attached to this post
	Link string
	// The name of the link
	Name string
	// The caption of the link (appears beneath the link name)
	Caption string
	// A description of the link (appears beneath the link caption)
	Description string
	// If available, the source of the stream attachment to add to this post (for example, a flash file or image)
	Source string
	// A link to an icon representing the type of this post
	Icon string
	// A string indicating which application was used to create this post
	Attribution string
	/* A list of available actions on the post (including commenting, liking, and an optional app-specified action),
	 * encoded as objects with keys for the 'name' and 'link'.
	 */
	Actions []Link
	/*
	 * An object that defines the privacy setting for a post, video, or album. It contains the following fields:
	 * value(string) : The privacy value for the object, specify one of EVERYONE, CUSTOM, ALL_FRIENDS, NETWORKS_FRIENDS, FRIENDS_OF_FRIENDS.
	 * friends(string) : For CUSTOM settings, this indicates which users can see the object. Can be one of EVERYONE, NETWORKS_FRIENDS (when the object can be seen by networks and friends), FRIENDS_OF_FRIENDS, ALL_FRIENDS, SOME_FRIENDS, SELF, or NO_FRIENDS (when the object can be seen by a network only).
	 * networks(string) : For CUSTOM settings, specify a comma-separated list of network IDs that can see the object, or 1 for all of a user's networks.
	 * allow(string) : When friends is set to SOME_FRIENDS, specify a comma-separated list of user IDs and friend list IDs that 'can' see the post.
	 * deny(string) : When friends is set to SOME_FRIENDS, specify a comma-separated list of user IDs and friend list IDs that 'cannot' see the post.
	 *
	 * Note: This privacy setting only applies to posts to the current or specified user's own Wall; Facebook ignores this setting for targeted Wall posts (when the user is writing on the Wall of a friend, Page, event, group connected to the user). Consistent with behavior on Facebook, all targeted posts are viewable by anyone who can see the target's Wall.
	 * Privacy Policy: Any non-default privacy setting must be intentionally chosen by the user. You may not set a custom privacy setting unless the user has proactively specified that they want this non-default setting.
	 */
	Privacy Object //?
	// The number of likes on this post
	Likes float64
	// The time the post was initially published
	CreatedTime *time.Time
	// The time of the last comment on this post
	UpdatedTime *time.Time

	// Connections
	// All of the comments on this post (this is no real connection, data is passed with the post)
	Comments []Comment
}

/*
 * Gets the Post with the provided ID.
 */
func GetPost(ID string) (post Post, err os.Error) {
	b, err := fetchBody(ID)
	data, err := getJsonMap(b)
	err = post.parseData(data)
	return
}

/*
 * Gets posts from an facebook GraphAPI URL.
 * At the moment url isn't checked.
 * Returns a Post array, err is nil if no error appeared.
 */
func GetPosts(url string) (posts []Post, err os.Error) {
	b, err := fetchPage(url)
	if err != nil {
		return
	}
	m, err := getJsonMap(b)
	if err != nil {
		return
	}
	data := m["data"].([]interface{})
	posts = make([]Post, len(data))
	for i, v := range data {
		err = posts[i].parseData(v.(map[string]interface{}))
	}
	return
}

/*
 * Parses Post data. Returns nil for err if no error appeared.
 */
func (p *Post) parseData(value map[string]interface{}) (err os.Error) {
	for key, val := range value {
		switch key {
		case "id":
			p.ID = val.(string)
		case "from":
			p.From = parseObject(val.(map[string]interface{}))
		case "to":
			data := val.(map[string]interface{})
			p.To = parseObjects(data["data"].([]interface{}))
		case "message":
			p.Message = val.(string)
		case "picture":
			p.Picture = NewPicture(val.(string))
		case "link":
			p.Link = val.(string)
		case "name":
			p.Name = val.(string)
		case "caption":
			p.Caption = val.(string)
		case "description":
			p.Description = val.(string)
		case "source":
			p.Source = val.(string)
		case "icon":
			p.Icon = val.(string)
		case "attribution":
			p.Attribution = val.(string)
		case "actions":
			p.Actions = parseLinks(val.([]interface{}))
		case "privacy":
			// TODO: Privacy
		case "likes":
			p.Likes = val.(float64)
		case "created_time":
			p.CreatedTime, err = parseTime(val.(string))
		case "updated_time":
			p.UpdatedTime, err = parseTime(val.(string))
		// Connections
		case "comments":
			data := val.(map[string]interface{})
			p.Comments, _ = parseComments(data)
		}
	}
	return
}
<|endoftext|>


<commit_before>package dbapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const (
	// GET is a shortcut for http.MethodGet.
	GET = http.MethodGet
)

const (
	// V1 is the version 1 of the dbAPI.
	V1 = "v1"
)

const (
	// DefaultURL is the URL of the Deutsche Bank API which is used by default.
	DefaultURL = "https://simulator-api.db.com/gw/dbapi/"
	// DefaultVersion is the default API version to use and defaults to v1.
	DefaultVersion = V1
)

var (
	// ErrInvalidClient is raised when the costum HTTP client is invalid (e.g.
	// nil).
	ErrInvalidClient = errors.New("Invalid http client!")
	// ErrInvalidURL is raised when the url couldn't be parsed by url.Parse().
	ErrInvalidURL = errors.New("Invalid url!")
)

// A Client manages communication with the Deutsche Bank API.
type Client struct {
	client  *http.Client
	baseURL *url.URL
	version Version

	// Authentication
	Authentication *AuthenticationService

	// API Resources
	Addresses    *AddressesService
	Accounts     *AccountsService
	Transactions *TransactionsService
	UserInfo     *UserInfoService
}

// A Response represents a http response from the Deutsche Bank API. It is a
// wrapper around the standard http.Response type.
type Response struct {
	*http.Response
}

// Version is the API version.
type Version string

func (v Version) String() string {
	return string(v)
}

// An Option serves as a 'functional parameter' which can be used to configure
// the behaviour of the API Client.
type Option func(c *Client) error

// SetClient specifies a custom http client that should be used to make requests.
// An error ErrInvalidClient is returned if the passed client is nil.
func SetClient(client *http.Client) Option {
	return func(c *Client) error { return c.setClient(client) }
}
func (c *Client) setClient(client *http.Client) error {
	if client == nil {
		return ErrInvalidClient
	}
	c.client = client
	return nil
}

// SetToken specifies the api token.
func SetToken(token string) Option {
	return func(c *Client) error { return c.setToken(token) }
}
func (c *Client) setToken(token string) error {
	c.Authentication.token = token
	return nil
}

// SetURL specifies the base url to use. An error ErrInvalidURL is returned if
// the passed url string can't be parsed properly.
func SetURL(urlStr string) Option {
	return func(c *Client) error { return c.setURL(urlStr) }
}
func (c *Client) setURL(urlStr string) error {
	if len(urlStr) == 0 {
		return ErrInvalidURL
	}
	// If there is no / at the end, add one.
	if strings.HasSuffix(urlStr, "/") == false {
		urlStr += "/"
	}
	url, err := url.Parse(urlStr)
	if err != nil {
		return ErrInvalidURL
	}
	c.baseURL = url
	return nil
}

// SetVersion specifies the api version to use.
func SetVersion(version Version) Option {
	return func(c *Client) error { return c.setVersion(version) }
}
func (c *Client) setVersion(version Version) error {
	c.version = version
	return nil
}

// New creates and returns a new API Client. Options can be passed to configure
// the Client.
func New(options ...Option) (*Client, error) {
	// Parse the DefaultURL.
	url, err := url.Parse(DefaultURL)
	if err != nil {
		return nil, err
	}

	// Create client with default settings.
	c := &Client{
		client:  http.DefaultClient,
		baseURL: url,
		version: DefaultVersion,
	}
	c.Authentication = &AuthenticationService{}
	c.Addresses = &AddressesService{client: c}
	c.Accounts = &AccountsService{client: c}
	c.Transactions = &TransactionsService{client: c}
	c.UserInfo = &UserInfoService{client: c}

	// Apply supplied options.
	if err := c.Options(options...); err != nil {
		return nil, err
	}

	return c, nil
}

// Options applies Options to a client instance.
func (c *Client) Options(options ...Option) error {
	for _, option := range options {
		if err := option(c); err != nil {
			return err
		}
	}
	return nil
}

// Call combines Client.NewRequest() and Client.Do() methodes to avoid code
// duplication.
//
// m is the HTTP method you want to call.
// u is the URL you want to call.
// b is the HTTP body.
// r is the HTTP response.
//
// For more information read https://github.com/google/go-github/issues/234
func (c *Client) Call(m, u string, b interface{}, r interface{}) (*Response, error) {
	req, err := c.NewRequest(m, u, b)
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req, r)
	if err != nil {
		return resp, err
	}

	return resp, err
}

// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if it has a status code outside the 200 range.
func CheckResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	return fmt.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
}

// Do sends an API request and returns the API response.
// The API response is JSON decoded and stored in the value pointed to by r, or
// returned as an error if an API error has occurred. If r implements the
// io.Writer interface, the raw response body will be written to r, without
// attempting to first decode it.
func (c *Client) Do(req *http.Request, r interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	// Wrap response
	response := &Response{Response: resp}

	err = CheckResponse(resp)
	if err != nil {
		// Return respone in case the caller wants to inspect it further.
		return response, err
	}

	if r != nil {
		if w, ok := r.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			var body []byte
			body, err = ioutil.ReadAll(resp.Body)
			if err != nil {
				// Return respone in case the caller wants to inspect it further.
				return response, err
			}
			err = json.Unmarshal(body, r)
		}
	}
	return response, err
}

// NewRequest creates an API request.
// A relative URL can be provided in urlStr, in which case it is resolved
// relative to the baseURL of the Client. Relative URLs should always be
// specified without a preceding slash. If specified, the value pointed to by
// body is JSON encoded and included as the request body.
func (c *Client) NewRequest(m, urlStr string, body interface{}) (*http.Request, error) {
	u, err := c.buildURLForRequest(urlStr)
	if err != nil {
		return nil, err
	}

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err = json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(m, u, buf)
	if err != nil {
		return nil, err
	}

	// Apply Authentication if credentials are present.
	// Documentation: https://developer.db.com/#/apidocumentation/apiauthorizationguide
	if c.Authentication.HasAuth() {
		req.Header.Add("Authorization", "Bearer "+c.Authentication.Token())
	}

	req.Header.Add("Accept", "application/json")

	return req, nil
}

// buildURLForRequest will build the URL (as string) that will be called. It
// does several cleaning tasks for us.
func (c *Client) buildURLForRequest(urlStr string) (string, error) {
	u := c.baseURL.String() + c.version.String()

	// If there is no / at the end, add one.
	if strings.HasSuffix(u, "/") == false {
		u += "/"
	}

	// If there is a "/" at the start, remove it.
	if strings.HasPrefix(urlStr, "/") == true {
		urlStr = urlStr[1:]
	}

	rel, err := url.Parse(urlStr)
	if err != nil {
		return "", err
	}
	u += rel.String()

	return u, nil
}
<commit_msg>Improve formatting<commit_after>package dbapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const (
	// GET is a shortcut for http.MethodGet.
	GET = http.MethodGet
)

const (
	// V1 is the version 1 of the dbAPI.
	V1 = "v1"
)

const (
	// DefaultURL is the URL of the Deutsche Bank API which is used by default.
	DefaultURL = "https://simulator-api.db.com/gw/dbapi/"

	// DefaultVersion is the default API version to use and defaults to v1.
	DefaultVersion = V1
)

var (
	// ErrInvalidClient is raised when the costum HTTP client is invalid (e.g.
	// nil).
	ErrInvalidClient = errors.New("Invalid http client!")

	// ErrInvalidURL is raised when the url couldn't be parsed by url.Parse().
	ErrInvalidURL = errors.New("Invalid url!")
)

// A Client manages communication with the Deutsche Bank API.
type Client struct {
	client  *http.Client
	baseURL *url.URL
	version Version

	// Authentication
	Authentication *AuthenticationService

	// API Resources
	Addresses    *AddressesService
	Accounts     *AccountsService
	Transactions *TransactionsService
	UserInfo     *UserInfoService
}

// A Response represents a http response from the Deutsche Bank API. It is a
// wrapper around the standard http.Response type.
type Response struct {
	*http.Response
}

// Version is the API version.
type Version string

func (v Version) String() string {
	return string(v)
}

// An Option serves as a 'functional parameter' which can be used to configure
// the behaviour of the API Client.
type Option func(c *Client) error

// SetClient specifies a custom http client that should be used to make requests.
// An error ErrInvalidClient is returned if the passed client is nil.
func SetClient(client *http.Client) Option {
	return func(c *Client) error { return c.setClient(client) }
}
func (c *Client) setClient(client *http.Client) error {
	if client == nil {
		return ErrInvalidClient
	}
	c.client = client
	return nil
}

// SetToken specifies the api token.
func SetToken(token string) Option {
	return func(c *Client) error { return c.setToken(token) }
}
func (c *Client) setToken(token string) error {
	c.Authentication.token = token
	return nil
}

// SetURL specifies the base url to use. An error ErrInvalidURL is returned if
// the passed url string can't be parsed properly.
func SetURL(urlStr string) Option {
	return func(c *Client) error { return c.setURL(urlStr) }
}
func (c *Client) setURL(urlStr string) error {
	if len(urlStr) == 0 {
		return ErrInvalidURL
	}
	// If there is no / at the end, add one.
	if strings.HasSuffix(urlStr, "/") == false {
		urlStr += "/"
	}
	url, err := url.Parse(urlStr)
	if err != nil {
		return ErrInvalidURL
	}
	c.baseURL = url
	return nil
}

// SetVersion specifies the api version to use.
func SetVersion(version Version) Option {
	return func(c *Client) error { return c.setVersion(version) }
}
func (c *Client) setVersion(version Version) error {
	c.version = version
	return nil
}

// New creates and returns a new API Client. Options can be passed to configure
// the Client.
func New(options ...Option) (*Client, error) {
	// Parse the DefaultURL.
	url, err := url.Parse(DefaultURL)
	if err != nil {
		return nil, err
	}

	// Create client with default settings.
	c := &Client{
		client:  http.DefaultClient,
		baseURL: url,
		version: DefaultVersion,
	}
	c.Authentication = &AuthenticationService{}
	c.Addresses = &AddressesService{client: c}
	c.Accounts = &AccountsService{client: c}
	c.Transactions = &TransactionsService{client: c}
	c.UserInfo = &UserInfoService{client: c}

	// Apply supplied options.
	if err := c.Options(options...); err != nil {
		return nil, err
	}

	return c, nil
}

// Options applies Options to a client instance.
func (c *Client) Options(options ...Option) error {
	for _, option := range options {
		if err := option(c); err != nil {
			return err
		}
	}
	return nil
}

// Call combines Client.NewRequest() and Client.Do() methodes to avoid code
// duplication.
//
// m is the HTTP method you want to call.
// u is the URL you want to call.
// b is the HTTP body.
// r is the HTTP response.
//
// For more information read https://github.com/google/go-github/issues/234
func (c *Client) Call(m, u string, b interface{}, r interface{}) (*Response, error) {
	req, err := c.NewRequest(m, u, b)
	if err != nil {
		return nil, err
	}

	resp, err := c.Do(req, r)
	if err != nil {
		return resp, err
	}

	return resp, err
}

// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if it has a status code outside the 200 range.
func CheckResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	return fmt.Errorf("API call to %s failed: %s", r.Request.URL.String(), r.Status)
}

// Do sends an API request and returns the API response.
// The API response is JSON decoded and stored in the value pointed to by r, or
// returned as an error if an API error has occurred. If r implements the
// io.Writer interface, the raw response body will be written to r, without
// attempting to first decode it.
func (c *Client) Do(req *http.Request, r interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	// Wrap response
	response := &Response{Response: resp}

	err = CheckResponse(resp)
	if err != nil {
		// Return respone in case the caller wants to inspect it further.
		return response, err
	}

	if r != nil {
		if w, ok := r.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			var body []byte
			body, err = ioutil.ReadAll(resp.Body)
			if err != nil {
				// Return respone in case the caller wants to inspect it further.
				return response, err
			}
			err = json.Unmarshal(body, r)
		}
	}
	return response, err
}

// NewRequest creates an API request.
// A relative URL can be provided in urlStr, in which case it is resolved
// relative to the baseURL of the Client. Relative URLs should always be
// specified without a preceding slash. If specified, the value pointed to by
// body is JSON encoded and included as the request body.
func (c *Client) NewRequest(m, urlStr string, body interface{}) (*http.Request, error) {
	u, err := c.buildURLForRequest(urlStr)
	if err != nil {
		return nil, err
	}

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err = json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(m, u, buf)
	if err != nil {
		return nil, err
	}

	// Apply Authentication if credentials are present.
	// Documentation: https://developer.db.com/#/apidocumentation/apiauthorizationguide
	if c.Authentication.HasAuth() {
		req.Header.Add("Authorization", "Bearer "+c.Authentication.Token())
	}

	req.Header.Add("Accept", "application/json")

	return req, nil
}

// buildURLForRequest will build the URL (as string) that will be called. It
// does several cleaning tasks for us.
func (c *Client) buildURLForRequest(urlStr string) (string, error) {
	u := c.baseURL.String() + c.version.String()

	// If there is no / at the end, add one.
	if strings.HasSuffix(u, "/") == false {
		u += "/"
	}

	// If there is a "/" at the start, remove it.
	if strings.HasPrefix(urlStr, "/") == true {
		urlStr = urlStr[1:]
	}

	rel, err := url.Parse(urlStr)
	if err != nil {
		return "", err
	}
	u += rel.String()

	return u, nil
}
<|endoftext|>


<commit_before>package couchbase

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

const MAX_RETRY_COUNT = 3

// ViewDefinition represents a single view within a design document.
type ViewDefinition struct {
	Map    string `json:"map"`
	Reduce string `json:"reduce,omitempty"`
}

// DDoc is the document body of a design document specifying a view.
type DDoc struct {
	Language string                    `json:"language,omitempty"`
	Views    map[string]ViewDefinition `json:"views"`
}

// DDocsResult represents the result from listing the design
// documents.
type DDocsResult struct {
	Rows []struct {
		DDoc struct {
			Meta map[string]interface{}
			JSON DDoc
		} `json:"doc"`
	} `json:"rows"`
}

// GetDDocs lists all design documents
func (b *Bucket) GetDDocs() (DDocsResult, error) {
	var ddocsResult DDocsResult

	err := b.pool.client.parseURLResponse(b.DDocs.URI, &ddocsResult)
	if err != nil {
		return DDocsResult{}, err
	}
	return ddocsResult, nil
}

func (b *Bucket) GetDDocWithRetry(docname string, into interface{}) error {

	ddocURI := fmt.Sprintf("/%s/_design/%s", b.Name, docname)
	err := b.parseAPIResponse(ddocURI, &into)
	if err != nil {
		return err
	}
	return nil
}

func (b *Bucket) GetDDocsWithRetry() (DDocsResult, error) {
	var ddocsResult DDocsResult

	err := b.parseURLResponse(b.DDocs.URI, &ddocsResult)
	if err != nil {
		return DDocsResult{}, err
	}
	return ddocsResult, nil
}

func (b *Bucket) ddocURL(docname string) (string, error) {
	u, err := b.randomBaseURL()
	if err != nil {
		return "", err
	}
	u.Path = fmt.Sprintf("/%s/_design/%s", b.Name, docname)
	return u.String(), nil
}

// PutDDoc installs a design document.
func (b *Bucket) PutDDoc(docname string, value interface{}) error {

	var Err error
	for retryCount := 0; retryCount < MAX_RETRY_COUNT; retryCount++ {

		Err = nil
		ddocU, err := b.ddocURL(docname)
		if err != nil {
			return err
		}

		j, err := json.Marshal(value)
		if err != nil {
			return err
		}

		req, err := http.NewRequest("PUT", ddocU, bytes.NewReader(j))
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/json")
		err = maybeAddAuth(req, b.authHandler())
		if err != nil {
			return err
		}

		res, err := HTTPClient.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		if res.StatusCode != 201 {
			body, _ := ioutil.ReadAll(res.Body)
			Err = fmt.Errorf("error installing view: %v / %s",
				res.Status, body)
			log.Printf(" Error in PutDDOC %v. Retrying...", Err)
			b.Refresh()
			continue
		}
		break
	}

	return Err
}

// GetDDoc retrieves a specific a design doc.
func (b *Bucket) GetDDoc(docname string, into interface{}) error {
	var Err error
	var res *http.Response

	for retryCount := 0; retryCount < MAX_RETRY_COUNT; retryCount++ {

		Err = nil
		ddocU, err := b.ddocURL(docname)
		if err != nil {
			return err
		}

		req, err := http.NewRequest("GET", ddocU, nil)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/json")
		err = maybeAddAuth(req, b.authHandler())
		if err != nil {
			return err
		}

		res, err = HTTPClient.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		if res.StatusCode != 200 {
			body, _ := ioutil.ReadAll(res.Body)
			Err = fmt.Errorf("error reading view: %v / %s",
				res.Status, body)
			log.Printf(" Error in GetDDOC %v Retrying...", Err)
			b.Refresh()
			continue
		}
	}

	if Err != nil {
		return Err
	}

	d := json.NewDecoder(res.Body)
	return d.Decode(into)
}

// DeleteDDoc removes a design document.
func (b *Bucket) DeleteDDoc(docname string) error {

	var Err error
	for retryCount := 0; retryCount < MAX_RETRY_COUNT; retryCount++ {

		Err = nil
		ddocU, err := b.ddocURL(docname)
		if err != nil {
			return err
		}

		req, err := http.NewRequest("DELETE", ddocU, nil)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/json")
		err = maybeAddAuth(req, b.authHandler())
		if err != nil {
			return err
		}

		res, err := HTTPClient.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		if res.StatusCode != 200 {
			body, _ := ioutil.ReadAll(res.Body)
			Err = fmt.Errorf("error deleting view : %v / %s", res.Status, body)
			log.Printf(" Error in DeleteDDOC %v. Retrying ... ", Err)
			b.Refresh()
			continue
		}

		break
	}
	return Err
}
<commit_msg>Handle response body close on retry and match the number of retries to equal the number of nodes in the cluste<commit_after>package couchbase

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

// ViewDefinition represents a single view within a design document.
type ViewDefinition struct {
	Map    string `json:"map"`
	Reduce string `json:"reduce,omitempty"`
}

// DDoc is the document body of a design document specifying a view.
type DDoc struct {
	Language string                    `json:"language,omitempty"`
	Views    map[string]ViewDefinition `json:"views"`
}

// DDocsResult represents the result from listing the design
// documents.
type DDocsResult struct {
	Rows []struct {
		DDoc struct {
			Meta map[string]interface{}
			JSON DDoc
		} `json:"doc"`
	} `json:"rows"`
}

// GetDDocs lists all design documents
func (b *Bucket) GetDDocs() (DDocsResult, error) {
	var ddocsResult DDocsResult

	err := b.pool.client.parseURLResponse(b.DDocs.URI, &ddocsResult)
	if err != nil {
		return DDocsResult{}, err
	}
	return ddocsResult, nil
}

func (b *Bucket) GetDDocWithRetry(docname string, into interface{}) error {

	ddocURI := fmt.Sprintf("/%s/_design/%s", b.Name, docname)
	err := b.parseAPIResponse(ddocURI, &into)
	if err != nil {
		return err
	}
	return nil
}

func (b *Bucket) GetDDocsWithRetry() (DDocsResult, error) {
	var ddocsResult DDocsResult

	err := b.parseURLResponse(b.DDocs.URI, &ddocsResult)
	if err != nil {
		return DDocsResult{}, err
	}
	return ddocsResult, nil
}

func (b *Bucket) ddocURL(docname string) (string, error) {
	u, err := b.randomBaseURL()
	if err != nil {
		return "", err
	}
	u.Path = fmt.Sprintf("/%s/_design/%s", b.Name, docname)
	return u.String(), nil
}

// PutDDoc installs a design document.
func (b *Bucket) PutDDoc(docname string, value interface{}) error {

	var Err error

	nodes := b.Nodes()
	if len(nodes) == 0 {
		return fmt.Errorf("no couch rest URLs")
	}
	maxRetries := len(nodes)

	for retryCount := 0; retryCount < maxRetries; retryCount++ {

		Err = nil
		ddocU, err := b.ddocURL(docname)
		if err != nil {
			return err
		}

		j, err := json.Marshal(value)
		if err != nil {
			return err
		}

		req, err := http.NewRequest("PUT", ddocU, bytes.NewReader(j))
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/json")
		err = maybeAddAuth(req, b.authHandler())
		if err != nil {
			return err
		}

		res, err := HTTPClient.Do(req)
		if err != nil {
			return err
		}

		if res.StatusCode != 201 {
			body, _ := ioutil.ReadAll(res.Body)
			Err = fmt.Errorf("error installing view: %v / %s",
				res.Status, body)
			log.Printf(" Error in PutDDOC %v. Retrying...", Err)
			res.Body.Close()
			b.Refresh()
			continue
		}

		res.Body.Close()
		break
	}

	return Err
}

// GetDDoc retrieves a specific a design doc.
func (b *Bucket) GetDDoc(docname string, into interface{}) error {
	var Err error
	var res *http.Response

	nodes := b.Nodes()
	if len(nodes) == 0 {
		return fmt.Errorf("no couch rest URLs")
	}
	maxRetries := len(nodes)

	for retryCount := 0; retryCount < maxRetries; retryCount++ {

		Err = nil
		ddocU, err := b.ddocURL(docname)
		if err != nil {
			return err
		}

		req, err := http.NewRequest("GET", ddocU, nil)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/json")
		err = maybeAddAuth(req, b.authHandler())
		if err != nil {
			return err
		}

		res, err = HTTPClient.Do(req)
		if err != nil {
			return err
		}
		if res.StatusCode != 200 {
			body, _ := ioutil.ReadAll(res.Body)
			Err = fmt.Errorf("error reading view: %v / %s",
				res.Status, body)
			log.Printf(" Error in GetDDOC %v Retrying...", Err)
			b.Refresh()
			res.Body.Close()
			continue
		}
		res.Body.Close()
		break
	}

	if Err != nil {
		return Err
	}

	d := json.NewDecoder(res.Body)
	return d.Decode(into)
}

// DeleteDDoc removes a design document.
func (b *Bucket) DeleteDDoc(docname string) error {

	var Err error
	nodes := b.Nodes()
	if len(nodes) == 0 {
		return fmt.Errorf("no couch rest URLs")
	}
	maxRetries := len(nodes)

	for retryCount := 0; retryCount < maxRetries; retryCount++ {

		Err = nil
		ddocU, err := b.ddocURL(docname)
		if err != nil {
			return err
		}

		req, err := http.NewRequest("DELETE", ddocU, nil)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/json")
		err = maybeAddAuth(req, b.authHandler())
		if err != nil {
			return err
		}

		res, err := HTTPClient.Do(req)
		if err != nil {
			return err
		}
		if res.StatusCode != 200 {
			body, _ := ioutil.ReadAll(res.Body)
			Err = fmt.Errorf("error deleting view : %v / %s", res.Status, body)
			log.Printf(" Error in DeleteDDOC %v. Retrying ... ", Err)
			b.Refresh()
			res.Body.Close()
			continue
		}

		res.Body.Close()
		break
	}
	return Err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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 auth

import (
	"testing"

	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/stretchr/testify/assert"
)

var (
	CrossTenantNetworkResourceNegativeConfig = []*AzureAuthConfig{
		{
			TenantID:        "TenantID",
			AADClientID:     "AADClientID",
			AADClientSecret: "AADClientSecret",
		},
		{
			TenantID:                      "TenantID",
			AADClientID:                   "AADClientID",
			AADClientSecret:               "AADClientSecret",
			NetworkResourceTenantID:       "NetworkResourceTenantID",
			NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
			IdentitySystem:                ADFSIdentitySystem,
		},
		{
			TenantID:                      "TenantID",
			AADClientID:                   "AADClientID",
			AADClientSecret:               "AADClientSecret",
			NetworkResourceTenantID:       "NetworkResourceTenantID",
			NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
			UseManagedIdentityExtension:   true,
		},
	}
)

func TestGetServicePrincipalToken(t *testing.T) {
	config := &AzureAuthConfig{
		TenantID:        "TenantID",
		AADClientID:     "AADClientID",
		AADClientSecret: "AADClientSecret",
	}
	env := &azure.PublicCloud

	token, err := GetServicePrincipalToken(config, env)
	assert.NoError(t, err)

	oauthConfig, err := adal.NewOAuthConfigWithAPIVersion(env.ActiveDirectoryEndpoint, config.TenantID, nil)
	assert.NoError(t, err)

	spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.AADClientID, config.AADClientSecret, env.ServiceManagementEndpoint)
	assert.NoError(t, err)

	assert.Equal(t, token, spt)
}

func TestGetMultiTenantServicePrincipalToken(t *testing.T) {
	config := &AzureAuthConfig{
		TenantID:                      "TenantID",
		AADClientID:                   "AADClientID",
		AADClientSecret:               "AADClientSecret",
		NetworkResourceTenantID:       "NetworkResourceTenantID",
		NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
	}
	env := &azure.PublicCloud

	multiTenantToken, err := GetMultiTenantServicePrincipalToken(config, env)
	assert.NoError(t, err)

	multiTenantOAuthConfig, err := adal.NewMultiTenantOAuthConfig(env.ActiveDirectoryEndpoint, config.TenantID, []string{config.NetworkResourceTenantID}, adal.OAuthOptions{})
	assert.NoError(t, err)

	spt, err := adal.NewMultiTenantServicePrincipalToken(multiTenantOAuthConfig, config.AADClientID, config.AADClientSecret, env.ServiceManagementEndpoint)
	assert.NoError(t, err)

	assert.Equal(t, multiTenantToken, spt)
}

func TestGetMultiTenantServicePrincipalTokenNegative(t *testing.T) {
	env := &azure.PublicCloud
	for _, config := range CrossTenantNetworkResourceNegativeConfig {
		_, err := GetMultiTenantServicePrincipalToken(config, env)
		assert.Error(t, err)
	}
}

func TestGetNetworkResourceServicePrincipalToken(t *testing.T) {
	config := &AzureAuthConfig{
		TenantID:                      "TenantID",
		AADClientID:                   "AADClientID",
		AADClientSecret:               "AADClientSecret",
		NetworkResourceTenantID:       "NetworkResourceTenantID",
		NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
	}
	env := &azure.PublicCloud

	token, err := GetNetworkResourceServicePrincipalToken(config, env)
	assert.NoError(t, err)

	oauthConfig, err := adal.NewOAuthConfigWithAPIVersion(env.ActiveDirectoryEndpoint, config.NetworkResourceTenantID, nil)
	assert.NoError(t, err)

	spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.AADClientID, config.AADClientSecret, env.ServiceManagementEndpoint)
	assert.NoError(t, err)

	assert.Equal(t, token, spt)
}

func TestGetNetworkResourceServicePrincipalTokenNegative(t *testing.T) {
	env := &azure.PublicCloud
	for _, config := range CrossTenantNetworkResourceNegativeConfig {
		_, err := GetNetworkResourceServicePrincipalToken(config, env)
		assert.Error(t, err)
	}
}

func TestParseAzureEnvironment(t *testing.T) {
	cases := []struct {
		cloudName               string
		resourceManagerEndpoint string
		identitySystem          string
		expected                *azure.Environment
	}{
		{
			cloudName:               "",
			resourceManagerEndpoint: "",
			identitySystem:          "",
			expected:                &azure.PublicCloud,
		},
		{
			cloudName:               "AZURECHINACLOUD",
			resourceManagerEndpoint: "",
			identitySystem:          "",
			expected:                &azure.ChinaCloud,
		},
	}

	for _, c := range cases {
		env, err := ParseAzureEnvironment(c.cloudName, c.resourceManagerEndpoint, c.identitySystem)
		assert.NoError(t, err)
		assert.Equal(t, env, c.expected)
	}
}

func TestAzureStackOverrides(t *testing.T) {
	env := &azure.PublicCloud
	resourceManagerEndpoint := "https://management.test.com/"

	azureStackOverrides(env, resourceManagerEndpoint, "")
	assert.Equal(t, env.ManagementPortalURL, "https://portal.test.com/")
	assert.Equal(t, env.ServiceManagementEndpoint, env.TokenAudience)
	assert.Equal(t, env.ResourceManagerVMDNSSuffix, "cloudapp.test.com")
	assert.Equal(t, env.ActiveDirectoryEndpoint, "https://login.microsoftonline.com/")

	azureStackOverrides(env, resourceManagerEndpoint, "adfs")
	assert.Equal(t, env.ManagementPortalURL, "https://portal.test.com/")
	assert.Equal(t, env.ServiceManagementEndpoint, env.TokenAudience)
	assert.Equal(t, env.ResourceManagerVMDNSSuffix, "cloudapp.test.com")
	assert.Equal(t, env.ActiveDirectoryEndpoint, "https://login.microsoftonline.com")
}
<commit_msg>Fix test order  staging/src/k8s.io/legacy-cloud-providers/azure/auth/azure_auth_test.go<commit_after>/*
Copyright 2019 The Kubernetes 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 auth

import (
	"testing"

	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/stretchr/testify/assert"
)

var (
	CrossTenantNetworkResourceNegativeConfig = []*AzureAuthConfig{
		{
			TenantID:        "TenantID",
			AADClientID:     "AADClientID",
			AADClientSecret: "AADClientSecret",
		},
		{
			TenantID:                      "TenantID",
			AADClientID:                   "AADClientID",
			AADClientSecret:               "AADClientSecret",
			NetworkResourceTenantID:       "NetworkResourceTenantID",
			NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
			IdentitySystem:                ADFSIdentitySystem,
		},
		{
			TenantID:                      "TenantID",
			AADClientID:                   "AADClientID",
			AADClientSecret:               "AADClientSecret",
			NetworkResourceTenantID:       "NetworkResourceTenantID",
			NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
			UseManagedIdentityExtension:   true,
		},
	}
)

func TestGetServicePrincipalToken(t *testing.T) {
	config := &AzureAuthConfig{
		TenantID:        "TenantID",
		AADClientID:     "AADClientID",
		AADClientSecret: "AADClientSecret",
	}
	env := &azure.PublicCloud

	token, err := GetServicePrincipalToken(config, env)
	assert.NoError(t, err)

	oauthConfig, err := adal.NewOAuthConfigWithAPIVersion(env.ActiveDirectoryEndpoint, config.TenantID, nil)
	assert.NoError(t, err)

	spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.AADClientID, config.AADClientSecret, env.ServiceManagementEndpoint)
	assert.NoError(t, err)

	assert.Equal(t, token, spt)
}

func TestGetMultiTenantServicePrincipalToken(t *testing.T) {
	config := &AzureAuthConfig{
		TenantID:                      "TenantID",
		AADClientID:                   "AADClientID",
		AADClientSecret:               "AADClientSecret",
		NetworkResourceTenantID:       "NetworkResourceTenantID",
		NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
	}
	env := &azure.PublicCloud

	multiTenantToken, err := GetMultiTenantServicePrincipalToken(config, env)
	assert.NoError(t, err)

	multiTenantOAuthConfig, err := adal.NewMultiTenantOAuthConfig(env.ActiveDirectoryEndpoint, config.TenantID, []string{config.NetworkResourceTenantID}, adal.OAuthOptions{})
	assert.NoError(t, err)

	spt, err := adal.NewMultiTenantServicePrincipalToken(multiTenantOAuthConfig, config.AADClientID, config.AADClientSecret, env.ServiceManagementEndpoint)
	assert.NoError(t, err)

	assert.Equal(t, multiTenantToken, spt)
}

func TestGetMultiTenantServicePrincipalTokenNegative(t *testing.T) {
	env := &azure.PublicCloud
	for _, config := range CrossTenantNetworkResourceNegativeConfig {
		_, err := GetMultiTenantServicePrincipalToken(config, env)
		assert.Error(t, err)
	}
}

func TestGetNetworkResourceServicePrincipalToken(t *testing.T) {
	config := &AzureAuthConfig{
		TenantID:                      "TenantID",
		AADClientID:                   "AADClientID",
		AADClientSecret:               "AADClientSecret",
		NetworkResourceTenantID:       "NetworkResourceTenantID",
		NetworkResourceSubscriptionID: "NetworkResourceSubscriptionID",
	}
	env := &azure.PublicCloud

	token, err := GetNetworkResourceServicePrincipalToken(config, env)
	assert.NoError(t, err)

	oauthConfig, err := adal.NewOAuthConfigWithAPIVersion(env.ActiveDirectoryEndpoint, config.NetworkResourceTenantID, nil)
	assert.NoError(t, err)

	spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.AADClientID, config.AADClientSecret, env.ServiceManagementEndpoint)
	assert.NoError(t, err)

	assert.Equal(t, token, spt)
}

func TestGetNetworkResourceServicePrincipalTokenNegative(t *testing.T) {
	env := &azure.PublicCloud
	for _, config := range CrossTenantNetworkResourceNegativeConfig {
		_, err := GetNetworkResourceServicePrincipalToken(config, env)
		assert.Error(t, err)
	}
}

func TestParseAzureEnvironment(t *testing.T) {
	cases := []struct {
		cloudName               string
		resourceManagerEndpoint string
		identitySystem          string
		expected                *azure.Environment
	}{
		{
			cloudName:               "",
			resourceManagerEndpoint: "",
			identitySystem:          "",
			expected:                &azure.PublicCloud,
		},
		{
			cloudName:               "AZURECHINACLOUD",
			resourceManagerEndpoint: "",
			identitySystem:          "",
			expected:                &azure.ChinaCloud,
		},
	}

	for _, c := range cases {
		env, err := ParseAzureEnvironment(c.cloudName, c.resourceManagerEndpoint, c.identitySystem)
		assert.NoError(t, err)
		assert.Equal(t, c.expected, env)
	}
}

func TestAzureStackOverrides(t *testing.T) {
	env := &azure.PublicCloud
	resourceManagerEndpoint := "https://management.test.com/"

	azureStackOverrides(env, resourceManagerEndpoint, "")
	assert.Equal(t, env.ManagementPortalURL, "https://portal.test.com/")
	assert.Equal(t, env.ServiceManagementEndpoint, env.TokenAudience)
	assert.Equal(t, env.ResourceManagerVMDNSSuffix, "cloudapp.test.com")
	assert.Equal(t, env.ActiveDirectoryEndpoint, "https://login.microsoftonline.com/")

	azureStackOverrides(env, resourceManagerEndpoint, "adfs")
	assert.Equal(t, env.ManagementPortalURL, "https://portal.test.com/")
	assert.Equal(t, env.ServiceManagementEndpoint, env.TokenAudience)
	assert.Equal(t, env.ResourceManagerVMDNSSuffix, "cloudapp.test.com")
	assert.Equal(t, env.ActiveDirectoryEndpoint, "https://login.microsoftonline.com")
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
)

var cmdWait = &Command{
	Exec:        runWait,
	UsageLine:   "wait [OPTIONS] SERVER [SERVER...]",
	Description: "Block until a server stops",
	Help:        "Block until a server stops.",
}

func init() {
	cmdWait.Flag.BoolVar(&waitHelp, []string{"h", "-help"}, false, "Print usage")
}

// Flags
var waitHelp bool // -h, --help flag

func runWait(cmd *Command, args []string) {
	if waitHelp {
		cmd.PrintUsage()
	}
	if len(args) != 0 {
		cmd.PrintShortUsage()
	}

	has_error := false
	for _, needle := range args {
		server_identifier := cmd.GetServer(needle)
		for {
			server, err := cmd.API.GetServer(server_identifier)
			if err != nil {
				log.Errorf("failed to retrieve information from server %s: %s", server_identifier, err)
				has_error = true
				break
			}
			if server.State == "stopped" {
				break
			}
			time.Sleep(1 * time.Second)
		}
	}
	if has_error {
		os.Exit(1)
	}
}
<commit_msg>Fixed wait usage<commit_after>package main

import (
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
)

var cmdWait = &Command{
	Exec:        runWait,
	UsageLine:   "wait [OPTIONS] SERVER [SERVER...]",
	Description: "Block until a server stops",
	Help:        "Block until a server stops.",
}

func init() {
	cmdWait.Flag.BoolVar(&waitHelp, []string{"h", "-help"}, false, "Print usage")
}

// Flags
var waitHelp bool // -h, --help flag

func runWait(cmd *Command, args []string) {
	if waitHelp {
		cmd.PrintUsage()
	}
	if len(args) < 1 {
		cmd.PrintShortUsage()
	}

	has_error := false
	for _, needle := range args {
		server_identifier := cmd.GetServer(needle)
		for {
			server, err := cmd.API.GetServer(server_identifier)
			if err != nil {
				log.Errorf("failed to retrieve information from server %s: %s", server_identifier, err)
				has_error = true
				break
			}
			if server.State == "stopped" {
				break
			}
			time.Sleep(1 * time.Second)
		}
	}
	if has_error {
		os.Exit(1)
	}
}
<|endoftext|>