<commit_before>// +build !linux

/*
Copyright 2014 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 mount

type Mounter struct {
	mounterPath string
}

func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
	return nil
}

func (mounter *Mounter) Unmount(target string) error {
	return nil
}

func (mounter *Mounter) List() ([]MountPoint, error) {
	return []MountPoint{}, nil
}

func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
	return true, nil
}

func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return "", nil
}

func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
	return false, nil
}

func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
	return true, nil
}

func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
	return nil
}

func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
	return true, nil
}
<commit_msg>add IsNotMountPoint() to mount_unsupported.go<commit_after>// +build !linux

/*
Copyright 2014 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 mount

type Mounter struct {
	mounterPath string
}

func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
	return nil
}

func (mounter *Mounter) Unmount(target string) error {
	return nil
}

func (mounter *Mounter) List() ([]MountPoint, error) {
	return []MountPoint{}, nil
}

func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
	return true, nil
}

func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
	return "", nil
}

func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
	return false, nil
}

func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
	return true, nil
}

func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
	return nil
}

func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
	return true, nil
}

func IsNotMountPoint(file string) (bool, error) {
	return true, nil
}
<|endoftext|>


<commit_before>package movingmedian

import (
	"math"
	"math/rand"
	"sort"
	"testing"
)

func TestUnit(t *testing.T) {
	tests := []struct {
		name       string
		windowSize int
		data       []float64
		want       []float64
	}{
		{
			"OneWindowSize",
			1,
			[]float64{1, 3, 5, 7, 9, 11, math.NaN()},
			[]float64{1, 3, 5, 7, 9, 11, math.NaN()},
		},
		{
			"OddWindowSize",
			3,
			[]float64{1, 3, 5, 7, 9, 11},
			[]float64{1, 2, 3, 5, 7, 9},
		},
		{
			"EvenWindowSize",
			4,
			[]float64{1, 3, 5, 7, 9, 11},
			[]float64{1, 2, 3, 4, 6, 8},
		},
		{
			"DecreasingValues",
			4,
			[]float64{19, 17, 15, 13, 11, 9},
			[]float64{19, 18, 17, 16, 14, 12},
		},
		{
			"DecreasingIncreasingValues",
			4,
			[]float64{21, 19, 17, 15, 13, 11, 13, 15, 17, 19},
			[]float64{21, 20, 19, 18, 16, 14, 13, 13, 14, 16},
		},
		{
			"IncreasingDecreasingValues",
			4,
			[]float64{11, 13, 15, 17, 19, 21, 19, 17, 15, 13},
			[]float64{11, 12, 13, 14, 16, 18, 19, 19, 18, 16},
		},
		{

			"ZigZag",
			4,
			[]float64{21, 23, 17, 27, 13, 31, 9, 35, 5, 39, 1},
			[]float64{21, 22, 21, 22, 20, 22, 20, 22, 20, 22, 20},
		},
		{

			"NewValuesInBetween",
			4,
			[]float64{21, 21, 19, 19, 21, 21, 19, 19, 19, 19},
			[]float64{21, 21, 21, 20, 20, 20, 20, 20, 19, 19},
		},
		{
			"SameNumberInBothHeaps3Times",
			4,
			[]float64{11, 13, 13, 13, 25, 27, 29, 31},
			[]float64{11, 12, 13, 13, 13, 19, 26, 28},
		},
		{
			"SameNumberInBothHeaps3TimesDecreasing",
			4,
			[]float64{31, 29, 29, 29, 17, 15, 13, 11},
			[]float64{31, 30, 29, 29, 29, 23, 16, 14},
		},
		{
			"SameNumberInBothHeaps4Times",
			4,
			[]float64{11, 13, 13, 13, 13, 25, 27, 29, 31},
			[]float64{11, 12, 13, 13, 13, 13, 19, 26, 28},
		},
	}

	for _, test := range tests {
		t.Log("test name", test.name)
		m := NewMovingMedian(test.windowSize)
		for i, v := range test.data {
			m.Push(v)
			actual := m.Median()
			if test.want[i] != actual && !(math.IsNaN(actual) && math.IsNaN(test.want[i])) {
				firstElement := 1 + i - test.windowSize
				if firstElement < 0 {
					firstElement = 0
				}
				t.Errorf("failed on test %s index %d the median of %f is %f and not %f",
					test.name,
					i,
					test.data[firstElement:1+i],
					test.want[i],
					actual)
			}
		}
	}
}

func TestRandom(t *testing.T) {
	rangeSize := 1000
	for windowSize := 1; windowSize < 50; windowSize++ {
		data := getData(rangeSize, windowSize)
		intData := make([]int, rangeSize)
		for i, v := range data {
			intData[i] = int(v)
		}

		t.Log("test name random test window size", windowSize)
		m := NewMovingMedian(windowSize)
		for i, v := range data {
			want := median(data, i, windowSize)

			m.Push(v)
			actual := m.Median()
			if want != actual {
				firstElement := 1 + i - windowSize
				if firstElement < 0 {
					firstElement = 0
				}

				t.Errorf("failed on test random window size %d index %d the median of %d is %f and not %f",
					windowSize,
					i,
					intData[firstElement:1+i],
					want,
					actual)
			}
		}
	}
}

func Benchmark_10values_windowsize1(b *testing.B) {
	benchmark(b, 10, 1)
}

func Benchmark_100values_windowsize10(b *testing.B) {
	benchmark(b, 100, 10)
}

func Benchmark_10Kvalues_windowsize100(b *testing.B) {
	benchmark(b, 10000, 100)
}

func Benchmark_10Kvalues_windowsize1000(b *testing.B) {
	benchmark(b, 10000, 1000)
}

func benchmark(b *testing.B, numberOfValues, windowSize int) {
	data := getData(numberOfValues, windowSize)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m := NewMovingMedian(windowSize)
		for _, v := range data {
			m.Push(v)
			m.Median()
		}
	}
}

func getData(rangeSize, windowSize int) []float64 {
	var data = make([]float64, rangeSize)
	var r = rand.New(rand.NewSource(99))
	for i, _ := range data {
		data[i] = math.Floor(1000 * r.Float64())
	}

	return data
}

func median(data []float64, i, windowSize int) float64 {
	min := 1 + i - windowSize
	if min < 0 {
		min = 0
	}

	if len(data) == 0 {
		return math.NaN()
	}

	window := make([]float64, 1+i-min)
	copy(window, data[min:i+1])

	if len(window) == 1 {
		return window[0]
	}

	sort.Float64s(window)

	k := len(window) / 2
	if len(window)%2 == 1 {
		return window[k]
	}

	return 0.5*window[k-1] + 0.5*window[k]
}
<commit_msg>remove unneeded _ from for loop<commit_after>package movingmedian

import (
	"math"
	"math/rand"
	"sort"
	"testing"
)

func TestUnit(t *testing.T) {
	tests := []struct {
		name       string
		windowSize int
		data       []float64
		want       []float64
	}{
		{
			"OneWindowSize",
			1,
			[]float64{1, 3, 5, 7, 9, 11, math.NaN()},
			[]float64{1, 3, 5, 7, 9, 11, math.NaN()},
		},
		{
			"OddWindowSize",
			3,
			[]float64{1, 3, 5, 7, 9, 11},
			[]float64{1, 2, 3, 5, 7, 9},
		},
		{
			"EvenWindowSize",
			4,
			[]float64{1, 3, 5, 7, 9, 11},
			[]float64{1, 2, 3, 4, 6, 8},
		},
		{
			"DecreasingValues",
			4,
			[]float64{19, 17, 15, 13, 11, 9},
			[]float64{19, 18, 17, 16, 14, 12},
		},
		{
			"DecreasingIncreasingValues",
			4,
			[]float64{21, 19, 17, 15, 13, 11, 13, 15, 17, 19},
			[]float64{21, 20, 19, 18, 16, 14, 13, 13, 14, 16},
		},
		{
			"IncreasingDecreasingValues",
			4,
			[]float64{11, 13, 15, 17, 19, 21, 19, 17, 15, 13},
			[]float64{11, 12, 13, 14, 16, 18, 19, 19, 18, 16},
		},
		{

			"ZigZag",
			4,
			[]float64{21, 23, 17, 27, 13, 31, 9, 35, 5, 39, 1},
			[]float64{21, 22, 21, 22, 20, 22, 20, 22, 20, 22, 20},
		},
		{

			"NewValuesInBetween",
			4,
			[]float64{21, 21, 19, 19, 21, 21, 19, 19, 19, 19},
			[]float64{21, 21, 21, 20, 20, 20, 20, 20, 19, 19},
		},
		{
			"SameNumberInBothHeaps3Times",
			4,
			[]float64{11, 13, 13, 13, 25, 27, 29, 31},
			[]float64{11, 12, 13, 13, 13, 19, 26, 28},
		},
		{
			"SameNumberInBothHeaps3TimesDecreasing",
			4,
			[]float64{31, 29, 29, 29, 17, 15, 13, 11},
			[]float64{31, 30, 29, 29, 29, 23, 16, 14},
		},
		{
			"SameNumberInBothHeaps4Times",
			4,
			[]float64{11, 13, 13, 13, 13, 25, 27, 29, 31},
			[]float64{11, 12, 13, 13, 13, 13, 19, 26, 28},
		},
	}

	for _, test := range tests {
		t.Log("test name", test.name)
		m := NewMovingMedian(test.windowSize)
		for i, v := range test.data {
			m.Push(v)
			actual := m.Median()
			if test.want[i] != actual && !(math.IsNaN(actual) && math.IsNaN(test.want[i])) {
				firstElement := 1 + i - test.windowSize
				if firstElement < 0 {
					firstElement = 0
				}
				t.Errorf("failed on test %s index %d the median of %f is %f and not %f",
					test.name,
					i,
					test.data[firstElement:1+i],
					test.want[i],
					actual)
			}
		}
	}
}

func TestRandom(t *testing.T) {
	rangeSize := 1000
	for windowSize := 1; windowSize < 50; windowSize++ {
		data := getData(rangeSize, windowSize)
		intData := make([]int, rangeSize)
		for i, v := range data {
			intData[i] = int(v)
		}

		t.Log("test name random test window size", windowSize)
		m := NewMovingMedian(windowSize)
		for i, v := range data {
			want := median(data, i, windowSize)

			m.Push(v)
			actual := m.Median()
			if want != actual {
				firstElement := 1 + i - windowSize
				if firstElement < 0 {
					firstElement = 0
				}

				t.Errorf("failed on test random window size %d index %d the median of %d is %f and not %f",
					windowSize,
					i,
					intData[firstElement:1+i],
					want,
					actual)
			}
		}
	}
}

func Benchmark_10values_windowsize1(b *testing.B) {
	benchmark(b, 10, 1)
}

func Benchmark_100values_windowsize10(b *testing.B) {
	benchmark(b, 100, 10)
}

func Benchmark_10Kvalues_windowsize100(b *testing.B) {
	benchmark(b, 10000, 100)
}

func Benchmark_10Kvalues_windowsize1000(b *testing.B) {
	benchmark(b, 10000, 1000)
}

func benchmark(b *testing.B, numberOfValues, windowSize int) {
	data := getData(numberOfValues, windowSize)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m := NewMovingMedian(windowSize)
		for _, v := range data {
			m.Push(v)
			m.Median()
		}
	}
}

func getData(rangeSize, windowSize int) []float64 {
	var data = make([]float64, rangeSize)
	var r = rand.New(rand.NewSource(99))
	for i := range data {
		data[i] = math.Floor(1000 * r.Float64())
	}

	return data
}

func median(data []float64, i, windowSize int) float64 {
	min := 1 + i - windowSize
	if min < 0 {
		min = 0
	}

	if len(data) == 0 {
		return math.NaN()
	}

	window := make([]float64, 1+i-min)
	copy(window, data[min:i+1])

	if len(window) == 1 {
		return window[0]
	}

	sort.Float64s(window)

	k := len(window) / 2
	if len(window)%2 == 1 {
		return window[k]
	}

	return 0.5*window[k-1] + 0.5*window[k]
}
<|endoftext|>


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

package mutable

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/jacobsa/fuse/fsutil"
	"github.com/jacobsa/timeutil"
)

// A temporary file that keeps track of the lowest offset at which it has been
// modified.
type TempFile interface {
	// Panic if any internal invariants are violated.
	CheckInvariants()

	// Semantics matching os.File.
	io.ReadSeeker
	io.ReaderAt
	io.WriterAt
	Truncate(n int64) (err error)

	// Return information about the current state of the content.
	Stat() (sr StatResult, err error)

	// Throw away the resources used by the temporary file. The object must not
	// be used again.
	Destroy()
}

type StatResult struct {
	// The current size in bytes of the content.
	Size int64

	// It is guaranteed that all bytes in the range [0, DirtyThreshold) are
	// unmodified from the original content with which the mutable content object
	// was created.
	DirtyThreshold int64

	// The time at which the content was last updated, or nil if we've never
	// changed it.
	Mtime *time.Time
}

// Create a temp file whose initial contents are given by the supplied reader.
func NewTempFile(
	content io.Reader,
	clock timeutil.Clock) (tf TempFile, err error) {
	// Create an anonymous file to wrap. When we close it, its resources will be
	// magically cleaned up.
	f, err := fsutil.AnonymousFile("")
	if err != nil {
		err = fmt.Errorf("AnonymousFile: %v", err)
		return
	}

	// Copy into the file.
	size, err := io.Copy(f, content)
	if err != nil {
		err = fmt.Errorf("copy: %v", err)
		return
	}

	tf = &tempFile{
		clock:          clock,
		f:              f,
		dirtyThreshold: size,
	}

	return
}

type tempFile struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	destroyed bool

	// A file containing our current contents.
	f *os.File

	// The lowest byte index that has been modified from the initial contents.
	//
	// INVARIANT: Stat().DirtyThreshold <= Stat().Size
	dirtyThreshold int64

	// The time at which a method that modifies our contents was last called, or
	// nil if never.
	//
	// INVARIANT: mtime == nil => Stat().DirtyThreshold == Stat().Size
	mtime *time.Time
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (tf *tempFile) CheckInvariants() {
	if tf.destroyed {
		panic("Use of destroyed tempFile object.")
	}

	// INVARIANT: Stat().DirtyThreshold <= Stat().Size
	sr, err := tf.Stat()
	if err != nil {
		panic(fmt.Sprintf("Stat: %v", err))
	}

	if !(sr.DirtyThreshold <= sr.Size) {
		panic(fmt.Sprintf("Mismatch: %d vs. %d", sr.DirtyThreshold, sr.Size))
	}

	// INVARIANT: mtime == nil => Stat().DirtyThreshold == Stat().Size
	if tf.mtime == nil && sr.DirtyThreshold != sr.Size {
		panic(fmt.Sprintf("Mismatch: %d vs. %d", sr.DirtyThreshold, sr.Size))
	}
}

func (tf *tempFile) Destroy() {
	tf.destroyed = true

	// Throw away the file.
	tf.f.Close()
	tf.f = nil
}

func (tf *tempFile) Read(p []byte) (int, error) {
	return tf.f.Read(p)
}

func (tf *tempFile) Seek(offset int64, whence int) (int64, error) {
	return tf.f.Seek(offset, whence)
}

func (tf *tempFile) ReadAt(p []byte, offset int64) (int, error) {
	return tf.f.ReadAt(p, offset)
}

func (tf *tempFile) Stat() (sr StatResult, err error) {
	sr.DirtyThreshold = tf.dirtyThreshold
	sr.Mtime = tf.mtime

	// Get the size from the file.
	sr.Size, err = tf.f.Seek(0, 2)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	return
}

func (tf *tempFile) WriteAt(p []byte, offset int64) (int, error) {
	// Update our state regarding being dirty.
	tf.dirtyThreshold = minInt64(tf.dirtyThreshold, offset)

	newMtime := tf.clock.Now()
	tf.mtime = &newMtime

	// Call through.
	return tf.f.WriteAt(p, offset)
}

func (tf *tempFile) Truncate(n int64) error {
	// Update our state regarding being dirty.
	tf.dirtyThreshold = minInt64(tf.dirtyThreshold, n)

	newMtime := tf.clock.Now()
	tf.mtime = &newMtime

	// Call through.
	return tf.f.Truncate(n)
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func minInt64(a int64, b int64) int64 {
	if a < b {
		return a
	}

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

package mutable

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/jacobsa/fuse/fsutil"
	"github.com/jacobsa/timeutil"
)

// A temporary file that keeps track of the lowest offset at which it has been
// modified.
//
// Not safe for concurrent access.
type TempFile interface {
	// Panic if any internal invariants are violated.
	CheckInvariants()

	// Semantics matching os.File.
	io.ReadSeeker
	io.ReaderAt
	io.WriterAt
	Truncate(n int64) (err error)

	// Return information about the current state of the content. May invalidate
	// the seek position.
	Stat() (sr StatResult, err error)

	// Throw away the resources used by the temporary file. The object must not
	// be used again.
	Destroy()
}

type StatResult struct {
	// The current size in bytes of the content.
	Size int64

	// It is guaranteed that all bytes in the range [0, DirtyThreshold) are
	// unmodified from the original content with which the mutable content object
	// was created.
	DirtyThreshold int64

	// The time at which the content was last updated, or nil if we've never
	// changed it.
	Mtime *time.Time
}

// Create a temp file whose initial contents are given by the supplied reader.
func NewTempFile(
	content io.Reader,
	clock timeutil.Clock) (tf TempFile, err error) {
	// Create an anonymous file to wrap. When we close it, its resources will be
	// magically cleaned up.
	f, err := fsutil.AnonymousFile("")
	if err != nil {
		err = fmt.Errorf("AnonymousFile: %v", err)
		return
	}

	// Copy into the file.
	size, err := io.Copy(f, content)
	if err != nil {
		err = fmt.Errorf("copy: %v", err)
		return
	}

	tf = &tempFile{
		clock:          clock,
		f:              f,
		dirtyThreshold: size,
	}

	return
}

type tempFile struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	destroyed bool

	// A file containing our current contents.
	f *os.File

	// The lowest byte index that has been modified from the initial contents.
	//
	// INVARIANT: Stat().DirtyThreshold <= Stat().Size
	dirtyThreshold int64

	// The time at which a method that modifies our contents was last called, or
	// nil if never.
	//
	// INVARIANT: mtime == nil => Stat().DirtyThreshold == Stat().Size
	mtime *time.Time
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (tf *tempFile) CheckInvariants() {
	if tf.destroyed {
		panic("Use of destroyed tempFile object.")
	}

	// Restore the seek position after using Stat below.
	pos, err := tf.Seek(0, 1)
	if err != nil {
		panic(fmt.Sprintf("Seek: %v", err))
	}

	defer func() {
		_, err := tf.Seek(pos, 0)
		if err != nil {
			panic(fmt.Sprintf("Seek: %v", err))
		}
	}()

	// INVARIANT: Stat().DirtyThreshold <= Stat().Size
	sr, err := tf.Stat()
	if err != nil {
		panic(fmt.Sprintf("Stat: %v", err))
	}

	if !(sr.DirtyThreshold <= sr.Size) {
		panic(fmt.Sprintf("Mismatch: %d vs. %d", sr.DirtyThreshold, sr.Size))
	}

	// INVARIANT: mtime == nil => Stat().DirtyThreshold == Stat().Size
	if tf.mtime == nil && sr.DirtyThreshold != sr.Size {
		panic(fmt.Sprintf("Mismatch: %d vs. %d", sr.DirtyThreshold, sr.Size))
	}
}

func (tf *tempFile) Destroy() {
	tf.destroyed = true

	// Throw away the file.
	tf.f.Close()
	tf.f = nil
}

func (tf *tempFile) Read(p []byte) (int, error) {
	return tf.f.Read(p)
}

func (tf *tempFile) Seek(offset int64, whence int) (int64, error) {
	return tf.f.Seek(offset, whence)
}

func (tf *tempFile) ReadAt(p []byte, offset int64) (int, error) {
	return tf.f.ReadAt(p, offset)
}

func (tf *tempFile) Stat() (sr StatResult, err error) {
	sr.DirtyThreshold = tf.dirtyThreshold
	sr.Mtime = tf.mtime

	// Get the size from the file.
	sr.Size, err = tf.f.Seek(0, 2)
	if err != nil {
		err = fmt.Errorf("Seek: %v", err)
		return
	}

	return
}

func (tf *tempFile) WriteAt(p []byte, offset int64) (int, error) {
	// Update our state regarding being dirty.
	tf.dirtyThreshold = minInt64(tf.dirtyThreshold, offset)

	newMtime := tf.clock.Now()
	tf.mtime = &newMtime

	// Call through.
	return tf.f.WriteAt(p, offset)
}

func (tf *tempFile) Truncate(n int64) error {
	// Update our state regarding being dirty.
	tf.dirtyThreshold = minInt64(tf.dirtyThreshold, n)

	newMtime := tf.clock.Now()
	tf.mtime = &newMtime

	// Call through.
	return tf.f.Truncate(n)
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func minInt64(a int64, b int64) int64 {
	if a < b {
		return a
	}

	return b
}
<|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 mvcc

import (
	"bytes"
	"fmt"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/coreos/etcd/lease"
	"github.com/coreos/etcd/mvcc/backend"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"go.uber.org/zap"
)

// TestWatcherWatchID tests that each watcher provides unique watchID,
// and the watched event attaches the correct watchID.
func TestWatcherWatchID(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	idm := make(map[WatchID]struct{})

	for i := 0; i < 10; i++ {
		id, _ := w.Watch(0, []byte("foo"), nil, 0)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: id %d exists", i, id)
		}
		idm[id] = struct{}{}

		s.Put([]byte("foo"), []byte("bar"), lease.NoLease)

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Error(err)
		}
	}

	s.Put([]byte("foo2"), []byte("bar"), lease.NoLease)

	// unsynced watchers
	for i := 10; i < 20; i++ {
		id, _ := w.Watch(0, []byte("foo2"), nil, 1)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: id %d exists", i, id)
		}
		idm[id] = struct{}{}

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Error(err)
		}
	}
}

func TestWatcherRequestsCustomID(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	// - Request specifically ID #1
	// - Try to duplicate it, get an error
	// - Make sure the auto-assignment skips over things we manually assigned

	tt := []struct {
		givenID     WatchID
		expectedID  WatchID
		expectedErr error
	}{
		{1, 1, nil},
		{1, 0, ErrWatcherDuplicateID},
		{0, 0, nil},
		{0, 2, nil},
	}

	for i, tcase := range tt {
		id, err := w.Watch(tcase.givenID, []byte("foo"), nil, 0)
		if tcase.expectedErr != nil || err != nil {
			if err != tcase.expectedErr {
				t.Errorf("expected get error %q in test case %q, got %q", tcase.expectedErr, i, err)
			}
		} else if tcase.expectedID != id {
			t.Errorf("expected to create ID %d, got %d in test case %d", tcase.expectedID, id, i)
		}
	}
}

// TestWatcherWatchPrefix tests if Watch operation correctly watches
// and returns events with matching prefixes.
func TestWatcherWatchPrefix(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	idm := make(map[WatchID]struct{})

	val := []byte("bar")
	keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar")

	for i := 0; i < 10; i++ {
		id, _ := w.Watch(0, keyWatch, keyEnd, 0)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: unexpected duplicated id %x", i, id)
		}
		idm[id] = struct{}{}

		s.Put(keyPut, val, lease.NoLease)

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Errorf("#%d: unexpected cancel error %v", i, err)
		}

		if len(resp.Events) != 1 {
			t.Errorf("#%d: len(resp.Events) got = %d, want = 1", i, len(resp.Events))
		}
		if len(resp.Events) == 1 {
			if !bytes.Equal(resp.Events[0].Kv.Key, keyPut) {
				t.Errorf("#%d: resp.Events got = %s, want = %s", i, resp.Events[0].Kv.Key, keyPut)
			}
		}
	}

	keyWatch1, keyEnd1, keyPut1 := []byte("foo1"), []byte("foo2"), []byte("foo1bar")
	s.Put(keyPut1, val, lease.NoLease)

	// unsynced watchers
	for i := 10; i < 15; i++ {
		id, _ := w.Watch(0, keyWatch1, keyEnd1, 1)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: id %d exists", i, id)
		}
		idm[id] = struct{}{}

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Error(err)
		}

		if len(resp.Events) != 1 {
			t.Errorf("#%d: len(resp.Events) got = %d, want = 1", i, len(resp.Events))
		}
		if len(resp.Events) == 1 {
			if !bytes.Equal(resp.Events[0].Kv.Key, keyPut1) {
				t.Errorf("#%d: resp.Events got = %s, want = %s", i, resp.Events[0].Kv.Key, keyPut1)
			}
		}
	}
}

// TestWatcherWatchWrongRange ensures that watcher with wrong 'end' range
// does not create watcher, which panics when canceling in range tree.
func TestWatcherWatchWrongRange(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	if _, err := w.Watch(0, []byte("foa"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
		t.Fatalf("key == end range given; expected ErrEmptyWatcherRange, got %+v", err)
	}
	if _, err := w.Watch(0, []byte("fob"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
		t.Fatalf("key > end range given; expected ErrEmptyWatcherRange, got %+v", err)
	}
	// watch request with 'WithFromKey' has empty-byte range end
	if id, _ := w.Watch(0, []byte("foo"), []byte{}, 1); id != 0 {
		t.Fatalf("\x00 is range given; id expected 0, got %d", id)
	}
}

func TestWatchDeleteRange(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil)

	defer func() {
		s.store.Close()
		os.Remove(tmpPath)
	}()

	testKeyPrefix := []byte("foo")

	for i := 0; i < 3; i++ {
		s.Put([]byte(fmt.Sprintf("%s_%d", testKeyPrefix, i)), []byte("bar"), lease.NoLease)
	}

	w := s.NewWatchStream()
	from, to := []byte(testKeyPrefix), []byte(fmt.Sprintf("%s_%d", testKeyPrefix, 99))
	w.Watch(0, from, to, 0)

	s.DeleteRange(from, to)

	we := []mvccpb.Event{
		{Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_0"), ModRevision: 5}},
		{Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_1"), ModRevision: 5}},
		{Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_2"), ModRevision: 5}},
	}

	select {
	case r := <-w.Chan():
		if !reflect.DeepEqual(r.Events, we) {
			t.Errorf("event = %v, want %v", r.Events, we)
		}
	case <-time.After(10 * time.Second):
		t.Fatal("failed to receive event after 10 seconds!")
	}
}

// TestWatchStreamCancelWatcherByID ensures cancel calls the cancel func of the watcher
// with given id inside watchStream.
func TestWatchStreamCancelWatcherByID(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	id, _ := w.Watch(0, []byte("foo"), nil, 0)

	tests := []struct {
		cancelID WatchID
		werr     error
	}{
		// no error should be returned when cancel the created watcher.
		{id, nil},
		// not exist error should be returned when cancel again.
		{id, ErrWatcherNotExist},
		// not exist error should be returned when cancel a bad id.
		{id + 1, ErrWatcherNotExist},
	}

	for i, tt := range tests {
		gerr := w.Cancel(tt.cancelID)

		if gerr != tt.werr {
			t.Errorf("#%d: err = %v, want %v", i, gerr, tt.werr)
		}
	}

	if l := len(w.(*watchStream).cancels); l != 0 {
		t.Errorf("cancels = %d, want 0", l)
	}
}

// TestWatcherRequestProgress ensures synced watcher can correctly
// report its correct progress.
func TestWatcherRequestProgress(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()

	// manually create watchableStore instead of newWatchableStore
	// because newWatchableStore automatically calls syncWatchers
	// method to sync watchers in unsynced map. We want to keep watchers
	// in unsynced to test if syncWatchers works as expected.
	s := &watchableStore{
		store:    NewStore(zap.NewExample(), b, &lease.FakeLessor{}, nil),
		unsynced: newWatcherGroup(),
		synced:   newWatcherGroup(),
	}

	defer func() {
		s.store.Close()
		os.Remove(tmpPath)
	}()

	testKey := []byte("foo")
	notTestKey := []byte("bad")
	testValue := []byte("bar")
	s.Put(testKey, testValue, lease.NoLease)

	w := s.NewWatchStream()

	badID := WatchID(1000)
	w.RequestProgress(badID)
	select {
	case resp := <-w.Chan():
		t.Fatalf("unexpected %+v", resp)
	default:
	}

	id, _ := w.Watch(0, notTestKey, nil, 1)
	w.RequestProgress(id)
	select {
	case resp := <-w.Chan():
		t.Fatalf("unexpected %+v", resp)
	default:
	}

	s.syncWatchers()

	w.RequestProgress(id)
	wrs := WatchResponse{WatchID: id, Revision: 2}
	select {
	case resp := <-w.Chan():
		if !reflect.DeepEqual(resp, wrs) {
			t.Fatalf("got %+v, expect %+v", resp, wrs)
		}
	case <-time.After(time.Second):
		t.Fatal("failed to receive progress")
	}
}

func TestWatcherWatchWithFilter(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	filterPut := func(e mvccpb.Event) bool {
		return e.Type == mvccpb.PUT
	}

	w.Watch(0, []byte("foo"), nil, 0, filterPut)
	done := make(chan struct{})

	go func() {
		<-w.Chan()
		done <- struct{}{}
	}()

	s.Put([]byte("foo"), []byte("bar"), 0)

	select {
	case <-done:
		t.Fatal("failed to filter put request")
	case <-time.After(100 * time.Millisecond):
	}

	s.DeleteRange([]byte("foo"), nil)

	select {
	case <-done:
	case <-time.After(100 * time.Millisecond):
		t.Fatal("failed to receive delete request")
	}
}
<commit_msg>mvcc: remove unnecessary type conversion<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 mvcc

import (
	"bytes"
	"fmt"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/coreos/etcd/lease"
	"github.com/coreos/etcd/mvcc/backend"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"go.uber.org/zap"
)

// TestWatcherWatchID tests that each watcher provides unique watchID,
// and the watched event attaches the correct watchID.
func TestWatcherWatchID(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	idm := make(map[WatchID]struct{})

	for i := 0; i < 10; i++ {
		id, _ := w.Watch(0, []byte("foo"), nil, 0)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: id %d exists", i, id)
		}
		idm[id] = struct{}{}

		s.Put([]byte("foo"), []byte("bar"), lease.NoLease)

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Error(err)
		}
	}

	s.Put([]byte("foo2"), []byte("bar"), lease.NoLease)

	// unsynced watchers
	for i := 10; i < 20; i++ {
		id, _ := w.Watch(0, []byte("foo2"), nil, 1)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: id %d exists", i, id)
		}
		idm[id] = struct{}{}

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Error(err)
		}
	}
}

func TestWatcherRequestsCustomID(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	// - Request specifically ID #1
	// - Try to duplicate it, get an error
	// - Make sure the auto-assignment skips over things we manually assigned

	tt := []struct {
		givenID     WatchID
		expectedID  WatchID
		expectedErr error
	}{
		{1, 1, nil},
		{1, 0, ErrWatcherDuplicateID},
		{0, 0, nil},
		{0, 2, nil},
	}

	for i, tcase := range tt {
		id, err := w.Watch(tcase.givenID, []byte("foo"), nil, 0)
		if tcase.expectedErr != nil || err != nil {
			if err != tcase.expectedErr {
				t.Errorf("expected get error %q in test case %q, got %q", tcase.expectedErr, i, err)
			}
		} else if tcase.expectedID != id {
			t.Errorf("expected to create ID %d, got %d in test case %d", tcase.expectedID, id, i)
		}
	}
}

// TestWatcherWatchPrefix tests if Watch operation correctly watches
// and returns events with matching prefixes.
func TestWatcherWatchPrefix(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	idm := make(map[WatchID]struct{})

	val := []byte("bar")
	keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar")

	for i := 0; i < 10; i++ {
		id, _ := w.Watch(0, keyWatch, keyEnd, 0)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: unexpected duplicated id %x", i, id)
		}
		idm[id] = struct{}{}

		s.Put(keyPut, val, lease.NoLease)

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Errorf("#%d: unexpected cancel error %v", i, err)
		}

		if len(resp.Events) != 1 {
			t.Errorf("#%d: len(resp.Events) got = %d, want = 1", i, len(resp.Events))
		}
		if len(resp.Events) == 1 {
			if !bytes.Equal(resp.Events[0].Kv.Key, keyPut) {
				t.Errorf("#%d: resp.Events got = %s, want = %s", i, resp.Events[0].Kv.Key, keyPut)
			}
		}
	}

	keyWatch1, keyEnd1, keyPut1 := []byte("foo1"), []byte("foo2"), []byte("foo1bar")
	s.Put(keyPut1, val, lease.NoLease)

	// unsynced watchers
	for i := 10; i < 15; i++ {
		id, _ := w.Watch(0, keyWatch1, keyEnd1, 1)
		if _, ok := idm[id]; ok {
			t.Errorf("#%d: id %d exists", i, id)
		}
		idm[id] = struct{}{}

		resp := <-w.Chan()
		if resp.WatchID != id {
			t.Errorf("#%d: watch id in event = %d, want %d", i, resp.WatchID, id)
		}

		if err := w.Cancel(id); err != nil {
			t.Error(err)
		}

		if len(resp.Events) != 1 {
			t.Errorf("#%d: len(resp.Events) got = %d, want = 1", i, len(resp.Events))
		}
		if len(resp.Events) == 1 {
			if !bytes.Equal(resp.Events[0].Kv.Key, keyPut1) {
				t.Errorf("#%d: resp.Events got = %s, want = %s", i, resp.Events[0].Kv.Key, keyPut1)
			}
		}
	}
}

// TestWatcherWatchWrongRange ensures that watcher with wrong 'end' range
// does not create watcher, which panics when canceling in range tree.
func TestWatcherWatchWrongRange(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	if _, err := w.Watch(0, []byte("foa"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
		t.Fatalf("key == end range given; expected ErrEmptyWatcherRange, got %+v", err)
	}
	if _, err := w.Watch(0, []byte("fob"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
		t.Fatalf("key > end range given; expected ErrEmptyWatcherRange, got %+v", err)
	}
	// watch request with 'WithFromKey' has empty-byte range end
	if id, _ := w.Watch(0, []byte("foo"), []byte{}, 1); id != 0 {
		t.Fatalf("\x00 is range given; id expected 0, got %d", id)
	}
}

func TestWatchDeleteRange(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil)

	defer func() {
		s.store.Close()
		os.Remove(tmpPath)
	}()

	testKeyPrefix := []byte("foo")

	for i := 0; i < 3; i++ {
		s.Put([]byte(fmt.Sprintf("%s_%d", testKeyPrefix, i)), []byte("bar"), lease.NoLease)
	}

	w := s.NewWatchStream()
	from, to := testKeyPrefix, []byte(fmt.Sprintf("%s_%d", testKeyPrefix, 99))
	w.Watch(0, from, to, 0)

	s.DeleteRange(from, to)

	we := []mvccpb.Event{
		{Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_0"), ModRevision: 5}},
		{Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_1"), ModRevision: 5}},
		{Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_2"), ModRevision: 5}},
	}

	select {
	case r := <-w.Chan():
		if !reflect.DeepEqual(r.Events, we) {
			t.Errorf("event = %v, want %v", r.Events, we)
		}
	case <-time.After(10 * time.Second):
		t.Fatal("failed to receive event after 10 seconds!")
	}
}

// TestWatchStreamCancelWatcherByID ensures cancel calls the cancel func of the watcher
// with given id inside watchStream.
func TestWatchStreamCancelWatcherByID(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	id, _ := w.Watch(0, []byte("foo"), nil, 0)

	tests := []struct {
		cancelID WatchID
		werr     error
	}{
		// no error should be returned when cancel the created watcher.
		{id, nil},
		// not exist error should be returned when cancel again.
		{id, ErrWatcherNotExist},
		// not exist error should be returned when cancel a bad id.
		{id + 1, ErrWatcherNotExist},
	}

	for i, tt := range tests {
		gerr := w.Cancel(tt.cancelID)

		if gerr != tt.werr {
			t.Errorf("#%d: err = %v, want %v", i, gerr, tt.werr)
		}
	}

	if l := len(w.(*watchStream).cancels); l != 0 {
		t.Errorf("cancels = %d, want 0", l)
	}
}

// TestWatcherRequestProgress ensures synced watcher can correctly
// report its correct progress.
func TestWatcherRequestProgress(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()

	// manually create watchableStore instead of newWatchableStore
	// because newWatchableStore automatically calls syncWatchers
	// method to sync watchers in unsynced map. We want to keep watchers
	// in unsynced to test if syncWatchers works as expected.
	s := &watchableStore{
		store:    NewStore(zap.NewExample(), b, &lease.FakeLessor{}, nil),
		unsynced: newWatcherGroup(),
		synced:   newWatcherGroup(),
	}

	defer func() {
		s.store.Close()
		os.Remove(tmpPath)
	}()

	testKey := []byte("foo")
	notTestKey := []byte("bad")
	testValue := []byte("bar")
	s.Put(testKey, testValue, lease.NoLease)

	w := s.NewWatchStream()

	badID := WatchID(1000)
	w.RequestProgress(badID)
	select {
	case resp := <-w.Chan():
		t.Fatalf("unexpected %+v", resp)
	default:
	}

	id, _ := w.Watch(0, notTestKey, nil, 1)
	w.RequestProgress(id)
	select {
	case resp := <-w.Chan():
		t.Fatalf("unexpected %+v", resp)
	default:
	}

	s.syncWatchers()

	w.RequestProgress(id)
	wrs := WatchResponse{WatchID: id, Revision: 2}
	select {
	case resp := <-w.Chan():
		if !reflect.DeepEqual(resp, wrs) {
			t.Fatalf("got %+v, expect %+v", resp, wrs)
		}
	case <-time.After(time.Second):
		t.Fatal("failed to receive progress")
	}
}

func TestWatcherWatchWithFilter(t *testing.T) {
	b, tmpPath := backend.NewDefaultTmpBackend()
	s := WatchableKV(newWatchableStore(zap.NewExample(), b, &lease.FakeLessor{}, nil))
	defer cleanup(s, b, tmpPath)

	w := s.NewWatchStream()
	defer w.Close()

	filterPut := func(e mvccpb.Event) bool {
		return e.Type == mvccpb.PUT
	}

	w.Watch(0, []byte("foo"), nil, 0, filterPut)
	done := make(chan struct{})

	go func() {
		<-w.Chan()
		done <- struct{}{}
	}()

	s.Put([]byte("foo"), []byte("bar"), 0)

	select {
	case <-done:
		t.Fatal("failed to filter put request")
	case <-time.After(100 * time.Millisecond):
	}

	s.DeleteRange([]byte("foo"), nil)

	select {
	case <-done:
	case <-time.After(100 * time.Millisecond):
		t.Fatal("failed to receive delete request")
	}
}
<|endoftext|>


<commit_before>// +build !windows

package main

import (
	"encoding/json"
	"strings"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/docker/go-units"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) {
	testRequires(c, cpuCfsQuota)
	name := "testbuildresourceconstraints"

	ctx, err := fakeContext(`
	FROM hello-world:frozen
	RUN ["/hello"]
	`, map[string]string{})
	c.Assert(err, checker.IsNil)

	dockerCmdInDir(c, ctx.Dir, "build", "--no-cache", "--rm=false", "--memory=64m", "--memory-swap=-1", "--cpuset-cpus=0", "--cpuset-mems=0", "--cpu-shares=100", "--cpu-quota=8000", "--ulimit", "nofile=42", "-t", name, ".")

	out, _ := dockerCmd(c, "ps", "-lq")
	cID := strings.TrimSpace(out)

	type hostConfig struct {
		Memory     int64
		MemorySwap int64
		CpusetCpus string
		CpusetMems string
		CPUShares  int64
		CPUQuota   int64
		Ulimits    []*units.Ulimit
	}

	cfg, err := inspectFieldJSON(cID, "HostConfig")
	c.Assert(err, checker.IsNil)

	var c1 hostConfig
	err = json.Unmarshal([]byte(cfg), &c1)
	c.Assert(err, checker.IsNil, check.Commentf(cfg))

	c.Assert(c1.Memory, checker.Equals, int64(64*1024*1024), check.Commentf("resource constraints not set properly for Memory"))
	c.Assert(c1.MemorySwap, checker.Equals, int64(-1), check.Commentf("resource constraints not set properly for MemorySwap"))
	c.Assert(c1.CpusetCpus, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetCpus"))
	c.Assert(c1.CpusetMems, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetMems"))
	c.Assert(c1.CPUShares, checker.Equals, int64(100), check.Commentf("resource constraints not set properly for CPUShares"))
	c.Assert(c1.CPUQuota, checker.Equals, int64(8000), check.Commentf("resource constraints not set properly for CPUQuota"))
	c.Assert(c1.Ulimits[0].Name, checker.Equals, "nofile", check.Commentf("resource constraints not set properly for Ulimits"))
	c.Assert(c1.Ulimits[0].Hard, checker.Equals, int64(42), check.Commentf("resource constraints not set properly for Ulimits"))

	// Make sure constraints aren't saved to image
	dockerCmd(c, "run", "--name=test", name)

	cfg, err = inspectFieldJSON("test", "HostConfig")
	c.Assert(err, checker.IsNil)

	var c2 hostConfig
	err = json.Unmarshal([]byte(cfg), &c2)
	c.Assert(err, checker.IsNil, check.Commentf(cfg))

	c.Assert(c2.Memory, check.Not(checker.Equals), int64(64*1024*1024), check.Commentf("resource leaked from build for Memory"))
	c.Assert(c2.MemorySwap, check.Not(checker.Equals), int64(-1), check.Commentf("resource leaked from build for MemorySwap"))
	c.Assert(c2.CpusetCpus, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetCpus"))
	c.Assert(c2.CpusetMems, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetMems"))
	c.Assert(c2.CPUShares, check.Not(checker.Equals), int64(100), check.Commentf("resource leaked from build for CPUShares"))
	c.Assert(c2.CPUQuota, check.Not(checker.Equals), int64(8000), check.Commentf("resource leaked from build for CPUQuota"))
	c.Assert(c2.Ulimits, checker.IsNil, check.Commentf("resource leaked from build for Ulimits"))
}
<commit_msg>Handle error for dockerCmdInDir<commit_after>// +build !windows

package main

import (
	"encoding/json"
	"strings"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/docker/go-units"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) {
	testRequires(c, cpuCfsQuota)
	name := "testbuildresourceconstraints"

	ctx, err := fakeContext(`
	FROM hello-world:frozen
	RUN ["/hello"]
	`, map[string]string{})
	c.Assert(err, checker.IsNil)

	_, _, err = dockerCmdInDir(c, ctx.Dir, "build", "--no-cache", "--rm=false", "--memory=64m", "--memory-swap=-1", "--cpuset-cpus=0", "--cpuset-mems=0", "--cpu-shares=100", "--cpu-quota=8000", "--ulimit", "nofile=42", "-t", name, ".")
	if err != nil {
		c.Fatal(err)
	}

	out, _ := dockerCmd(c, "ps", "-lq")
	cID := strings.TrimSpace(out)

	type hostConfig struct {
		Memory     int64
		MemorySwap int64
		CpusetCpus string
		CpusetMems string
		CPUShares  int64
		CPUQuota   int64
		Ulimits    []*units.Ulimit
	}

	cfg, err := inspectFieldJSON(cID, "HostConfig")
	c.Assert(err, checker.IsNil)

	var c1 hostConfig
	err = json.Unmarshal([]byte(cfg), &c1)
	c.Assert(err, checker.IsNil, check.Commentf(cfg))

	c.Assert(c1.Memory, checker.Equals, int64(64*1024*1024), check.Commentf("resource constraints not set properly for Memory"))
	c.Assert(c1.MemorySwap, checker.Equals, int64(-1), check.Commentf("resource constraints not set properly for MemorySwap"))
	c.Assert(c1.CpusetCpus, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetCpus"))
	c.Assert(c1.CpusetMems, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetMems"))
	c.Assert(c1.CPUShares, checker.Equals, int64(100), check.Commentf("resource constraints not set properly for CPUShares"))
	c.Assert(c1.CPUQuota, checker.Equals, int64(8000), check.Commentf("resource constraints not set properly for CPUQuota"))
	c.Assert(c1.Ulimits[0].Name, checker.Equals, "nofile", check.Commentf("resource constraints not set properly for Ulimits"))
	c.Assert(c1.Ulimits[0].Hard, checker.Equals, int64(42), check.Commentf("resource constraints not set properly for Ulimits"))

	// Make sure constraints aren't saved to image
	dockerCmd(c, "run", "--name=test", name)

	cfg, err = inspectFieldJSON("test", "HostConfig")
	c.Assert(err, checker.IsNil)

	var c2 hostConfig
	err = json.Unmarshal([]byte(cfg), &c2)
	c.Assert(err, checker.IsNil, check.Commentf(cfg))

	c.Assert(c2.Memory, check.Not(checker.Equals), int64(64*1024*1024), check.Commentf("resource leaked from build for Memory"))
	c.Assert(c2.MemorySwap, check.Not(checker.Equals), int64(-1), check.Commentf("resource leaked from build for MemorySwap"))
	c.Assert(c2.CpusetCpus, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetCpus"))
	c.Assert(c2.CpusetMems, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetMems"))
	c.Assert(c2.CPUShares, check.Not(checker.Equals), int64(100), check.Commentf("resource leaked from build for CPUShares"))
	c.Assert(c2.CPUQuota, check.Not(checker.Equals), int64(8000), check.Commentf("resource leaked from build for CPUQuota"))
	c.Assert(c2.Ulimits, checker.IsNil, check.Commentf("resource leaked from build for Ulimits"))
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016, 2017 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"sync"
	"sync/atomic"

	"gopkg.in/btrdb.v4"

	ws "github.com/gorilla/websocket"
	uuid "github.com/pborman/uuid"
)

const (
	QUASAR_LOW   int64 = 1 - (16 << 56)
	QUASAR_HIGH  int64 = (48 << 56) - 1
	INVALID_TIME int64 = -0x8000000000000000
)

func splitTime(time int64) (millis int64, nanos int32) {
	millis = time / 1000000
	nanos = int32(time % 1000000)
	if nanos < 0 {
		nanos += 1000000
		millis++
	}
	return
}

type Writable interface {
	GetWriter() io.Writer
}

type ConnWrapper struct {
	Writing    *sync.Mutex
	Conn       *ws.Conn
	CurrWriter io.WriteCloser
}

func (cw *ConnWrapper) GetWriter() io.Writer {
	cw.Writing.Lock()
	w, err := cw.Conn.NextWriter(ws.TextMessage)
	if err == nil {
		cw.CurrWriter = w
		return w
	} else {
		log.Printf("Could not get writer on WebSocket: %v", err)
		return nil
	}
}

// DataRequester encapsulates a series of connections used for obtaining data
// from QUASAR.
type DataRequester struct {
	totalWaiting   uint64
	pending        uint32
	maxPending     uint32
	pendingLock    *sync.Mutex
	pendingCondVar *sync.Cond
	stateLock      *sync.RWMutex
	alive          bool

	btrdb *btrdb.BTrDB
}

// NewDataRequester creates a new DataRequester object.
// btrdbConn - established connection to a BTrDB cluster.
// maxPending - a limit on the maximum number of pending requests. */
func NewDataRequester(btrdbConn *btrdb.BTrDB, maxPending uint32) *DataRequester {
	pendingLock := &sync.Mutex{}
	var dr *DataRequester = &DataRequester{
		totalWaiting:   0,
		pending:        0,
		maxPending:     maxPending,
		pendingLock:    pendingLock,
		pendingCondVar: sync.NewCond(pendingLock),
		btrdb:          btrdbConn,
	}

	return dr
}

/* Makes a request for data and writes the result to the specified Writer. */
func (dr *DataRequester) MakeDataRequest(ctx context.Context, uuidBytes uuid.UUID, startTime int64, endTime int64, pw uint8, writ Writable) {
	atomic.AddUint64(&dr.totalWaiting, 1)
	defer atomic.AddUint64(&dr.totalWaiting, 0xFFFFFFFFFFFFFFFF)

	dr.pendingLock.Lock()
	for dr.pending == dr.maxPending {
		dr.pendingCondVar.Wait()
	}
	dr.pending += 1
	dr.pendingLock.Unlock()

	defer func() {
		dr.pendingLock.Lock()
		dr.pending -= 1
		dr.pendingCondVar.Signal()
		dr.pendingLock.Unlock()
	}()

	var w io.Writer

	var stream = dr.btrdb.StreamFromUUID(uuidBytes)

	var exists bool
	var err error
	exists, err = stream.Exists(context.TODO())
	if err != nil || !exists {
		w = writ.GetWriter()
		w.Write([]byte("[]"))
		return
	}

	var results chan btrdb.StatPoint
	var errors chan error
	results, _, errors = stream.AlignedWindows(ctx, startTime, endTime, pw, 0)

	w = writ.GetWriter()
	w.Write([]byte("["))

	var firstpt bool = true
	for statpt := range results {
		millis, nanos := splitTime(statpt.Time)
		if firstpt {
			w.Write([]byte(fmt.Sprintf("[%v,%v,%v,%v,%v,%v]", millis, nanos, statpt.Min, statpt.Mean, statpt.Max, statpt.Count)))
			firstpt = false
		} else {
			w.Write([]byte(fmt.Sprintf(",[%v,%v,%v,%v,%v,%v]", millis, nanos, statpt.Min, statpt.Mean, statpt.Max, statpt.Count)))
		}
	}

	var waserror bool = false
	for err = range errors {
		w.Write([]byte("\nError: "))
		w.Write([]byte(err.Error()))
		waserror = true
	}

	if !waserror {
		w.Write([]byte("]"))
	}
}

func (dr *DataRequester) MakeBracketRequest(ctx context.Context, uuids []uuid.UUID, writ Writable) {
	atomic.AddUint64(&dr.totalWaiting, 1)
	defer atomic.AddUint64(&dr.totalWaiting, 0xFFFFFFFFFFFFFFFF)

	dr.pendingLock.Lock()
	for dr.pending == dr.maxPending {
		dr.pendingCondVar.Wait()
	}
	dr.pending += 1
	dr.pendingLock.Unlock()

	defer func() {
		dr.pendingLock.Lock()
		dr.pending -= 1
		dr.pendingCondVar.Signal()
		dr.pendingLock.Unlock()
	}()

	var numResponses int = len(uuids) << 1
	var boundarySlice []int64 = make([]int64, numResponses)

	var wg sync.WaitGroup

	var stream *btrdb.Stream

	var i int
	for i = 0; i != numResponses; i++ {
		var seconditer bool = ((i & 1) != 0)

		if !seconditer {
			stream = dr.btrdb.StreamFromUUID(uuids[i>>1])

			var exists bool
			var err error
			exists, err = stream.Exists(ctx)
			if err != nil || !exists {
				boundarySlice[i] = INVALID_TIME
				i++
				boundarySlice[i] = INVALID_TIME
				continue
			} else {
				wg.Add(2)
			}
		}

		go func(stream *btrdb.Stream, loc *int64, high bool) {
			var rawpoint btrdb.RawPoint
			var e error
			var ref int64
			if high {
				ref = QUASAR_HIGH
			} else {
				ref = QUASAR_LOW
			}
			rawpoint, _, e = stream.Nearest(ctx, ref, 0, high)

			if e == nil {
				*loc = rawpoint.Time
			} else {
				*loc = INVALID_TIME
			}

			wg.Done()
		}(stream, &boundarySlice[i], seconditer)
	}

	wg.Wait()

	// For final processing once all responses are received
	var (
		boundary  int64
		lNanos    int32
		lMillis   int64
		rNanos    int32
		rMillis   int64
		lowest    int64     = QUASAR_HIGH
		highest   int64     = QUASAR_LOW
		trailchar rune      = ','
		w         io.Writer = writ.GetWriter()
	)

	w.Write([]byte("{\"Brackets\": ["))

	for i = 0; i < len(uuids); i++ {
		boundary = boundarySlice[i<<1]
		if boundary != INVALID_TIME && boundary < lowest {
			lowest = boundary
		}
		lMillis, lNanos = splitTime(boundary)
		boundary = boundarySlice[(i<<1)+1]
		if boundary != INVALID_TIME && boundary > highest {
			highest = boundary
		}
		rMillis, rNanos = splitTime(boundary)
		if i == len(uuids)-1 {
			trailchar = ']'
		}
		w.Write([]byte(fmt.Sprintf("[[%v,%v],[%v,%v]]%c", lMillis, lNanos, rMillis, rNanos, trailchar)))
	}
	if len(uuids) == 0 {
		w.Write([]byte("]"))
	}
	lMillis, lNanos = splitTime(lowest)
	rMillis, rNanos = splitTime(highest)
	w.Write([]byte(fmt.Sprintf(",\"Merged\":[[%v,%v],[%v,%v]]}", lMillis, lNanos, rMillis, rNanos)))
}
<commit_msg>Use real context when checking if stream exists<commit_after>/*
 * Copyright (C) 2016, 2017 Sam Kumar, Michael Andersen, and the University
 * of California, Berkeley.
 *
 * This file is part of Mr. Plotter (the Multi-Resolution Plotter).
 *
 * Mr. Plotter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Mr. Plotter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Mr. Plotter.  If not, see <http://www.gnu.org/licenses/>.
 */

package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"sync"
	"sync/atomic"

	"gopkg.in/btrdb.v4"

	ws "github.com/gorilla/websocket"
	uuid "github.com/pborman/uuid"
)

const (
	QUASAR_LOW   int64 = 1 - (16 << 56)
	QUASAR_HIGH  int64 = (48 << 56) - 1
	INVALID_TIME int64 = -0x8000000000000000
)

func splitTime(time int64) (millis int64, nanos int32) {
	millis = time / 1000000
	nanos = int32(time % 1000000)
	if nanos < 0 {
		nanos += 1000000
		millis++
	}
	return
}

type Writable interface {
	GetWriter() io.Writer
}

type ConnWrapper struct {
	Writing    *sync.Mutex
	Conn       *ws.Conn
	CurrWriter io.WriteCloser
}

func (cw *ConnWrapper) GetWriter() io.Writer {
	cw.Writing.Lock()
	w, err := cw.Conn.NextWriter(ws.TextMessage)
	if err == nil {
		cw.CurrWriter = w
		return w
	} else {
		log.Printf("Could not get writer on WebSocket: %v", err)
		return nil
	}
}

// DataRequester encapsulates a series of connections used for obtaining data
// from QUASAR.
type DataRequester struct {
	totalWaiting   uint64
	pending        uint32
	maxPending     uint32
	pendingLock    *sync.Mutex
	pendingCondVar *sync.Cond
	stateLock      *sync.RWMutex
	alive          bool

	btrdb *btrdb.BTrDB
}

// NewDataRequester creates a new DataRequester object.
// btrdbConn - established connection to a BTrDB cluster.
// maxPending - a limit on the maximum number of pending requests. */
func NewDataRequester(btrdbConn *btrdb.BTrDB, maxPending uint32) *DataRequester {
	pendingLock := &sync.Mutex{}
	var dr *DataRequester = &DataRequester{
		totalWaiting:   0,
		pending:        0,
		maxPending:     maxPending,
		pendingLock:    pendingLock,
		pendingCondVar: sync.NewCond(pendingLock),
		btrdb:          btrdbConn,
	}

	return dr
}

/* Makes a request for data and writes the result to the specified Writer. */
func (dr *DataRequester) MakeDataRequest(ctx context.Context, uuidBytes uuid.UUID, startTime int64, endTime int64, pw uint8, writ Writable) {
	atomic.AddUint64(&dr.totalWaiting, 1)
	defer atomic.AddUint64(&dr.totalWaiting, 0xFFFFFFFFFFFFFFFF)

	dr.pendingLock.Lock()
	for dr.pending == dr.maxPending {
		dr.pendingCondVar.Wait()
	}
	dr.pending += 1
	dr.pendingLock.Unlock()

	defer func() {
		dr.pendingLock.Lock()
		dr.pending -= 1
		dr.pendingCondVar.Signal()
		dr.pendingLock.Unlock()
	}()

	var w io.Writer

	var stream = dr.btrdb.StreamFromUUID(uuidBytes)

	var exists bool
	var err error
	exists, err = stream.Exists(ctx)
	if err != nil || !exists {
		w = writ.GetWriter()
		w.Write([]byte("[]"))
		return
	}

	var results chan btrdb.StatPoint
	var errors chan error
	results, _, errors = stream.AlignedWindows(ctx, startTime, endTime, pw, 0)

	w = writ.GetWriter()
	w.Write([]byte("["))

	var firstpt bool = true
	for statpt := range results {
		millis, nanos := splitTime(statpt.Time)
		if firstpt {
			w.Write([]byte(fmt.Sprintf("[%v,%v,%v,%v,%v,%v]", millis, nanos, statpt.Min, statpt.Mean, statpt.Max, statpt.Count)))
			firstpt = false
		} else {
			w.Write([]byte(fmt.Sprintf(",[%v,%v,%v,%v,%v,%v]", millis, nanos, statpt.Min, statpt.Mean, statpt.Max, statpt.Count)))
		}
	}

	var waserror bool = false
	for err = range errors {
		w.Write([]byte("\nError: "))
		w.Write([]byte(err.Error()))
		waserror = true
	}

	if !waserror {
		w.Write([]byte("]"))
	}
}

func (dr *DataRequester) MakeBracketRequest(ctx context.Context, uuids []uuid.UUID, writ Writable) {
	atomic.AddUint64(&dr.totalWaiting, 1)
	defer atomic.AddUint64(&dr.totalWaiting, 0xFFFFFFFFFFFFFFFF)

	dr.pendingLock.Lock()
	for dr.pending == dr.maxPending {
		dr.pendingCondVar.Wait()
	}
	dr.pending += 1
	dr.pendingLock.Unlock()

	defer func() {
		dr.pendingLock.Lock()
		dr.pending -= 1
		dr.pendingCondVar.Signal()
		dr.pendingLock.Unlock()
	}()

	var numResponses int = len(uuids) << 1
	var boundarySlice []int64 = make([]int64, numResponses)

	var wg sync.WaitGroup

	var stream *btrdb.Stream

	var i int
	for i = 0; i != numResponses; i++ {
		var seconditer bool = ((i & 1) != 0)

		if !seconditer {
			stream = dr.btrdb.StreamFromUUID(uuids[i>>1])

			var exists bool
			var err error
			exists, err = stream.Exists(ctx)
			if err != nil || !exists {
				boundarySlice[i] = INVALID_TIME
				i++
				boundarySlice[i] = INVALID_TIME
				continue
			} else {
				wg.Add(2)
			}
		}

		go func(stream *btrdb.Stream, loc *int64, high bool) {
			var rawpoint btrdb.RawPoint
			var e error
			var ref int64
			if high {
				ref = QUASAR_HIGH
			} else {
				ref = QUASAR_LOW
			}
			rawpoint, _, e = stream.Nearest(ctx, ref, 0, high)

			if e == nil {
				*loc = rawpoint.Time
			} else {
				*loc = INVALID_TIME
			}

			wg.Done()
		}(stream, &boundarySlice[i], seconditer)
	}

	wg.Wait()

	// For final processing once all responses are received
	var (
		boundary  int64
		lNanos    int32
		lMillis   int64
		rNanos    int32
		rMillis   int64
		lowest    int64     = QUASAR_HIGH
		highest   int64     = QUASAR_LOW
		trailchar rune      = ','
		w         io.Writer = writ.GetWriter()
	)

	w.Write([]byte("{\"Brackets\": ["))

	for i = 0; i < len(uuids); i++ {
		boundary = boundarySlice[i<<1]
		if boundary != INVALID_TIME && boundary < lowest {
			lowest = boundary
		}
		lMillis, lNanos = splitTime(boundary)
		boundary = boundarySlice[(i<<1)+1]
		if boundary != INVALID_TIME && boundary > highest {
			highest = boundary
		}
		rMillis, rNanos = splitTime(boundary)
		if i == len(uuids)-1 {
			trailchar = ']'
		}
		w.Write([]byte(fmt.Sprintf("[[%v,%v],[%v,%v]]%c", lMillis, lNanos, rMillis, rNanos, trailchar)))
	}
	if len(uuids) == 0 {
		w.Write([]byte("]"))
	}
	lMillis, lNanos = splitTime(lowest)
	rMillis, rNanos = splitTime(highest)
	w.Write([]byte(fmt.Sprintf(",\"Merged\":[[%v,%v],[%v,%v]]}", lMillis, lNanos, rMillis, rNanos)))
}
<|endoftext|>


<commit_before>// +build linux

package namespaces

import (
	"encoding/json"
	"os"
	"strconv"

	"github.com/docker/libcontainer"
	"github.com/docker/libcontainer/label"
	"github.com/docker/libcontainer/system"
)

// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
	// TODO(vmarmol): If this gets too long, send it over a pipe to the child.
	// Marshall the container into JSON since it won't be available in the namespace.
	containerJson, err := json.Marshal(container)
	if err != nil {
		return err
	}

	// Enter the namespace and then finish setup
	finalArgs := []string{os.Args[0], "nsenter", "--nspid", strconv.Itoa(state.InitPid), "--containerjson", string(containerJson), "--"}
	finalArgs = append(finalArgs, args...)
	if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil {
		return err
	}
	panic("unreachable")
}

// Run a command in a container after entering the namespace.
func NsEnter(container *libcontainer.Config, args []string) error {
	// clear the current processes env and replace it with the environment
	// defined on the container
	if err := LoadContainerEnvironment(container); err != nil {
		return err
	}
	if err := FinalizeNamespace(container); err != nil {
		return err
	}

	if container.ProcessLabel != "" {
		if err := label.SetProcessLabel(container.ProcessLabel); err != nil {
			return err
		}
	}

	if err := system.Execv(args[0], args[0:], container.Env); err != nil {
		return err
	}
	panic("unreachable")
}
<commit_msg>Adding RunIn to run a user specified command in an existing container, wait for it to exit and return the exit code. RunIn will connect to a user specified Terminal before running the command.<commit_after>// +build linux

package namespaces

import (
	"encoding/json"
	"os"
	"os/exec"
	"strconv"
	"syscall"

	"github.com/docker/libcontainer"
	"github.com/docker/libcontainer/label"
	"github.com/docker/libcontainer/system"
)

// Runs the command under 'args' inside an existing container referred to by 'container'. This API currently does not support a Tty based 'term'.
// Returns the exitcode of the command upon success and appropriate error on failure.
func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, term Terminal, startCallback func()) (int, error) {
	containerJson, err := getContainerJson(container)
	if err != nil {
		return -1, err
	}

	var cmd exec.Cmd

	cmd.Path = nsinitPath
	cmd.Args = getNsEnterCommand(nsinitPath, strconv.Itoa(state.InitPid), containerJson, args)

	if err := term.Attach(&cmd); err != nil {
		return -1, err
	}
	defer term.Close()

	if err := cmd.Start(); err != nil {
		return -1, err
	}
	if startCallback != nil {
		startCallback()
	}

	if err := cmd.Wait(); err != nil {
		if _, ok := err.(*exec.ExitError); !ok {
			return -1, err
		}
	}

	return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
}

// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
	containerJson, err := getContainerJson(container)
	if err != nil {
		return err
	}
	// Enter the namespace and then finish setup
	finalArgs := getNsEnterCommand(os.Args[0], strconv.Itoa(state.InitPid), containerJson, args)

	if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil {
		return err
	}
	panic("unreachable")
}

func getContainerJson(container *libcontainer.Config) (string, error) {
	// TODO(vmarmol): If this gets too long, send it over a pipe to the child.
	// Marshall the container into JSON since it won't be available in the namespace.
	containerJson, err := json.Marshal(container)
	if err != nil {
		return "", err
	}
	return string(containerJson), nil
}

func getNsEnterCommand(nsinitPath, initPid, containerJson string, args []string) []string {
	return append([]string{
		nsinitPath,
		"nsenter",
		"--nspid", initPid,
		"--containerjson", containerJson,
		"--",
	}, args...)
}

// Run a command in a container after entering the namespace.
func NsEnter(container *libcontainer.Config, args []string) error {
	// clear the current processes env and replace it with the environment
	// defined on the container
	if err := LoadContainerEnvironment(container); err != nil {
		return err
	}
	if err := FinalizeNamespace(container); err != nil {
		return err
	}

	if container.ProcessLabel != "" {
		if err := label.SetProcessLabel(container.ProcessLabel); err != nil {
			return err
		}
	}

	if err := system.Execv(args[0], args[0:], container.Env); err != nil {
		return err
	}
	panic("unreachable")
}
<|endoftext|>


<commit_before>package apiGatewayDeploy

import (
	"database/sql"
	"fmt"
	"github.com/30x/apid-core"
	"sync"
)

var (
	unsafeDB apid.DB
	dbMux    sync.RWMutex
)

type DataDeployment struct {
	ID                 string
	BundleConfigID     string
	ApidClusterID      string
	DataScopeID        string
	BundleConfigJSON   string
	ConfigJSON         string
	Created            string
	CreatedBy          string
	Updated            string
	UpdatedBy          string
	BundleName         string
	BundleURI          string
	LocalBundleURI     string
	BundleChecksum     string
	BundleChecksumType string
	DeployStatus       string
	DeployErrorCode    int
	DeployErrorMessage string
}

type SQLExec interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
}

func InitDB(db apid.DB) error {
	_, err := db.Exec(`
	CREATE TABLE IF NOT EXISTS deployments (
		id character varying(36) NOT NULL,
		bundle_config_id varchar(36) NOT NULL,
		apid_cluster_id varchar(36) NOT NULL,
		data_scope_id varchar(36) NOT NULL,
		bundle_config_json text NOT NULL,
		config_json text NOT NULL,
		created timestamp without time zone,
		created_by text,
		updated timestamp without time zone,
		updated_by text,
		bundle_name text,
		bundle_uri text,
		local_bundle_uri text,
		bundle_checksum text,
		bundle_checksum_type text,
		deploy_status string,
		deploy_error_code int,
		deploy_error_message text,
		PRIMARY KEY (id)
	);
	`)
	if err != nil {
		return err
	}

	log.Debug("Database tables created.")
	return nil
}

func getDB() apid.DB {
	dbMux.RLock()
	db := unsafeDB
	dbMux.RUnlock()
	return db
}

// caller is responsible for calling dbMux.Lock() and dbMux.Unlock()
func SetDB(db apid.DB) {
	if unsafeDB == nil { // init API when DB is initialized
		go InitAPI()
	}
	unsafeDB = db
}

func InsertDeployment(tx *sql.Tx, dep DataDeployment) error {

	log.Debugf("insertDeployment: %s", dep.ID)

	stmt, err := tx.Prepare(`
	INSERT INTO deployments
		(id, bundle_config_id, apid_cluster_id, data_scope_id,
		bundle_config_json, config_json, created, created_by,
		updated, updated_by, bundle_name, bundle_uri, local_bundle_uri,
		bundle_checksum, bundle_checksum_type, deploy_status,
		deploy_error_code, deploy_error_message)
		VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18);
	`)
	if err != nil {
		log.Errorf("prepare insert into deployments %s failed: %v", dep.ID, err)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(
		dep.ID, dep.BundleConfigID, dep.ApidClusterID, dep.DataScopeID,
		dep.BundleConfigJSON, dep.ConfigJSON, dep.Created, dep.CreatedBy,
		dep.Updated, dep.UpdatedBy, dep.BundleName, dep.BundleURI,
		dep.LocalBundleURI, dep.BundleChecksum, dep.BundleChecksumType, dep.DeployStatus,
		dep.DeployErrorCode, dep.DeployErrorMessage)
	if err != nil {
		log.Errorf("insert into deployments %s failed: %v", dep.ID, err)
		return err
	}

	log.Debugf("insert into deployments %s succeeded", dep.ID)
	return err
}

func deleteDeployment(tx *sql.Tx, depID string) error {

	log.Debugf("deleteDeployment: %s", depID)

	stmt, err := tx.Prepare("DELETE FROM deployments where id = $1;")
	if err != nil {
		log.Errorf("prepare delete from deployments %s failed: %v", depID, err)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(depID)
	if err != nil {
		log.Errorf("delete from deployments %s failed: %v", depID, err)
		return err
	}

	deploymentsChanged <- depID

	log.Debugf("deleteDeployment %s succeeded", depID)
	return err
}

// getReadyDeployments() returns array of deployments that are ready to deploy
func getReadyDeployments() (deployments []DataDeployment, err error) {
	return getDeployments("WHERE local_bundle_uri != $1", "")
}

// getUnreadyDeployments() returns array of deployments that are not yet ready to deploy
func getUnreadyDeployments() (deployments []DataDeployment, err error) {
	return getDeployments("WHERE local_bundle_uri = $1 and deploy_status = $2", "", "")
}

// getDeployments() accepts a "WHERE ..." clause and optional parameters and returns the list of deployments
func getDeployments(where string, a ...interface{}) (deployments []DataDeployment, err error) {
	db := getDB()

	var stmt *sql.Stmt
	stmt, err = db.Prepare(`
	SELECT id, bundle_config_id, apid_cluster_id, data_scope_id,
		bundle_config_json, config_json, created, created_by,
		updated, updated_by, bundle_name, bundle_uri,
		local_bundle_uri, bundle_checksum, bundle_checksum_type, deploy_status,
		deploy_error_code, deploy_error_message
	FROM deployments
	` + where)
	if err != nil {
		return
	}
	var rows *sql.Rows
	rows, err = stmt.Query(a...)
	if err != nil {
		if err == sql.ErrNoRows {
			return
		}
		log.Errorf("Error querying deployments: %v", err)
		return
	}
	defer rows.Close()

	deployments = dataDeploymentsFromRows(rows)

	return
}

func dataDeploymentsFromRows(rows *sql.Rows) (deployments []DataDeployment) {
	for rows.Next() {
		dep := DataDeployment{}
		rows.Scan(&dep.ID, &dep.BundleConfigID, &dep.ApidClusterID, &dep.DataScopeID,
			&dep.BundleConfigJSON, &dep.ConfigJSON, &dep.Created, &dep.CreatedBy,
			&dep.Updated, &dep.UpdatedBy, &dep.BundleName, &dep.BundleURI,
			&dep.LocalBundleURI, &dep.BundleChecksum, &dep.BundleChecksumType, &dep.DeployStatus,
			&dep.DeployErrorCode, &dep.DeployErrorMessage,
		)
		deployments = append(deployments, dep)
	}
	return
}

func setDeploymentResults(results apiDeploymentResults) error {

	log.Debugf("setDeploymentResults: %v", results)

	tx, err := getDB().Begin()
	if err != nil {
		log.Errorf("Unable to begin transaction: %v", err)
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare(`
	UPDATE deployments
	SET deploy_status=$1, deploy_error_code=$2, deploy_error_message=$3
	WHERE id=$4;
	`)
	if err != nil {
		log.Errorf("prepare updateDeploymentStatus failed: %v", err)
		return err
	}
	defer stmt.Close()

	for _, result := range results {
		res, err := stmt.Exec(result.Status, result.ErrorCode, result.Message, result.ID)
		if err != nil {
			log.Errorf("update deployments %s to %s failed: %v", result.ID, result.Status, err)
			return err
		}
		n, err := res.RowsAffected()
		if n == 0 || err != nil {
			log.Error(fmt.Sprintf("no deployment matching '%s' to update. skipping.", result.ID))
		}
	}

	err = tx.Commit()
	if err != nil {
		log.Errorf("Unable to commit setDeploymentResults transaction: %v", err)
	}
	return err
}

func updateLocalBundleURI(depID, localBundleUri string) error {

	stmt, err := getDB().Prepare("UPDATE deployments SET local_bundle_uri=$1 WHERE id=$2;")
	if err != nil {
		log.Errorf("prepare updateLocalBundleURI failed: %v", err)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(localBundleUri, depID)
	if err != nil {
		log.Errorf("update deployments %s localBundleUri to %s failed: %v", depID, localBundleUri, err)
		return err
	}

	log.Debugf("update deployments %s localBundleUri to %s succeeded", depID, localBundleUri)

	return nil
}

func getLocalBundleURI(tx *sql.Tx, depID string) (localBundleUri string, err error) {

	err = tx.QueryRow("SELECT local_bundle_uri FROM deployments WHERE id=$1;", depID).Scan(&localBundleUri)
	if err == sql.ErrNoRows {
		err = nil
	}
	return
}
<commit_msg>missing stmt close<commit_after>package apiGatewayDeploy

import (
	"database/sql"
	"fmt"
	"sync"

	"github.com/30x/apid-core"
)

var (
	unsafeDB apid.DB
	dbMux    sync.RWMutex
)

type DataDeployment struct {
	ID                 string
	BundleConfigID     string
	ApidClusterID      string
	DataScopeID        string
	BundleConfigJSON   string
	ConfigJSON         string
	Created            string
	CreatedBy          string
	Updated            string
	UpdatedBy          string
	BundleName         string
	BundleURI          string
	LocalBundleURI     string
	BundleChecksum     string
	BundleChecksumType string
	DeployStatus       string
	DeployErrorCode    int
	DeployErrorMessage string
}

type SQLExec interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
}

func InitDB(db apid.DB) error {
	_, err := db.Exec(`
	CREATE TABLE IF NOT EXISTS deployments (
		id character varying(36) NOT NULL,
		bundle_config_id varchar(36) NOT NULL,
		apid_cluster_id varchar(36) NOT NULL,
		data_scope_id varchar(36) NOT NULL,
		bundle_config_json text NOT NULL,
		config_json text NOT NULL,
		created timestamp without time zone,
		created_by text,
		updated timestamp without time zone,
		updated_by text,
		bundle_name text,
		bundle_uri text,
		local_bundle_uri text,
		bundle_checksum text,
		bundle_checksum_type text,
		deploy_status string,
		deploy_error_code int,
		deploy_error_message text,
		PRIMARY KEY (id)
	);
	`)
	if err != nil {
		return err
	}

	log.Debug("Database tables created.")
	return nil
}

func getDB() apid.DB {
	dbMux.RLock()
	db := unsafeDB
	dbMux.RUnlock()
	return db
}

// caller is responsible for calling dbMux.Lock() and dbMux.Unlock()
func SetDB(db apid.DB) {
	if unsafeDB == nil { // init API when DB is initialized
		go InitAPI()
	}
	unsafeDB = db
}

func InsertDeployment(tx *sql.Tx, dep DataDeployment) error {

	log.Debugf("insertDeployment: %s", dep.ID)

	stmt, err := tx.Prepare(`
	INSERT INTO deployments
		(id, bundle_config_id, apid_cluster_id, data_scope_id,
		bundle_config_json, config_json, created, created_by,
		updated, updated_by, bundle_name, bundle_uri, local_bundle_uri,
		bundle_checksum, bundle_checksum_type, deploy_status,
		deploy_error_code, deploy_error_message)
		VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18);
	`)
	if err != nil {
		log.Errorf("prepare insert into deployments %s failed: %v", dep.ID, err)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(
		dep.ID, dep.BundleConfigID, dep.ApidClusterID, dep.DataScopeID,
		dep.BundleConfigJSON, dep.ConfigJSON, dep.Created, dep.CreatedBy,
		dep.Updated, dep.UpdatedBy, dep.BundleName, dep.BundleURI,
		dep.LocalBundleURI, dep.BundleChecksum, dep.BundleChecksumType, dep.DeployStatus,
		dep.DeployErrorCode, dep.DeployErrorMessage)
	if err != nil {
		log.Errorf("insert into deployments %s failed: %v", dep.ID, err)
		return err
	}

	log.Debugf("insert into deployments %s succeeded", dep.ID)
	return err
}

func deleteDeployment(tx *sql.Tx, depID string) error {

	log.Debugf("deleteDeployment: %s", depID)

	stmt, err := tx.Prepare("DELETE FROM deployments where id = $1;")
	if err != nil {
		log.Errorf("prepare delete from deployments %s failed: %v", depID, err)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(depID)
	if err != nil {
		log.Errorf("delete from deployments %s failed: %v", depID, err)
		return err
	}

	deploymentsChanged <- depID

	log.Debugf("deleteDeployment %s succeeded", depID)
	return err
}

// getReadyDeployments() returns array of deployments that are ready to deploy
func getReadyDeployments() (deployments []DataDeployment, err error) {
	return getDeployments("WHERE local_bundle_uri != $1", "")
}

// getUnreadyDeployments() returns array of deployments that are not yet ready to deploy
func getUnreadyDeployments() (deployments []DataDeployment, err error) {
	return getDeployments("WHERE local_bundle_uri = $1 and deploy_status = $2", "", "")
}

// getDeployments() accepts a "WHERE ..." clause and optional parameters and returns the list of deployments
func getDeployments(where string, a ...interface{}) (deployments []DataDeployment, err error) {
	db := getDB()

	var stmt *sql.Stmt
	stmt, err = db.Prepare(`
	SELECT id, bundle_config_id, apid_cluster_id, data_scope_id,
		bundle_config_json, config_json, created, created_by,
		updated, updated_by, bundle_name, bundle_uri,
		local_bundle_uri, bundle_checksum, bundle_checksum_type, deploy_status,
		deploy_error_code, deploy_error_message
	FROM deployments
	` + where)
	if err != nil {
		return
	}
	defer stmt.Close()
	var rows *sql.Rows
	rows, err = stmt.Query(a...)
	if err != nil {
		if err == sql.ErrNoRows {
			return
		}
		log.Errorf("Error querying deployments: %v", err)
		return
	}
	defer rows.Close()

	deployments = dataDeploymentsFromRows(rows)

	return
}

func dataDeploymentsFromRows(rows *sql.Rows) (deployments []DataDeployment) {
	for rows.Next() {
		dep := DataDeployment{}
		rows.Scan(&dep.ID, &dep.BundleConfigID, &dep.ApidClusterID, &dep.DataScopeID,
			&dep.BundleConfigJSON, &dep.ConfigJSON, &dep.Created, &dep.CreatedBy,
			&dep.Updated, &dep.UpdatedBy, &dep.BundleName, &dep.BundleURI,
			&dep.LocalBundleURI, &dep.BundleChecksum, &dep.BundleChecksumType, &dep.DeployStatus,
			&dep.DeployErrorCode, &dep.DeployErrorMessage,
		)
		deployments = append(deployments, dep)
	}
	return
}

func setDeploymentResults(results apiDeploymentResults) error {

	log.Debugf("setDeploymentResults: %v", results)

	tx, err := getDB().Begin()
	if err != nil {
		log.Errorf("Unable to begin transaction: %v", err)
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.Prepare(`
	UPDATE deployments
	SET deploy_status=$1, deploy_error_code=$2, deploy_error_message=$3
	WHERE id=$4;
	`)
	if err != nil {
		log.Errorf("prepare updateDeploymentStatus failed: %v", err)
		return err
	}
	defer stmt.Close()

	for _, result := range results {
		res, err := stmt.Exec(result.Status, result.ErrorCode, result.Message, result.ID)
		if err != nil {
			log.Errorf("update deployments %s to %s failed: %v", result.ID, result.Status, err)
			return err
		}
		n, err := res.RowsAffected()
		if n == 0 || err != nil {
			log.Error(fmt.Sprintf("no deployment matching '%s' to update. skipping.", result.ID))
		}
	}

	err = tx.Commit()
	if err != nil {
		log.Errorf("Unable to commit setDeploymentResults transaction: %v", err)
	}
	return err
}

func updateLocalBundleURI(depID, localBundleUri string) error {

	stmt, err := getDB().Prepare("UPDATE deployments SET local_bundle_uri=$1 WHERE id=$2;")
	if err != nil {
		log.Errorf("prepare updateLocalBundleURI failed: %v", err)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(localBundleUri, depID)
	if err != nil {
		log.Errorf("update deployments %s localBundleUri to %s failed: %v", depID, localBundleUri, err)
		return err
	}

	log.Debugf("update deployments %s localBundleUri to %s succeeded", depID, localBundleUri)

	return nil
}

func getLocalBundleURI(tx *sql.Tx, depID string) (localBundleUri string, err error) {

	err = tx.QueryRow("SELECT local_bundle_uri FROM deployments WHERE id=$1;", depID).Scan(&localBundleUri)
	if err == sql.ErrNoRows {
		err = nil
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2022 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 fakegitserver

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cgi"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/sirupsen/logrus"
)

type Client struct {
	host       string
	httpClient *http.Client
}

type RepoSetup struct {
	// Name of the Git repo. It will get a ".git" appended to it and be
	// initialized underneath o.gitReposParentDir.
	Name string `json:"name"`
	// Script to execute. This script runs inside the repo to perform any
	// additional repo setup tasks. This script is executed by /bin/sh.
	Script string `json:"script"`
	// Whether to create the repo at the path (o.gitReposParentDir + name +
	// ".git") even if a file (directory) exists there already. This basically
	// does a 'rm -rf' of the folder first.
	Overwrite bool `json:"overwrite"`
}

func NewClient(host string, timeout time.Duration) *Client {
	return &Client{
		host: host,
		httpClient: &http.Client{
			Timeout: timeout,
		},
	}
}

func (c *Client) do(method, endpoint string, payload []byte, params map[string]string) (*http.Response, error) {
	baseURL := fmt.Sprintf("%s/%s", c.host, endpoint)
	req, err := http.NewRequest(method, baseURL, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json; charset=UTF-8")
	q := req.URL.Query()
	for key, val := range params {
		q.Set(key, val)
	}
	req.URL.RawQuery = q.Encode()
	return c.httpClient.Do(req)
}

// SetupRepo sends a POST request with the RepoSetup contents.
func (c *Client) SetupRepo(repoSetup RepoSetup) error {
	buf, err := json.Marshal(repoSetup)
	if err != nil {
		return fmt.Errorf("could not marshal %v", repoSetup)
	}

	resp, err := c.do(http.MethodPost, "setup-repo", buf, nil)
	if err != nil {
		return errors.New("FGS repo setup failed")
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("got %v response", resp.StatusCode)
	}
	return nil
}

// GitCGIHandler returns an http.Handler that is backed by git-http-backend (a
// CGI executable). git-http-backend is the `git http-backend` subcommand that
// comes distributed with a default git installation.
func GitCGIHandler(gitBinary, gitReposParentDir string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		h := &cgi.Handler{
			Path: gitBinary,
			Env: []string{
				"GIT_PROJECT_ROOT=" + gitReposParentDir,
				// Allow reading of all repos under gitReposParentDir.
				"GIT_HTTP_EXPORT_ALL=1",
			},
			Args: []string{
				"http-backend",
			},
		}
		// Remove the "/repo" prefix, because git-http-backend expects the
		// request to simply be the Git repo name.
		req.URL.Path = strings.TrimPrefix(string(req.URL.Path), "/repo")
		// It appears that this RequestURI field is not used; but for
		// completeness trim the prefix here as well.
		req.RequestURI = strings.TrimPrefix(req.RequestURI, "/repo")
		h.ServeHTTP(w, req)
	})
}

// SetupRepoHandler executes a JSON payload of instructions to set up a Git
// repo.
func SetupRepoHandler(gitReposParentDir string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		buf, err := ioutil.ReadAll(req.Body)
		defer req.Body.Close()
		if err != nil {
			logrus.Errorf("failed to read request body: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}

		logrus.Infof("request body received: %v", string(buf))
		var repoSetup RepoSetup
		err = json.Unmarshal(buf, &repoSetup)
		if err != nil {
			logrus.Errorf("failed to parse request body as FGSRepoSetup: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}

		repo, err := setupRepo(gitReposParentDir, &repoSetup)
		if err != nil {
			// Just log the error if the setup fails so that the developer can
			// fix their error and retry without having to restart this server.
			logrus.Errorf("failed to setup repo: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}

		msg, err := getLog(repo)
		if err != nil {
			logrus.Errorf("failed to get repo stats: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}
		fmt.Fprintf(w, "%s", msg)
	})
}

func setupRepo(gitReposParentDir string, repoSetup *RepoSetup) (*git.Repository, error) {
	dir := filepath.Join(gitReposParentDir, repoSetup.Name+".git")
	logger := logrus.WithField("directory", dir)

	if _, err := os.Stat(dir); !os.IsNotExist(err) {
		if repoSetup.Overwrite {
			if err := os.RemoveAll(dir); err != nil {
				logger.Error("(overwrite) could not remove directory")
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("path %s already exists but overwrite is not enabled; aborting", dir)
		}
	}

	if err := os.MkdirAll(dir, os.ModePerm); err != nil {
		logger.Error("could not create directory")
		return nil, err
	}

	repo, err := git.PlainInit(dir, false)
	if err != nil {
		logger.Error("could not initialize git repo in directory")
		return nil, err
	}

	if err := setGitConfigOptions(repo); err != nil {
		logger.Error("config setup failed")
		return nil, err
	}

	if err := runSetupScript(dir, repoSetup.Script); err != nil {
		logger.Error("running the repo setup script failed")
		return nil, err
	}

	if err := convertToBareRepo(repo, dir); err != nil {
		logger.Error("conversion to bare repo failed")
		return nil, err
	}

	repo, err = git.PlainOpen(dir)
	if err != nil {
		logger.Error("could not reopen repo")
		return nil, err
	}
	return repo, nil
}

// getLog creates a report of Git repo statistics.
func getLog(repo *git.Repository) (string, error) {

	var stats string

	// Show `git log --all` equivalent.
	ref, err := repo.Head()
	if err != nil {
		return "", errors.New("could not get HEAD")
	}
	commits, err := repo.Log(&git.LogOptions{From: ref.Hash(), All: true})
	if err != nil {
		return "", errors.New("could not get git logs")
	}
	err = commits.ForEach(func(commit *object.Commit) error {
		stats += fmt.Sprintln(commit)
		return nil
	})

	return stats, nil
}

func convertToBareRepo(repo *git.Repository, repoPath string) error {
	// Convert to a bare repo.
	config, err := repo.Config()
	if err != nil {
		return err
	}
	config.Core.IsBare = true
	repo.SetConfig(config)

	tempDir, err := ioutil.TempDir("", "fgs")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)

	// Move "<REPO>/.git" directory to a temporary directory.
	err = os.Rename(filepath.Join(repoPath, ".git"), filepath.Join(tempDir, ".git"))
	if err != nil {
		return err
	}

	// Delete <REPO> folder. This takes care of deleting all worktree files.
	err = os.RemoveAll(repoPath)
	if err != nil {
		return err
	}

	// Move the .git folder to the <REPO> folder path.
	err = os.Rename(filepath.Join(tempDir, ".git"), repoPath)
	if err != nil {
		return err
	}
	return nil
}

func runSetupScript(repoPath, script string) error {
	// Catch errors in the script.
	script = "set -eu;" + script

	logrus.Infof("setup script looks like: %v", script)

	cmd := exec.Command("sh", "-c", script)
	cmd.Dir = repoPath

	// By default, make it so that the git commands contained in the script
	// result in reproducible commits. This can be overridden by the script
	// itself if it chooses to (re-)export the same environment variables.
	cmd.Env = []string{
		"GIT_AUTHOR_NAME=abc",
		"GIT_AUTHOR_EMAIL=d@e.f",
		"GIT_AUTHOR_DATE='Thu May 19 12:34:56 2022 +0000'",
		"GIT_COMMITTER_NAME=abc",
		"GIT_COMMITTER_EMAIL=d@e.f",
		"GIT_COMMITTER_DATE='Thu May 19 12:34:56 2022 +0000'"}

	return cmd.Run()
}

func setGitConfigOptions(r *git.Repository) error {
	config, err := r.Config()
	if err != nil {
		return err
	}

	// Ensure that the given Git repo allows anonymous push access. This is
	// required for unauthenticated clients to push to the repo over HTTP.
	config.Raw.SetOption("http", "", "receivepack", "true")

	// Advertise all objects. This allows clients to fetch by raw commit SHAs,
	// avoiding the dreaded
	//
	// 		Server does not allow request for unadvertised object <SHA>
	//
	// error.
	config.Raw.SetOption("uploadpack", "", "allowAnySHA1InWant", "true")

	r.SetConfig(config)

	return nil
}
<commit_msg>fakegitserver: add more logging<commit_after>/*
Copyright 2022 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 fakegitserver

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cgi"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/sirupsen/logrus"
)

type Client struct {
	host       string
	httpClient *http.Client
}

type RepoSetup struct {
	// Name of the Git repo. It will get a ".git" appended to it and be
	// initialized underneath o.gitReposParentDir.
	Name string `json:"name"`
	// Script to execute. This script runs inside the repo to perform any
	// additional repo setup tasks. This script is executed by /bin/sh.
	Script string `json:"script"`
	// Whether to create the repo at the path (o.gitReposParentDir + name +
	// ".git") even if a file (directory) exists there already. This basically
	// does a 'rm -rf' of the folder first.
	Overwrite bool `json:"overwrite"`
}

func NewClient(host string, timeout time.Duration) *Client {
	return &Client{
		host: host,
		httpClient: &http.Client{
			Timeout: timeout,
		},
	}
}

func (c *Client) do(method, endpoint string, payload []byte, params map[string]string) (*http.Response, error) {
	baseURL := fmt.Sprintf("%s/%s", c.host, endpoint)
	req, err := http.NewRequest(method, baseURL, bytes.NewBuffer(payload))
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json; charset=UTF-8")
	q := req.URL.Query()
	for key, val := range params {
		q.Set(key, val)
	}
	req.URL.RawQuery = q.Encode()
	return c.httpClient.Do(req)
}

// SetupRepo sends a POST request with the RepoSetup contents.
func (c *Client) SetupRepo(repoSetup RepoSetup) error {
	buf, err := json.Marshal(repoSetup)
	if err != nil {
		return fmt.Errorf("could not marshal %v", repoSetup)
	}

	resp, err := c.do(http.MethodPost, "setup-repo", buf, nil)
	if err != nil {
		return errors.New("FGS repo setup failed")
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("got %v response", resp.StatusCode)
	}
	return nil
}

// GitCGIHandler returns an http.Handler that is backed by git-http-backend (a
// CGI executable). git-http-backend is the `git http-backend` subcommand that
// comes distributed with a default git installation.
func GitCGIHandler(gitBinary, gitReposParentDir string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		h := &cgi.Handler{
			Path: gitBinary,
			Env: []string{
				"GIT_PROJECT_ROOT=" + gitReposParentDir,
				// Allow reading of all repos under gitReposParentDir.
				"GIT_HTTP_EXPORT_ALL=1",
			},
			Args: []string{
				"http-backend",
			},
		}
		// Remove the "/repo" prefix, because git-http-backend expects the
		// request to simply be the Git repo name.
		req.URL.Path = strings.TrimPrefix(string(req.URL.Path), "/repo")
		// It appears that this RequestURI field is not used; but for
		// completeness trim the prefix here as well.
		req.RequestURI = strings.TrimPrefix(req.RequestURI, "/repo")
		h.ServeHTTP(w, req)
	})
}

// SetupRepoHandler executes a JSON payload of instructions to set up a Git
// repo.
func SetupRepoHandler(gitReposParentDir string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		buf, err := ioutil.ReadAll(req.Body)
		defer req.Body.Close()
		if err != nil {
			logrus.Errorf("failed to read request body: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}

		logrus.Infof("request body received: %v", string(buf))
		var repoSetup RepoSetup
		err = json.Unmarshal(buf, &repoSetup)
		if err != nil {
			logrus.Errorf("failed to parse request body as FGSRepoSetup: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}

		repo, err := setupRepo(gitReposParentDir, &repoSetup)
		if err != nil {
			// Just log the error if the setup fails so that the developer can
			// fix their error and retry without having to restart this server.
			logrus.Errorf("failed to setup repo: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}

		msg, err := getLog(repo)
		if err != nil {
			logrus.Errorf("failed to get repo stats: %v", err)
			http.Error(w, err.Error(), 500)
			return
		}
		fmt.Fprintf(w, "%s", msg)
	})
}

func setupRepo(gitReposParentDir string, repoSetup *RepoSetup) (*git.Repository, error) {
	dir := filepath.Join(gitReposParentDir, repoSetup.Name+".git")
	logger := logrus.WithField("directory", dir)

	if _, err := os.Stat(dir); !os.IsNotExist(err) {
		if repoSetup.Overwrite {
			if err := os.RemoveAll(dir); err != nil {
				logger.Error("(overwrite) could not remove directory")
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("path %s already exists but overwrite is not enabled; aborting", dir)
		}
	}

	if err := os.MkdirAll(dir, os.ModePerm); err != nil {
		logger.Error("could not create directory")
		return nil, err
	}

	repo, err := git.PlainInit(dir, false)
	if err != nil {
		logger.Error("could not initialize git repo in directory")
		return nil, err
	}

	if err := setGitConfigOptions(repo); err != nil {
		logger.Error("config setup failed")
		return nil, err
	}

	if err := runSetupScript(dir, repoSetup.Script); err != nil {
		logger.Error("running the repo setup script failed")
		return nil, err
	}

	logger.Infof("successfully ran setup script in %s", dir)

	if err := convertToBareRepo(repo, dir); err != nil {
		logger.Error("conversion to bare repo failed")
		return nil, err
	}

	repo, err = git.PlainOpen(dir)
	if err != nil {
		logger.Error("could not reopen repo")
		return nil, err
	}
	return repo, nil
}

// getLog creates a report of Git repo statistics.
func getLog(repo *git.Repository) (string, error) {

	var stats string

	// Show `git log --all` equivalent.
	ref, err := repo.Head()
	if err != nil {
		return "", errors.New("could not get HEAD")
	}
	commits, err := repo.Log(&git.LogOptions{From: ref.Hash(), All: true})
	if err != nil {
		return "", errors.New("could not get git logs")
	}
	err = commits.ForEach(func(commit *object.Commit) error {
		stats += fmt.Sprintln(commit)
		return nil
	})

	return stats, nil
}

func convertToBareRepo(repo *git.Repository, repoPath string) error {
	// Convert to a bare repo.
	config, err := repo.Config()
	if err != nil {
		return err
	}
	config.Core.IsBare = true
	repo.SetConfig(config)

	tempDir, err := ioutil.TempDir("", "fgs")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)

	// Move "<REPO>/.git" directory to a temporary directory.
	err = os.Rename(filepath.Join(repoPath, ".git"), filepath.Join(tempDir, ".git"))
	if err != nil {
		return err
	}

	// Delete <REPO> folder. This takes care of deleting all worktree files.
	err = os.RemoveAll(repoPath)
	if err != nil {
		return err
	}

	// Move the .git folder to the <REPO> folder path.
	err = os.Rename(filepath.Join(tempDir, ".git"), repoPath)
	if err != nil {
		return err
	}
	return nil
}

func runSetupScript(repoPath, script string) error {
	// Catch errors in the script.
	script = "set -eu;" + script

	logrus.Infof("setup script looks like: %v", script)

	cmd := exec.Command("sh", "-c", script)
	cmd.Dir = repoPath

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// By default, make it so that the git commands contained in the script
	// result in reproducible commits. This can be overridden by the script
	// itself if it chooses to (re-)export the same environment variables.
	cmd.Env = []string{
		"GIT_AUTHOR_NAME=abc",
		"GIT_AUTHOR_EMAIL=d@e.f",
		"GIT_AUTHOR_DATE='Thu May 19 12:34:56 2022 +0000'",
		"GIT_COMMITTER_NAME=abc",
		"GIT_COMMITTER_EMAIL=d@e.f",
		"GIT_COMMITTER_DATE='Thu May 19 12:34:56 2022 +0000'"}

	return cmd.Run()
}

func setGitConfigOptions(r *git.Repository) error {
	config, err := r.Config()
	if err != nil {
		return err
	}

	// Ensure that the given Git repo allows anonymous push access. This is
	// required for unauthenticated clients to push to the repo over HTTP.
	config.Raw.SetOption("http", "", "receivepack", "true")

	// Advertise all objects. This allows clients to fetch by raw commit SHAs,
	// avoiding the dreaded
	//
	// 		Server does not allow request for unadvertised object <SHA>
	//
	// error.
	config.Raw.SetOption("uploadpack", "", "allowAnySHA1InWant", "true")

	r.SetConfig(config)

	return nil
}
<|endoftext|>