<commit_before>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package build

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"appengine"
	"appengine/datastore"
)

var knownTags = map[string]string{
	"go1":   "0051c7442fed9c888de6617fa9239a913904d96e",
	"go1.1": "d29da2ced72ba2cf48ed6a8f1ec4abc01e4c5bf1",
	"go1.2": "b1edf8faa5d6cbc50c6515785df9df9c19296564",
}

var lastRelease = "go1.2"

func splitBench(benchProcs string) (string, int) {
	ss := strings.Split(benchProcs, "-")
	procs, _ := strconv.Atoi(ss[1])
	return ss[0], procs
}

func dashPerfCommits(c appengine.Context, page int) ([]*Commit, error) {
	q := datastore.NewQuery("Commit").
		Ancestor((&Package{}).Key(c)).
		Order("-Num").
		Filter("NeedsBenchmarking =", true).
		Limit(commitsPerPage).
		Offset(page * commitsPerPage)
	var commits []*Commit
	_, err := q.GetAll(c, &commits)
	if err == nil && len(commits) == 0 {
		err = fmt.Errorf("no commits")
	}
	return commits, err
}

func perfChangeStyle(pc *PerfConfig, v float64, builder, benchmark, metric string) string {
	noise := pc.NoiseLevel(builder, benchmark, metric)
	if isNoise(v, noise) {
		return "noise"
	}
	if v > 0 {
		return "bad"
	}
	return "good"
}

func isNoise(diff, noise float64) bool {
	rnoise := -100 * noise / (noise + 100)
	return diff < noise && diff > rnoise
}

func perfDiff(old, new uint64) float64 {
	return 100*float64(new)/float64(old) - 100
}

func isPerfFailed(res *PerfResult, builder string) bool {
	data := res.ParseData()[builder]
	return data != nil && data["meta-done"] != nil && !data["meta-done"].OK
}

// PerfResultCache caches a set of PerfResults so that it's easy to access them
// without lots of duplicate accesses to datastore.
// It allows to iterate over newer or older results for some base commit.
type PerfResultCache struct {
	c       appengine.Context
	newer   bool
	iter    *datastore.Iterator
	results map[int]*PerfResult
}

func MakePerfResultCache(c appengine.Context, com *Commit, newer bool) *PerfResultCache {
	p := &Package{}
	q := datastore.NewQuery("PerfResult").Ancestor(p.Key(c)).Limit(100)
	if newer {
		q = q.Filter("CommitNum >=", com.Num).Order("CommitNum")
	} else {
		q = q.Filter("CommitNum <=", com.Num).Order("-CommitNum")
	}
	rc := &PerfResultCache{c: c, newer: newer, iter: q.Run(c), results: make(map[int]*PerfResult)}
	return rc
}

func (rc *PerfResultCache) Get(commitNum int) *PerfResult {
	rc.Next(commitNum) // fetch the commit, if necessary
	return rc.results[commitNum]
}

// Next returns the next PerfResult for the commit commitNum.
// It does not care whether the result has any data, failed or whatever.
func (rc *PerfResultCache) Next(commitNum int) (*PerfResult, error) {
	// See if we have next result in the cache.
	next := -1
	for ci := range rc.results {
		if rc.newer {
			if ci > commitNum && (next == -1 || ci < next) {
				next = ci
			}
		} else {
			if ci < commitNum && (next == -1 || ci > next) {
				next = ci
			}
		}
	}
	//rc.c.Errorf("PerfResultCache.Next: num=%v next=%v", commitNum, next)
	if next != -1 {
		return rc.results[next], nil
	}
	// Fetch next result from datastore.
	res := new(PerfResult)
	_, err := rc.iter.Next(res)
	//rc.c.Errorf("PerfResultCache.Next: fetched %v %+v", err, res)
	if err == datastore.Done {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("fetching perf results: %v", err)
	}
	if (rc.newer && res.CommitNum < commitNum) || (!rc.newer && res.CommitNum > commitNum) {
		rc.c.Errorf("PerfResultCache.Next: bad commit num")
	}
	rc.results[res.CommitNum] = res
	return res, nil
}

// NextForComparison returns PerfResult which we need to use for performance comprison.
// It skips failed results, but does not skip results with no data.
func (rc *PerfResultCache) NextForComparison(commitNum int, builder string) (*PerfResult, error) {
	for {
		res, err := rc.Next(commitNum)
		if err != nil {
			return nil, err
		}
		if res == nil {
			return nil, nil
		}
		if res.CommitNum == commitNum {
			continue
		}
		parsed := res.ParseData()
		if builder != "" {
			// Comparing for a particular builder.
			// This is used in perf_changes and in email notifications.
			b := parsed[builder]
			if b == nil || b["meta-done"] == nil {
				// No results yet, must not do the comparison.
				return nil, nil
			}
			if b["meta-done"].OK {
				// Have complete results, compare.
				return res, nil
			}
		} else {
			// Comparing for all builders, find a result with at least
			// one successful meta-done.
			// This is used in perf_detail.
			for _, benchs := range parsed {
				if data := benchs["meta-done"]; data != nil && data.OK {
					return res, nil
				}
			}
		}
		// Failed, try next result.
		commitNum = res.CommitNum
	}
}

type PerfChange struct {
	builder string
	bench   string
	metric  string
	old     uint64
	new     uint64
	diff    float64
}

func significantPerfChanges(pc *PerfConfig, builder string, prevRes, res *PerfResult) (changes []*PerfChange) {
	// First, collect all significant changes.
	for builder1, benchmarks1 := range res.ParseData() {
		if builder != "" && builder != builder1 {
			// This is not the builder you're looking for, Luke.
			continue
		}
		benchmarks0 := prevRes.ParseData()[builder1]
		if benchmarks0 == nil {
			continue
		}
		for benchmark, data1 := range benchmarks1 {
			data0 := benchmarks0[benchmark]
			if data0 == nil {
				continue
			}
			for metric, val := range data1.Metrics {
				val0 := data0.Metrics[metric]
				if val0 == 0 {
					continue
				}
				diff := perfDiff(val0, val)
				noise := pc.NoiseLevel(builder, benchmark, metric)
				if isNoise(diff, noise) {
					continue
				}
				ch := &PerfChange{builder: builder, bench: benchmark, metric: metric, old: val0, new: val, diff: diff}
				changes = append(changes, ch)
			}
		}
	}
	// Then, strip non-repeatable changes (flakes).
	// The hypothesis is that a real change must show up with at least
	// 2 different values of GOMAXPROCS.
	cnt := make(map[string]int)
	for _, ch := range changes {
		b, _ := splitBench(ch.bench)
		name := b + "|" + ch.metric
		inc := 1
		if ch.diff < 0 {
			inc = -1
		}
		cnt[name] = cnt[name] + inc
	}
	for i := 0; i < len(changes); i++ {
		ch := changes[i]
		b, _ := splitBench(ch.bench)
		name := b + "|" + ch.metric
		if n := cnt[name]; n <= -2 || n >= 2 {
			continue
		}
		last := len(changes) - 1
		changes[i] = changes[last]
		changes = changes[:last]
		i--
	}
	return changes
}

// orderPrefTodo reorders commit nums for benchmarking todo.
// The resulting order is somewhat tricky. We want 2 things:
// 1. benchmark sequentially backwards (this provides information about most
// recent changes, and allows to estimate noise levels)
// 2. benchmark old commits in "scatter" order (this allows to quickly gather
// brief information about thousands of old commits)
// So this function interleaves the two orders.
func orderPrefTodo(nums []int) []int {
	sort.Ints(nums)
	n := len(nums)
	pow2 := uint32(0) // next power-of-two that is >= n
	npow2 := 0
	for npow2 <= n {
		pow2++
		npow2 = 1 << pow2
	}
	res := make([]int, n)
	resPos := n - 1            // result array is filled backwards
	present := make([]bool, n) // denotes values that already present in result array
	for i0, i1 := n-1, 0; i0 >= 0 || i1 < npow2; {
		// i0 represents "benchmark sequentially backwards" sequence
		// find the next commit that is not yet present and add it
		for cnt := 0; cnt < 2; cnt++ {
			for ; i0 >= 0; i0-- {
				if !present[i0] {
					present[i0] = true
					res[resPos] = nums[i0]
					resPos--
					i0--
					break
				}
			}
		}
		// i1 represents "scatter order" sequence
		// find the next commit that is not yet present and add it
		for ; i1 < npow2; i1++ {
			// do the "recursive split-ordering" trick
			idx := 0 // bitwise reverse of i1
			for j := uint32(0); j <= pow2; j++ {
				if (i1 & (1 << j)) != 0 {
					idx = idx | (1 << (pow2 - j - 1))
				}
			}
			if idx < n && !present[idx] {
				present[idx] = true
				res[resPos] = nums[idx]
				resPos--
				i1++
				break
			}
		}
	}
	// The above can't possibly be correct. Do dump check.
	res2 := make([]int, n)
	copy(res2, res)
	sort.Ints(res2)
	for i := range res2 {
		if res2[i] != nums[i] {
			panic(fmt.Sprintf("diff at %v: expect %v, want %v\nwas: %v\n become: %v",
				i, nums[i], res2[i], nums, res2))
		}
	}
	return res
}
<commit_msg>dashboard/app: tag 1.3 release<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package build

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"appengine"
	"appengine/datastore"
)

var knownTags = map[string]string{
	"go1":   "0051c7442fed9c888de6617fa9239a913904d96e",
	"go1.1": "d29da2ced72ba2cf48ed6a8f1ec4abc01e4c5bf1",
	"go1.2": "b1edf8faa5d6cbc50c6515785df9df9c19296564",
	"go1.3": "f153208c0a0e306bfca14f71ef11f09859ccabc8",
}

var lastRelease = "go1.3"

func splitBench(benchProcs string) (string, int) {
	ss := strings.Split(benchProcs, "-")
	procs, _ := strconv.Atoi(ss[1])
	return ss[0], procs
}

func dashPerfCommits(c appengine.Context, page int) ([]*Commit, error) {
	q := datastore.NewQuery("Commit").
		Ancestor((&Package{}).Key(c)).
		Order("-Num").
		Filter("NeedsBenchmarking =", true).
		Limit(commitsPerPage).
		Offset(page * commitsPerPage)
	var commits []*Commit
	_, err := q.GetAll(c, &commits)
	if err == nil && len(commits) == 0 {
		err = fmt.Errorf("no commits")
	}
	return commits, err
}

func perfChangeStyle(pc *PerfConfig, v float64, builder, benchmark, metric string) string {
	noise := pc.NoiseLevel(builder, benchmark, metric)
	if isNoise(v, noise) {
		return "noise"
	}
	if v > 0 {
		return "bad"
	}
	return "good"
}

func isNoise(diff, noise float64) bool {
	rnoise := -100 * noise / (noise + 100)
	return diff < noise && diff > rnoise
}

func perfDiff(old, new uint64) float64 {
	return 100*float64(new)/float64(old) - 100
}

func isPerfFailed(res *PerfResult, builder string) bool {
	data := res.ParseData()[builder]
	return data != nil && data["meta-done"] != nil && !data["meta-done"].OK
}

// PerfResultCache caches a set of PerfResults so that it's easy to access them
// without lots of duplicate accesses to datastore.
// It allows to iterate over newer or older results for some base commit.
type PerfResultCache struct {
	c       appengine.Context
	newer   bool
	iter    *datastore.Iterator
	results map[int]*PerfResult
}

func MakePerfResultCache(c appengine.Context, com *Commit, newer bool) *PerfResultCache {
	p := &Package{}
	q := datastore.NewQuery("PerfResult").Ancestor(p.Key(c)).Limit(100)
	if newer {
		q = q.Filter("CommitNum >=", com.Num).Order("CommitNum")
	} else {
		q = q.Filter("CommitNum <=", com.Num).Order("-CommitNum")
	}
	rc := &PerfResultCache{c: c, newer: newer, iter: q.Run(c), results: make(map[int]*PerfResult)}
	return rc
}

func (rc *PerfResultCache) Get(commitNum int) *PerfResult {
	rc.Next(commitNum) // fetch the commit, if necessary
	return rc.results[commitNum]
}

// Next returns the next PerfResult for the commit commitNum.
// It does not care whether the result has any data, failed or whatever.
func (rc *PerfResultCache) Next(commitNum int) (*PerfResult, error) {
	// See if we have next result in the cache.
	next := -1
	for ci := range rc.results {
		if rc.newer {
			if ci > commitNum && (next == -1 || ci < next) {
				next = ci
			}
		} else {
			if ci < commitNum && (next == -1 || ci > next) {
				next = ci
			}
		}
	}
	//rc.c.Errorf("PerfResultCache.Next: num=%v next=%v", commitNum, next)
	if next != -1 {
		return rc.results[next], nil
	}
	// Fetch next result from datastore.
	res := new(PerfResult)
	_, err := rc.iter.Next(res)
	//rc.c.Errorf("PerfResultCache.Next: fetched %v %+v", err, res)
	if err == datastore.Done {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("fetching perf results: %v", err)
	}
	if (rc.newer && res.CommitNum < commitNum) || (!rc.newer && res.CommitNum > commitNum) {
		rc.c.Errorf("PerfResultCache.Next: bad commit num")
	}
	rc.results[res.CommitNum] = res
	return res, nil
}

// NextForComparison returns PerfResult which we need to use for performance comprison.
// It skips failed results, but does not skip results with no data.
func (rc *PerfResultCache) NextForComparison(commitNum int, builder string) (*PerfResult, error) {
	for {
		res, err := rc.Next(commitNum)
		if err != nil {
			return nil, err
		}
		if res == nil {
			return nil, nil
		}
		if res.CommitNum == commitNum {
			continue
		}
		parsed := res.ParseData()
		if builder != "" {
			// Comparing for a particular builder.
			// This is used in perf_changes and in email notifications.
			b := parsed[builder]
			if b == nil || b["meta-done"] == nil {
				// No results yet, must not do the comparison.
				return nil, nil
			}
			if b["meta-done"].OK {
				// Have complete results, compare.
				return res, nil
			}
		} else {
			// Comparing for all builders, find a result with at least
			// one successful meta-done.
			// This is used in perf_detail.
			for _, benchs := range parsed {
				if data := benchs["meta-done"]; data != nil && data.OK {
					return res, nil
				}
			}
		}
		// Failed, try next result.
		commitNum = res.CommitNum
	}
}

type PerfChange struct {
	builder string
	bench   string
	metric  string
	old     uint64
	new     uint64
	diff    float64
}

func significantPerfChanges(pc *PerfConfig, builder string, prevRes, res *PerfResult) (changes []*PerfChange) {
	// First, collect all significant changes.
	for builder1, benchmarks1 := range res.ParseData() {
		if builder != "" && builder != builder1 {
			// This is not the builder you're looking for, Luke.
			continue
		}
		benchmarks0 := prevRes.ParseData()[builder1]
		if benchmarks0 == nil {
			continue
		}
		for benchmark, data1 := range benchmarks1 {
			data0 := benchmarks0[benchmark]
			if data0 == nil {
				continue
			}
			for metric, val := range data1.Metrics {
				val0 := data0.Metrics[metric]
				if val0 == 0 {
					continue
				}
				diff := perfDiff(val0, val)
				noise := pc.NoiseLevel(builder, benchmark, metric)
				if isNoise(diff, noise) {
					continue
				}
				ch := &PerfChange{builder: builder, bench: benchmark, metric: metric, old: val0, new: val, diff: diff}
				changes = append(changes, ch)
			}
		}
	}
	// Then, strip non-repeatable changes (flakes).
	// The hypothesis is that a real change must show up with at least
	// 2 different values of GOMAXPROCS.
	cnt := make(map[string]int)
	for _, ch := range changes {
		b, _ := splitBench(ch.bench)
		name := b + "|" + ch.metric
		inc := 1
		if ch.diff < 0 {
			inc = -1
		}
		cnt[name] = cnt[name] + inc
	}
	for i := 0; i < len(changes); i++ {
		ch := changes[i]
		b, _ := splitBench(ch.bench)
		name := b + "|" + ch.metric
		if n := cnt[name]; n <= -2 || n >= 2 {
			continue
		}
		last := len(changes) - 1
		changes[i] = changes[last]
		changes = changes[:last]
		i--
	}
	return changes
}

// orderPrefTodo reorders commit nums for benchmarking todo.
// The resulting order is somewhat tricky. We want 2 things:
// 1. benchmark sequentially backwards (this provides information about most
// recent changes, and allows to estimate noise levels)
// 2. benchmark old commits in "scatter" order (this allows to quickly gather
// brief information about thousands of old commits)
// So this function interleaves the two orders.
func orderPrefTodo(nums []int) []int {
	sort.Ints(nums)
	n := len(nums)
	pow2 := uint32(0) // next power-of-two that is >= n
	npow2 := 0
	for npow2 <= n {
		pow2++
		npow2 = 1 << pow2
	}
	res := make([]int, n)
	resPos := n - 1            // result array is filled backwards
	present := make([]bool, n) // denotes values that already present in result array
	for i0, i1 := n-1, 0; i0 >= 0 || i1 < npow2; {
		// i0 represents "benchmark sequentially backwards" sequence
		// find the next commit that is not yet present and add it
		for cnt := 0; cnt < 2; cnt++ {
			for ; i0 >= 0; i0-- {
				if !present[i0] {
					present[i0] = true
					res[resPos] = nums[i0]
					resPos--
					i0--
					break
				}
			}
		}
		// i1 represents "scatter order" sequence
		// find the next commit that is not yet present and add it
		for ; i1 < npow2; i1++ {
			// do the "recursive split-ordering" trick
			idx := 0 // bitwise reverse of i1
			for j := uint32(0); j <= pow2; j++ {
				if (i1 & (1 << j)) != 0 {
					idx = idx | (1 << (pow2 - j - 1))
				}
			}
			if idx < n && !present[idx] {
				present[idx] = true
				res[resPos] = nums[idx]
				resPos--
				i1++
				break
			}
		}
	}
	// The above can't possibly be correct. Do dump check.
	res2 := make([]int, n)
	copy(res2, res)
	sort.Ints(res2)
	for i := range res2 {
		if res2[i] != nums[i] {
			panic(fmt.Sprintf("diff at %v: expect %v, want %v\nwas: %v\n become: %v",
				i, nums[i], res2[i], nums, res2))
		}
	}
	return res
}
<|endoftext|>


<commit_before>package kiwi

/*
Copyright (c) 2016, Alexander I.Grafov aka Axel
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of kvlog nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"
)

/* All tests consists of three parts:

- arrange structures and initialize objects for use in tests
- act on testing object
- check and assert on results

These parts separated by empty lines in each test function.
*/

var (
	sampleMixContext = []interface{}{"context1", "value", "context2", 1, "context3", 0.1, "context4", []string{"the", "sample"}}
	sampleMixRecord  = []interface{}{"key1", "value", "key2", 2, 3, 4, "common", []string{"the", "sample"}}
)

// Get records from logger. Helper for testing.
func (l *Logger) getRecords() []pair {
	return l.pairs
}

// Get context from logger. Helper for testing.
func (l *Logger) getContext() []pair {
	return l.context
}

func TestNewLogger(t *testing.T) {
	l := New()

	if l == nil {
		t.Fatal("initalized logger is nil")
	}
}

// Test logging of string value.
func TestLogger_LogStringValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", "The sample string with a lot of spaces.")

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		t.Fail()
	}
}

// Test logging of byte array.
func TestLogger_LogBytesValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", []byte("The sample string with a lot of spaces."))

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		t.Fail()
	}
}

// Test logging of integer value.
func TestLogger_LogIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		t.Fail()
	}
}

// Test logging of negative integer value.
func TestLogger_LogNegativeIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		t.Fail()
	}
}

// Test logging of float value in default (scientific) format.
func TestLogger_LogFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359e+00" {
		t.Fail()
	}
}

// Test logging of float value in fixed format.
func TestLogger_LogFixedFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	FloatFormat = 'f'
	log.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359" {
		t.Fail()
	}
	// Turn back to default format.
	FloatFormat = 'e'
}

// Test logging of boolean value.
func TestLogger_LogBoolValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", true, "k2", false)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=true k2=false" {
		t.Fail()
	}
}

// Test logging of complex number.
func TestLogger_LogComplexValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", .12345E+5i, "k2", 1.e+0i)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=(0.000000+12345.000000i) k2=(0.000000+1.000000i)" {
		t.Fail()
	}
}

// Test logging of time literal.
func TestLogger_LogTimeValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	value := time.Now()
	valueString := value.Format(TimeLayout)
	defer out.Close()

	log.Log("k", value)

	out.Flush()
	if strings.TrimSpace(output.String()) != fmt.Sprintf("k=%s", valueString) {
		t.Fail()
	}
}

// Test chaining for Add()
func TestLogger_AddMixChained_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Add("k", "value2").Add("k2", 123).Add("k3", 3.14159265359).Log()

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"value2\" k2=123 k3=3.14159265359e+00" {
		t.Fail()
	}
}

// Test log with the context value.
func TestLogger_WithContextPassed_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.With("key1", "value")
	log.Log("key2", "value")

	out.Flush()
	if strings.TrimSpace(output.String()) != `key1="value" key2="value"` {
		t.Fail()
	}
}

// Test log with adding then removing the context.
func TestLogger_WithoutContextPassed_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	// add the context
	log.With("key1", "value")
	// add regular pair
	log.Add("key2", "value")
	// remove the context and flush the record
	log.Without("key1").Log()

	out.Flush()
	if strings.TrimSpace(output.String()) != `key2="value"` {
		t.Fail()
	}
}
<commit_msg>Add test for context reset.<commit_after>package kiwi

/*
Copyright (c) 2016, Alexander I.Grafov aka Axel
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of kvlog nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"
)

/* All tests consists of three parts:

- arrange structures and initialize objects for use in tests
- act on testing object
- check and assert on results

These parts separated by empty lines in each test function.
*/

var (
	sampleMixContext = []interface{}{"context1", "value", "context2", 1, "context3", 0.1, "context4", []string{"the", "sample"}}
	sampleMixRecord  = []interface{}{"key1", "value", "key2", 2, 3, 4, "common", []string{"the", "sample"}}
)

// Get records from logger. Helper for testing.
func (l *Logger) getRecords() []pair {
	return l.pairs
}

// Get context from logger. Helper for testing.
func (l *Logger) getContext() []pair {
	return l.context
}

func TestNewLogger(t *testing.T) {
	l := New()

	if l == nil {
		t.Fatal("initalized logger is nil")
	}
}

// Test logging of string value.
func TestLogger_LogStringValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", "The sample string with a lot of spaces.")

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		t.Fail()
	}
}

// Test logging of byte array.
func TestLogger_LogBytesValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", []byte("The sample string with a lot of spaces."))

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"The sample string with a lot of spaces.\"" {
		t.Fail()
	}
}

// Test logging of integer value.
func TestLogger_LogIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		t.Fail()
	}
}

// Test logging of negative integer value.
func TestLogger_LogNegativeIntValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", 123)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=123" {
		t.Fail()
	}
}

// Test logging of float value in default (scientific) format.
func TestLogger_LogFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359e+00" {
		t.Fail()
	}
}

// Test logging of float value in fixed format.
func TestLogger_LogFixedFloatValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	FloatFormat = 'f'
	log.Log("k", 3.14159265359)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=3.14159265359" {
		t.Fail()
	}
	// Turn back to default format.
	FloatFormat = 'e'
}

// Test logging of boolean value.
func TestLogger_LogBoolValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", true, "k2", false)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=true k2=false" {
		t.Fail()
	}
}

// Test logging of complex number.
func TestLogger_LogComplexValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Log("k", .12345E+5i, "k2", 1.e+0i)

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=(0.000000+12345.000000i) k2=(0.000000+1.000000i)" {
		t.Fail()
	}
}

// Test logging of time literal.
func TestLogger_LogTimeValue_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	value := time.Now()
	valueString := value.Format(TimeLayout)
	defer out.Close()

	log.Log("k", value)

	out.Flush()
	if strings.TrimSpace(output.String()) != fmt.Sprintf("k=%s", valueString) {
		t.Fail()
	}
}

// Test chaining for Add()
func TestLogger_AddMixChained_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.Add("k", "value2").Add("k2", 123).Add("k3", 3.14159265359).Log()

	out.Flush()
	if strings.TrimSpace(output.String()) != "k=\"value2\" k2=123 k3=3.14159265359e+00" {
		t.Fail()
	}
}

// Test log with the context value.
func TestLogger_WithContextPassed_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	log.With("key1", "value")
	log.Log("key2", "value")

	out.Flush()
	if strings.TrimSpace(output.String()) != `key1="value" key2="value"` {
		t.Fail()
	}
}

// Test log with adding then removing the context.
func TestLogger_WithoutContextPassed_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	// add the context
	log.With("key1", "value")
	// add regular pair
	log.Add("key2", "value")
	// remove the context and flush the record
	log.Without("key1").Log()

	out.Flush()
	if strings.TrimSpace(output.String()) != `key2="value"` {
		t.Fail()
	}
}

// Test log with adding then reset the context.
func TestLogger_ResetContext_Logfmt(t *testing.T) {
	output := bytes.NewBufferString("")
	log := New()
	out := SinkTo(output, UseLogfmt()).Start()
	defer out.Close()

	// add the context
	log.With("key1", "value")
	// add regular pair
	log.Add("key2", "value")
	// reset the context and flush the record
	log.ResetContext().Log()

	out.Flush()
	if strings.TrimSpace(output.String()) != `key2="value"` {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package app

import (
	"encoding/json"
	"fmt"

	"github.com/urfave/cli"

	"github.com/appPlant/alpinepass/src/filters"
	"github.com/appPlant/alpinepass/src/io"
	"github.com/appPlant/alpinepass/src/util"
	"github.com/appPlant/alpinepass/src/validation"
)

//execute reads the input, filters it and writes the output.
func execute(context *cli.Context) error {
	configs := io.ReadConfigs(context.GlobalString("input"))
	configs = filters.FilterConfigs(configs, context)

	validation.Validate(configs)

	if context.GlobalBool("display") {
		var configsJSON []byte
		var err error
		if context.GlobalBool("readable") {
			configsJSON, err = json.MarshalIndent(configs, "", "    ")
		} else {
			configsJSON, err = json.Marshal(configs)
		}
		util.CheckError(err)
		fmt.Println(string(configsJSON))
	} else {
		io.WriteJSON(context.GlobalString("output"), configs, context.GlobalBool("readable"))
	}

	return nil
}
<commit_msg>Allow skipping the validation with the -s flag<commit_after>package app

import (
	"encoding/json"
	"fmt"

	"github.com/urfave/cli"

	"github.com/appPlant/alpinepass/src/filters"
	"github.com/appPlant/alpinepass/src/io"
	"github.com/appPlant/alpinepass/src/util"
	"github.com/appPlant/alpinepass/src/validation"
)

//execute reads the input, filters it and writes the output.
func execute(context *cli.Context) error {
	configs := io.ReadConfigs(context.GlobalString("input"))
	configs = filters.FilterConfigs(configs, context)

	if !context.GlobalBool("skip") {
		validation.Validate(configs)
	}

	if context.GlobalBool("display") {
		var configsJSON []byte
		var err error
		if context.GlobalBool("readable") {
			configsJSON, err = json.MarshalIndent(configs, "", "    ")
		} else {
			configsJSON, err = json.Marshal(configs)
		}
		util.CheckError(err)
		fmt.Println(string(configsJSON))
	} else {
		io.WriteJSON(context.GlobalString("output"), configs, context.GlobalBool("readable"))
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 network

import (
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
	"k8s.io/kubernetes/test/e2e/upgrades"

	"github.com/onsi/ginkgo"
)

// ServiceUpgradeTest tests that a service is available before and
// after a cluster upgrade. During a master-only upgrade, it will test
// that a service remains available during the upgrade.
type ServiceUpgradeTest struct {
	jig          *e2eservice.TestJig
	tcpService   *v1.Service
	tcpIngressIP string
	svcPort      int
}

// Name returns the tracking name of the test.
func (ServiceUpgradeTest) Name() string { return "service-upgrade" }

func shouldTestPDBs() bool { return framework.ProviderIs("gce", "gke") }

// Setup creates a service with a load balancer and makes sure it's reachable.
func (t *ServiceUpgradeTest) Setup(f *framework.Framework) {
	serviceName := "service-test"
	jig := e2eservice.NewTestJig(f.ClientSet, f.Namespace.Name, serviceName)

	ns := f.Namespace
	cs := f.ClientSet

	ginkgo.By("creating a TCP service " + serviceName + " with type=LoadBalancer in namespace " + ns.Name)
	_, err := jig.CreateTCPService(func(s *v1.Service) {
		s.Spec.Type = v1.ServiceTypeLoadBalancer
	})
	framework.ExpectNoError(err)
	tcpService, err := jig.WaitForLoadBalancer(e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
	framework.ExpectNoError(err)

	// Get info to hit it with
	tcpIngressIP := e2eservice.GetIngressPoint(&tcpService.Status.LoadBalancer.Ingress[0])
	svcPort := int(tcpService.Spec.Ports[0].Port)

	ginkgo.By("creating pod to be part of service " + serviceName)
	rc, err := jig.Run(jig.AddRCAntiAffinity)
	framework.ExpectNoError(err)

	if shouldTestPDBs() {
		ginkgo.By("creating a PodDisruptionBudget to cover the ReplicationController")
		_, err = jig.CreatePDB(rc)
		framework.ExpectNoError(err)
	}

	// Hit it once before considering ourselves ready
	ginkgo.By("hitting the pod through the service's LoadBalancer")
	timeout := e2eservice.LoadBalancerLagTimeoutDefault
	if framework.ProviderIs("aws") {
		timeout = e2eservice.LoadBalancerLagTimeoutAWS
	}
	e2eservice.TestReachableHTTP(tcpIngressIP, svcPort, timeout)

	t.jig = jig
	t.tcpService = tcpService
	t.tcpIngressIP = tcpIngressIP
	t.svcPort = svcPort
}

// Test runs a connectivity check to the service.
func (t *ServiceUpgradeTest) Test(f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
	switch upgrade {
	case upgrades.MasterUpgrade, upgrades.ClusterUpgrade:
		t.test(f, done, true, true)
	case upgrades.NodeUpgrade:
		// Node upgrades should test during disruption only on GCE/GKE for now.
		t.test(f, done, shouldTestPDBs(), false)
	default:
		t.test(f, done, false, false)
	}
}

// Teardown cleans up any remaining resources.
func (t *ServiceUpgradeTest) Teardown(f *framework.Framework) {
	// rely on the namespace deletion to clean up everything
}

func (t *ServiceUpgradeTest) test(f *framework.Framework, done <-chan struct{}, testDuringDisruption, testFinalizer bool) {
	if testDuringDisruption {
		// Continuous validation
		ginkgo.By("continuously hitting the pod through the service's LoadBalancer")
		wait.Until(func() {
			e2eservice.TestReachableHTTP(t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
		}, framework.Poll, done)
	} else {
		// Block until upgrade is done
		ginkgo.By("waiting for upgrade to finish without checking if service remains up")
		<-done
	}

	// Hit it once more
	ginkgo.By("hitting the pod through the service's LoadBalancer")
	e2eservice.TestReachableHTTP(t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
	if testFinalizer {
		defer func() {
			ginkgo.By("Check that service can be deleted with finalizer")
			e2eservice.WaitForServiceDeletedWithFinalizer(t.jig.Client, t.tcpService.Namespace, t.tcpService.Name)
		}()
		ginkgo.By("Check that finalizer is present on loadBalancer type service")
		e2eservice.WaitForServiceUpdatedWithFinalizer(t.jig.Client, t.tcpService.Namespace, t.tcpService.Name, true)
	}
}
<commit_msg>UPSTREAM: <carry>: Always test PDB's during service upgrade test<commit_after>/*
Copyright 2017 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 network

import (
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
	"k8s.io/kubernetes/test/e2e/upgrades"

	"github.com/onsi/ginkgo"
)

// ServiceUpgradeTest tests that a service is available before and
// after a cluster upgrade. During a master-only upgrade, it will test
// that a service remains available during the upgrade.
type ServiceUpgradeTest struct {
	jig          *e2eservice.TestJig
	tcpService   *v1.Service
	tcpIngressIP string
	svcPort      int
}

// Name returns the tracking name of the test.
func (ServiceUpgradeTest) Name() string { return "service-upgrade" }

func shouldTestPDBs() bool { return true }

// Setup creates a service with a load balancer and makes sure it's reachable.
func (t *ServiceUpgradeTest) Setup(f *framework.Framework) {
	serviceName := "service-test"
	jig := e2eservice.NewTestJig(f.ClientSet, f.Namespace.Name, serviceName)

	ns := f.Namespace
	cs := f.ClientSet

	ginkgo.By("creating a TCP service " + serviceName + " with type=LoadBalancer in namespace " + ns.Name)
	_, err := jig.CreateTCPService(func(s *v1.Service) {
		s.Spec.Type = v1.ServiceTypeLoadBalancer
	})
	framework.ExpectNoError(err)
	tcpService, err := jig.WaitForLoadBalancer(e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
	framework.ExpectNoError(err)

	// Get info to hit it with
	tcpIngressIP := e2eservice.GetIngressPoint(&tcpService.Status.LoadBalancer.Ingress[0])
	svcPort := int(tcpService.Spec.Ports[0].Port)

	ginkgo.By("creating pod to be part of service " + serviceName)
	rc, err := jig.Run(jig.AddRCAntiAffinity)
	framework.ExpectNoError(err)

	if shouldTestPDBs() {
		ginkgo.By("creating a PodDisruptionBudget to cover the ReplicationController")
		_, err = jig.CreatePDB(rc)
		framework.ExpectNoError(err)
	}

	// Hit it once before considering ourselves ready
	ginkgo.By("hitting the pod through the service's LoadBalancer")
	timeout := e2eservice.LoadBalancerLagTimeoutDefault
	if framework.ProviderIs("aws") {
		timeout = e2eservice.LoadBalancerLagTimeoutAWS
	}
	e2eservice.TestReachableHTTP(tcpIngressIP, svcPort, timeout)

	t.jig = jig
	t.tcpService = tcpService
	t.tcpIngressIP = tcpIngressIP
	t.svcPort = svcPort
}

// Test runs a connectivity check to the service.
func (t *ServiceUpgradeTest) Test(f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
	switch upgrade {
	case upgrades.MasterUpgrade, upgrades.ClusterUpgrade:
		t.test(f, done, true, true)
	case upgrades.NodeUpgrade:
		// Node upgrades should test during disruption only on GCE/GKE for now.
		t.test(f, done, shouldTestPDBs(), false)
	default:
		t.test(f, done, false, false)
	}
}

// Teardown cleans up any remaining resources.
func (t *ServiceUpgradeTest) Teardown(f *framework.Framework) {
	// rely on the namespace deletion to clean up everything
}

func (t *ServiceUpgradeTest) test(f *framework.Framework, done <-chan struct{}, testDuringDisruption, testFinalizer bool) {
	if testDuringDisruption {
		// Continuous validation
		ginkgo.By("continuously hitting the pod through the service's LoadBalancer")
		wait.Until(func() {
			e2eservice.TestReachableHTTP(t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
		}, framework.Poll, done)
	} else {
		// Block until upgrade is done
		ginkgo.By("waiting for upgrade to finish without checking if service remains up")
		<-done
	}

	// Hit it once more
	ginkgo.By("hitting the pod through the service's LoadBalancer")
	e2eservice.TestReachableHTTP(t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
	if testFinalizer {
		defer func() {
			ginkgo.By("Check that service can be deleted with finalizer")
			e2eservice.WaitForServiceDeletedWithFinalizer(t.jig.Client, t.tcpService.Namespace, t.tcpService.Name)
		}()
		ginkgo.By("Check that finalizer is present on loadBalancer type service")
		e2eservice.WaitForServiceUpdatedWithFinalizer(t.jig.Client, t.tcpService.Namespace, t.tcpService.Name, true)
	}
}
<|endoftext|>


<commit_before>package baseftrwapp

import (
	"fmt"
	standardLog "log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/Financial-Times/go-fthealth/v1a"
	log "github.com/Sirupsen/logrus"
	"github.com/cyberdelia/go-metrics-graphite"
	"github.com/gorilla/mux"
	"github.com/rcrowley/go-metrics"
)

// RunServer will set up GET, PUT and DELETE endpoints for the specified path,
// calling the appropriate service functions:
// PUT -> Write
// GET -> Read
// DELETE -> Delete
// It will also setup the healthcheck and ping endpoints
// Endpoints are wrapped in a metrics timer and request loggin including transactionID, which is generated
// if not found on the request as X-Request-Id header
func RunServer(engs map[string]Service, serviceName string, serviceDescription string, port int) {
	//TODO work out how to supply the v1a.Handler as a parameter (so can have several checks)

	m := mux.NewRouter()
	http.Handle("/", m)

	for path, eng := range engs {
		handlers := httpHandlers{eng}
		m.HandleFunc(fmt.Sprintf("/%s/{uuid}", path), handlers.getHandler).Methods("GET")
		m.HandleFunc(fmt.Sprintf("/%s/{uuid}", path), handlers.putHandler).Methods("PUT")
		m.HandleFunc(fmt.Sprintf("/%s/{uuid}", path), handlers.deleteHandler).Methods("DELETE")
	}

	var checks []v1a.Check

	for _, eng := range engs {
		checks = append(checks, eng.Check())
	}

	m.HandleFunc("/__health", v1a.Handler(serviceName, serviceDescription, checks...))
	m.HandleFunc("/__ping", pingHandler)

	go func() {
		log.Printf("listening on %d", port)
		http.ListenAndServe(fmt.Sprintf(":%d", port), HTTPMetricsHandler(TransactionAwareRequestLoggingHandler(os.Stdout, m)))
	}()

	// wait for ctrl-c
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c

	log.Println("exiting")
}

//OutputMetricsIfRequired will send metrics to Graphite if a non-empty graphiteTCPAddress is passed in, or to the standard log if logMetrics is true.
// Make sure a sensible graphitePrefix that will uniquely identify your service is passed in, e.g. "content.test.people.rw.neo4j.ftaps58938-law1a-eu-t
func OutputMetricsIfRequired(graphiteTCPAddress string, graphitePrefix string, logMetrics bool) {
	if graphiteTCPAddress != "" {
		addr, _ := net.ResolveTCPAddr("tcp", graphiteTCPAddress)
		go graphite.Graphite(metrics.DefaultRegistry, 1*time.Minute, graphitePrefix, addr)
	}
	if logMetrics { //useful locally
		//messy use of the 'standard' log package here as this method takes the log struct, not an interface, so can't use logrus.Logger
		go metrics.Log(metrics.DefaultRegistry, 60*time.Second, standardLog.New(os.Stdout, "metrics", standardLog.Lmicroseconds))
	}
}

// Healthcheck defines the information needed to set up a healthcheck
type Healthcheck struct {
	Name        string
	Description string
	Checks      []v1a.Check
	Parallel    bool
}
<commit_msg>Change graphite logging to happen every five seconds<commit_after>package baseftrwapp

import (
	"fmt"
	standardLog "log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"github.com/Financial-Times/go-fthealth/v1a"
	log "github.com/Sirupsen/logrus"
	"github.com/cyberdelia/go-metrics-graphite"
	"github.com/gorilla/mux"
	"github.com/rcrowley/go-metrics"
)

// RunServer will set up GET, PUT and DELETE endpoints for the specified path,
// calling the appropriate service functions:
// PUT -> Write
// GET -> Read
// DELETE -> Delete
// It will also setup the healthcheck and ping endpoints
// Endpoints are wrapped in a metrics timer and request loggin including transactionID, which is generated
// if not found on the request as X-Request-Id header
func RunServer(engs map[string]Service, serviceName string, serviceDescription string, port int) {
	//TODO work out how to supply the v1a.Handler as a parameter (so can have several checks)

	m := mux.NewRouter()
	http.Handle("/", m)

	for path, eng := range engs {
		handlers := httpHandlers{eng}
		m.HandleFunc(fmt.Sprintf("/%s/{uuid}", path), handlers.getHandler).Methods("GET")
		m.HandleFunc(fmt.Sprintf("/%s/{uuid}", path), handlers.putHandler).Methods("PUT")
		m.HandleFunc(fmt.Sprintf("/%s/{uuid}", path), handlers.deleteHandler).Methods("DELETE")
	}

	var checks []v1a.Check

	for _, eng := range engs {
		checks = append(checks, eng.Check())
	}

	m.HandleFunc("/__health", v1a.Handler(serviceName, serviceDescription, checks...))
	m.HandleFunc("/__ping", pingHandler)

	go func() {
		log.Printf("listening on %d", port)
		http.ListenAndServe(fmt.Sprintf(":%d", port), HTTPMetricsHandler(TransactionAwareRequestLoggingHandler(os.Stdout, m)))
	}()

	// wait for ctrl-c
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c

	log.Println("exiting")
}

//OutputMetricsIfRequired will send metrics to Graphite if a non-empty graphiteTCPAddress is passed in, or to the standard log if logMetrics is true.
// Make sure a sensible graphitePrefix that will uniquely identify your service is passed in, e.g. "content.test.people.rw.neo4j.ftaps58938-law1a-eu-t
func OutputMetricsIfRequired(graphiteTCPAddress string, graphitePrefix string, logMetrics bool) {
	if graphiteTCPAddress != "" {
		addr, _ := net.ResolveTCPAddr("tcp", graphiteTCPAddress)
		go graphite.Graphite(metrics.DefaultRegistry, 5*time.Second, graphitePrefix, addr)
	}
	if logMetrics { //useful locally
		//messy use of the 'standard' log package here as this method takes the log struct, not an interface, so can't use logrus.Logger
		go metrics.Log(metrics.DefaultRegistry, 60*time.Second, standardLog.New(os.Stdout, "metrics", standardLog.Lmicroseconds))
	}
}

// Healthcheck defines the information needed to set up a healthcheck
type Healthcheck struct {
	Name        string
	Description string
	Checks      []v1a.Check
	Parallel    bool
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	fg "github.com/audrenbdb/goforeground"
	"github.com/go-vgo/robotgo"
	ps "github.com/mitchellh/go-ps"
	"os"
	"runtime"
	"strings"
	//"github.com/lxn/win"  I can't get this to be useful.
	//w32 "github.com/gonutz/w32/v2"  I also can't get this to be useful.
)

/*
  HISTORY
  -------
  8 Jun 22 -- Started playing w/ this.  This will take a while, as I have SIR in Boston soon.
  10 June 22 -- Seems to be mostly working.  Tomorrow going to Boston.

*/

const lastModified = "June 10, 2022"

var verboseFlag bool
var pid int
var target string

type pet struct {
	pid   int32
	exec  string
	title string
}

func main() {
	fmt.Printf("newclickgo is my attempt to use Go to activate a process so I can click on the screen.  Last modified %s.  Compiled by %s \n",
		lastModified, runtime.Version())

	flag.BoolVar(&verboseFlag, "v", false, " Verbose flag")
	flag.StringVar(&target, "target", "", " Process name search target")
	flag.Parse()

	target = strings.ToLower(target)

	processes, err := ps.Processes()
	if err != nil {
		fmt.Printf(" Error from ps.Processes is %v.  Exiting \n", err)
		os.Exit(1)
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	for i := range processes {
		fmt.Printf("i = %d, name = %q, PID = %d, PPID = %d.\n", i, processes[i].Executable(), processes[i].Pid(), processes[i].PPid())
		processNameLower := strings.ToLower(processes[i].Executable())
		if target != "" && strings.Contains(processNameLower, target) {
			pid = processes[i].Pid()
			if !verboseFlag { // if verbose, show all processes even after find a match w/ target.
				break
			}
		}
	}

	fmt.Printf(" Target is %q, matched pid = %d.\n", target, pid)

	if pid != 0 { // pid == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(pid)
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	pause()

	pets := make([]pet, 0, len(processes))
	for i := range processes {
		piD := int32(processes[i].Pid())
		apet := pet{ // meaning a pet
			pid:   piD,
			exec:  strings.ToLower(processes[i].Executable()),
			title: robotgo.GetTitle(piD),
		}
		pets = append(pets, apet)
	}

	fmt.Println(robotgo.GetTitle())
	ids, er := robotgo.FindIds("")
	if er != nil {
		fmt.Printf(" Error from robotgo FindIds is %v.  Exiting\n")
		os.Exit(1)
	}
	name, _ := robotgo.FindName(ids[100])
	fmt.Printf(" robotgo GetTitle for id[%d], title is %q, and name is %q\n", ids[100], robotgo.GetTitle(ids[100]), name)

	fmt.Printf(" Will now show you my pets.\n")
	pause()

	for _, peT := range pets {
		fmt.Printf(" PID=%d, exe=%q, Title=%q\n", peT.pid, peT.exec, peT.title)
	}
	fmt.Printf(" There are %d pets and %d processes.\n", len(pets), len(processes))

	pause()

	var piD int32
	for _, peT := range pets {
		if target != "" && (strings.Contains(peT.title, target) || strings.Contains(peT.exec, target)) {
			piD = peT.pid
			if !verboseFlag { // if verbose, show all processes even after find a match w/ target.
				break
			}
		}
	}

	if piD != 0 { // piD == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(int(piD))
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}

}

// --------------------------------------------------------------------------------------------

func pause() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print(" Pausing.  Hit <enter> to continue  ")
	scanner.Scan()
	_ = scanner.Text()
}
<commit_msg>06/11/2022 10:11:22 newclickgo/newclickgo.go<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	fg "github.com/audrenbdb/goforeground"
	"github.com/go-vgo/robotgo"
	ps "github.com/mitchellh/go-ps"
	"os"
	"runtime"
	"strings"
	//"github.com/lxn/win"  I can't get this to be useful.
	//w32 "github.com/gonutz/w32/v2"  I also can't get this to be useful.
)

/*
  HISTORY
  -------
  8 Jun 22 -- Started playing w/ this.  This will take a while, as I have SIR in Boston soon.
  10 June 22 -- Seems to be mostly working.  Tomorrow going to Boston.

*/

const lastModified = "June 11, 2022"

var verboseFlag bool
var pid int
var target string

type pet struct {
	pid   int32
	exec  string
	title string
}

func main() {
	fmt.Printf("newclickgo to use Go to activate a process so can be clicked on the screen.  Last modified %s.  Compiled by %s \n",
		lastModified, runtime.Version())

	flag.BoolVar(&verboseFlag, "v", false, " Verbose flag")
	flag.StringVar(&target, "target", "", " Process name search target")
	flag.Parse()

	target = strings.ToLower(target)

	processes, err := ps.Processes()
	if err != nil {
		fmt.Printf(" Error from ps.Processes is %v.  Exiting \n", err)
		os.Exit(1)
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	for i := range processes {
		//fmt.Printf("i = %d, name = %q, PID = %d, PPID = %d.\n", i, processes[i].Executable(), processes[i].Pid(), processes[i].PPid())
		processNameLower := strings.ToLower(processes[i].Executable())
		if target != "" && strings.Contains(processNameLower, target) {
			pid = processes[i].Pid()
			fmt.Printf(" Matching process index = %d, pid = %d, PID() = %d, name = %q\n",
				i, pid, processes[i].Pid(), processes[i].Executable())
			break
		}
	}

	fmt.Printf(" Target is %q, matched pid = %d.\n", target, pid)
	pause()

	if pid != 0 { // pid == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(pid)
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	pause()

	pets := make([]pet, 0, len(processes))
	for i := range processes {
		piD := int32(processes[i].Pid())
		apet := pet{ // meaning a pet
			pid:   piD,
			exec:  strings.ToLower(processes[i].Executable()),
			title: robotgo.GetTitle(piD),
		}
		pets = append(pets, apet)
	}

	fmt.Println(robotgo.GetTitle())
	ids, er := robotgo.FindIds("")
	if er != nil {
		fmt.Printf(" Error from robotgo FindIds is %v.  Exiting\n")
		os.Exit(1)
	}
	name, _ := robotgo.FindName(ids[100])
	fmt.Printf(" robotgo GetTitle for id[%d], title is %q, and name is %q\n", ids[100], robotgo.GetTitle(ids[100]), name)

	fmt.Printf(" Will now show you my pets.\n")
	pause()

	for i, peT := range pets {
		fmt.Printf(" i=%d; pet: PID=%d, exe=%q, Title=%q; processes pid = %d, name = %q\n",
			i, peT.pid, peT.exec, peT.title, processes[i].Pid(), processes[i].Executable())
		if i%40 == 0 {
			pause()
		}
	}
	fmt.Printf(" There are %d pets and %d processes.\n", len(pets), len(processes))

	pause()

	var piD int32
	var index int
	for i, peT := range pets {
		if target != "" && (strings.Contains(peT.title, target) || strings.Contains(peT.exec, target)) {
			piD = peT.pid
			index = i
			fmt.Printf(" index = %d, target = %q matches pet PID of %d.  Corresponding processes PID = %d, title = %q, name = %q\n",
				index, target, piD, processes[i].Pid(), peT.title, peT.exec)
			break
		}
	}

	if piD != 0 { // piD == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(int(piD))
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}
	pause()
}

// --------------------------------------------------------------------------------------------

func pause() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print(" Pausing.  Hit <enter> to continue  ")
	scanner.Scan()
	_ = scanner.Text()
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

// CustomHostnameStatus is the enumeration of valid state values in the CustomHostnameSSL
type CustomHostnameStatus string

const (
	// PENDING status represents state of CustomHostname is pending.
	PENDING CustomHostnameStatus = "pending"
	// ACTIVE status represents state of CustomHostname is active.
	ACTIVE CustomHostnameStatus = "active"
	// MOVED status represents state of CustomHostname is moved.
	MOVED CustomHostnameStatus = "moved"
	// DELETED status represents state of CustomHostname is removed.
	DELETED CustomHostnameStatus = "deleted"
)

// CustomHostnameSSLSettings represents the SSL settings for a custom hostname.
type CustomHostnameSSLSettings struct {
	HTTP2         string   `json:"http2,omitempty"`
	TLS13         string   `json:"tls_1_3,omitempty"`
	MinTLSVersion string   `json:"min_tls_version,omitempty"`
	Ciphers       []string `json:"ciphers,omitempty"`
}

//CustomHostnameOwnershipVerification represents ownership verification status of a given custom hostname.
type CustomHostnameOwnershipVerification struct {
	Type  string `json:"type,omitempty"`
	Name  string `json:"name,omitempty"`
	Value string `json:"value,omitempty"`
}

//CustomHostnameSSLValidationErrors represents errors that occurred during SSL validation.
type CustomHostnameSSLValidationErrors struct {
	Message string `json:"message,omitempty"`
}

// CustomHostnameSSL represents the SSL section in a given custom hostname.
type CustomHostnameSSL struct {
	ID                   string                              `json:"id,omitempty"`
	Status               string                              `json:"status,omitempty"`
	Method               string                              `json:"method,omitempty"`
	Type                 string                              `json:"type,omitempty"`
	CnameTarget          string                              `json:"cname_target,omitempty"`
	CnameName            string                              `json:"cname,omitempty"`
	Wildcard             *bool                               `json:"wildcard,omitempty"`
	CustomCertificate    string                              `json:"custom_certificate,omitempty"`
	CustomKey            string                              `json:"custom_key,omitempty"`
	CertificateAuthority string                              `json:"certificate_authority,omitempty"`
	Issuer               string                              `json:"issuer,omitempty"`
	SerialNumber         string                              `json:"serial_number,omitempty"`
	Settings             CustomHostnameSSLSettings           `json:"settings,omitempty"`
	ValidationErrors     []CustomHostnameSSLValidationErrors `json:"validation_errors,omitempty"`
	HTTPUrl              string                              `json:"http_url,omitempty"`
	HTTPBody             string                              `json:"http_body,omitempty"`
}

// CustomMetadata defines custom metadata for the hostname. This requires logic to be implemented by Cloudflare to act on the data provided.
type CustomMetadata map[string]interface{}

// CustomHostname represents a custom hostname in a zone.
type CustomHostname struct {
	ID                        string                                  `json:"id,omitempty"`
	Hostname                  string                                  `json:"hostname,omitempty"`
	CustomOriginServer        string                                  `json:"custom_origin_server,omitempty"`
	SSL                       *CustomHostnameSSL                      `json:"ssl,omitempty"`
	CustomMetadata            CustomMetadata                          `json:"custom_metadata,omitempty"`
	Status                    CustomHostnameStatus                    `json:"status,omitempty"`
	VerificationErrors        []string                                `json:"verification_errors,omitempty"`
	OwnershipVerification     CustomHostnameOwnershipVerification     `json:"ownership_verification,omitempty"`
	OwnershipVerificationHTTP CustomHostnameOwnershipVerificationHTTP `json:"ownership_verification_http,omitempty"`
	CreatedAt                 *time.Time                              `json:"created_at,omitempty"`
}

// CustomHostnameOwnershipVerificationHTTP represents a response from the Custom Hostnames endpoints.
type CustomHostnameOwnershipVerificationHTTP struct {
	HTTPUrl  string `json:"http_url,omitempty"`
	HTTPBody string `json:"http_body,omitempty"`
}

// CustomHostnameResponse represents a response from the Custom Hostnames endpoints.
type CustomHostnameResponse struct {
	Result CustomHostname `json:"result"`
	Response
}

// CustomHostnameListResponse represents a response from the Custom Hostnames endpoints.
type CustomHostnameListResponse struct {
	Result []CustomHostname `json:"result"`
	Response
	ResultInfo `json:"result_info"`
}

// CustomHostnameFallbackOrigin represents a Custom Hostnames Fallback Origin
type CustomHostnameFallbackOrigin struct {
	Origin string   `json:"origin,omitempty"`
	Status string   `json:"status,omitempty"`
	Errors []string `json:"errors,omitempty"`
}

// CustomHostnameFallbackOriginResponse represents a response from the Custom Hostnames Fallback Origin endpoint.
type CustomHostnameFallbackOriginResponse struct {
	Result CustomHostnameFallbackOrigin `json:"result"`
	Response
}

// UpdateCustomHostnameSSL modifies SSL configuration for the given custom
// hostname in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
func (api *API) UpdateCustomHostnameSSL(ctx context.Context, zoneID string, customHostnameID string, ssl *CustomHostnameSSL) (*CustomHostnameResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	ch := CustomHostname{
		SSL: ssl,
	}
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, ch)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return response, nil
}

// UpdateCustomHostname modifies configuration for the given custom
// hostname in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
func (api *API) UpdateCustomHostname(ctx context.Context, zoneID string, customHostnameID string, ch CustomHostname) (*CustomHostnameResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, ch)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return response, nil
}

// DeleteCustomHostname deletes a custom hostname (and any issued SSL
// certificates).
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-delete-a-custom-hostname-and-any-issued-ssl-certificates-
func (api *API) DeleteCustomHostname(ctx context.Context, zoneID string, customHostnameID string) error {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}

	return nil
}

// CreateCustomHostname creates a new custom hostname and requests that an SSL certificate be issued for it.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-create-custom-hostname
func (api *API) CreateCustomHostname(ctx context.Context, zoneID string, ch CustomHostname) (*CustomHostnameResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, ch)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// CustomHostnames fetches custom hostnames for the given zone,
// by applying filter.Hostname if not empty and scoping the result to page'th 50 items.
//
// The returned ResultInfo can be used to implement pagination.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-list-custom-hostnames
func (api *API) CustomHostnames(ctx context.Context, zoneID string, page int, filter CustomHostname) ([]CustomHostname, ResultInfo, error) {
	v := url.Values{}
	v.Set("per_page", "50")
	v.Set("page", strconv.Itoa(page))
	if filter.Hostname != "" {
		v.Set("hostname", filter.Hostname)
	}

	uri := fmt.Sprintf("/zones/%s/custom_hostnames?%s", zoneID, v.Encode())
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []CustomHostname{}, ResultInfo{}, err
	}
	var customHostnameListResponse CustomHostnameListResponse
	err = json.Unmarshal(res, &customHostnameListResponse)
	if err != nil {
		return []CustomHostname{}, ResultInfo{}, err
	}

	return customHostnameListResponse.Result, customHostnameListResponse.ResultInfo, nil
}

// CustomHostname inspects the given custom hostname in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-custom-hostname-configuration-details
func (api *API) CustomHostname(ctx context.Context, zoneID string, customHostnameID string) (CustomHostname, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return CustomHostname{}, err
	}

	var response CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return CustomHostname{}, errors.Wrap(err, errUnmarshalError)
	}

	return response.Result, nil
}

// CustomHostnameIDByName retrieves the ID for the given hostname in the given zone.
func (api *API) CustomHostnameIDByName(ctx context.Context, zoneID string, hostname string) (string, error) {
	customHostnames, _, err := api.CustomHostnames(ctx, zoneID, 1, CustomHostname{Hostname: hostname})
	if err != nil {
		return "", errors.Wrap(err, "CustomHostnames command failed")
	}
	for _, ch := range customHostnames {
		if ch.Hostname == hostname {
			return ch.ID, nil
		}
	}
	return "", errors.New("CustomHostname could not be found")
}

// UpdateCustomHostnameFallbackOrigin modifies the Custom Hostname Fallback origin in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-fallback-origin-for-a-zone-update-fallback-origin-for-custom-hostnames
func (api *API) UpdateCustomHostnameFallbackOrigin(ctx context.Context, zoneID string, chfo CustomHostnameFallbackOrigin) (*CustomHostnameFallbackOriginResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/fallback_origin", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, chfo)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameFallbackOriginResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return response, nil
}

// DeleteCustomHostnameFallbackOrigin deletes the Custom Hostname Fallback origin in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-fallback-origin-for-a-zone-delete-fallback-origin-for-custom-hostnames
func (api *API) DeleteCustomHostnameFallbackOrigin(ctx context.Context, zoneID string) error {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/fallback_origin", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return err
	}

	var response *CustomHostnameFallbackOriginResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}

// CustomHostnameFallbackOrigin inspects the Custom Hostname Fallback origin in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-fallback-origin-for-a-zone-properties
func (api *API) CustomHostnameFallbackOrigin(ctx context.Context, zoneID string) (CustomHostnameFallbackOrigin, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/fallback_origin", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return CustomHostnameFallbackOrigin{}, err
	}

	var response CustomHostnameFallbackOriginResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return CustomHostnameFallbackOrigin{}, errors.Wrap(err, errUnmarshalError)
	}

	return response.Result, nil
}
<commit_msg>Add txt_name & txt_value fields (DNS validation) to CustomHostname struct<commit_after>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/pkg/errors"
)

// CustomHostnameStatus is the enumeration of valid state values in the CustomHostnameSSL
type CustomHostnameStatus string

const (
	// PENDING status represents state of CustomHostname is pending.
	PENDING CustomHostnameStatus = "pending"
	// ACTIVE status represents state of CustomHostname is active.
	ACTIVE CustomHostnameStatus = "active"
	// MOVED status represents state of CustomHostname is moved.
	MOVED CustomHostnameStatus = "moved"
	// DELETED status represents state of CustomHostname is removed.
	DELETED CustomHostnameStatus = "deleted"
)

// CustomHostnameSSLSettings represents the SSL settings for a custom hostname.
type CustomHostnameSSLSettings struct {
	HTTP2         string   `json:"http2,omitempty"`
	TLS13         string   `json:"tls_1_3,omitempty"`
	MinTLSVersion string   `json:"min_tls_version,omitempty"`
	Ciphers       []string `json:"ciphers,omitempty"`
}

//CustomHostnameOwnershipVerification represents ownership verification status of a given custom hostname.
type CustomHostnameOwnershipVerification struct {
	Type  string `json:"type,omitempty"`
	Name  string `json:"name,omitempty"`
	Value string `json:"value,omitempty"`
}

//CustomHostnameSSLValidationErrors represents errors that occurred during SSL validation.
type CustomHostnameSSLValidationErrors struct {
	Message string `json:"message,omitempty"`
}

// CustomHostnameSSL represents the SSL section in a given custom hostname.
type CustomHostnameSSL struct {
	ID                   string                              `json:"id,omitempty"`
	Status               string                              `json:"status,omitempty"`
	Method               string                              `json:"method,omitempty"`
	Type                 string                              `json:"type,omitempty"`
	CnameTarget          string                              `json:"cname_target,omitempty"`
	CnameName            string                              `json:"cname,omitempty"`
	TxtName              string                              `json:"txt_name,omitempty"`
	TxtValue             string                              `json:"txt_value,omitempty"`
	Wildcard             *bool                               `json:"wildcard,omitempty"`
	CustomCertificate    string                              `json:"custom_certificate,omitempty"`
	CustomKey            string                              `json:"custom_key,omitempty"`
	CertificateAuthority string                              `json:"certificate_authority,omitempty"`
	Issuer               string                              `json:"issuer,omitempty"`
	SerialNumber         string                              `json:"serial_number,omitempty"`
	Settings             CustomHostnameSSLSettings           `json:"settings,omitempty"`
	ValidationErrors     []CustomHostnameSSLValidationErrors `json:"validation_errors,omitempty"`
	HTTPUrl              string                              `json:"http_url,omitempty"`
	HTTPBody             string                              `json:"http_body,omitempty"`
}

// CustomMetadata defines custom metadata for the hostname. This requires logic to be implemented by Cloudflare to act on the data provided.
type CustomMetadata map[string]interface{}

// CustomHostname represents a custom hostname in a zone.
type CustomHostname struct {
	ID                        string                                  `json:"id,omitempty"`
	Hostname                  string                                  `json:"hostname,omitempty"`
	CustomOriginServer        string                                  `json:"custom_origin_server,omitempty"`
	SSL                       *CustomHostnameSSL                      `json:"ssl,omitempty"`
	CustomMetadata            CustomMetadata                          `json:"custom_metadata,omitempty"`
	Status                    CustomHostnameStatus                    `json:"status,omitempty"`
	VerificationErrors        []string                                `json:"verification_errors,omitempty"`
	OwnershipVerification     CustomHostnameOwnershipVerification     `json:"ownership_verification,omitempty"`
	OwnershipVerificationHTTP CustomHostnameOwnershipVerificationHTTP `json:"ownership_verification_http,omitempty"`
	CreatedAt                 *time.Time                              `json:"created_at,omitempty"`
}

// CustomHostnameOwnershipVerificationHTTP represents a response from the Custom Hostnames endpoints.
type CustomHostnameOwnershipVerificationHTTP struct {
	HTTPUrl  string `json:"http_url,omitempty"`
	HTTPBody string `json:"http_body,omitempty"`
}

// CustomHostnameResponse represents a response from the Custom Hostnames endpoints.
type CustomHostnameResponse struct {
	Result CustomHostname `json:"result"`
	Response
}

// CustomHostnameListResponse represents a response from the Custom Hostnames endpoints.
type CustomHostnameListResponse struct {
	Result []CustomHostname `json:"result"`
	Response
	ResultInfo `json:"result_info"`
}

// CustomHostnameFallbackOrigin represents a Custom Hostnames Fallback Origin
type CustomHostnameFallbackOrigin struct {
	Origin string   `json:"origin,omitempty"`
	Status string   `json:"status,omitempty"`
	Errors []string `json:"errors,omitempty"`
}

// CustomHostnameFallbackOriginResponse represents a response from the Custom Hostnames Fallback Origin endpoint.
type CustomHostnameFallbackOriginResponse struct {
	Result CustomHostnameFallbackOrigin `json:"result"`
	Response
}

// UpdateCustomHostnameSSL modifies SSL configuration for the given custom
// hostname in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
func (api *API) UpdateCustomHostnameSSL(ctx context.Context, zoneID string, customHostnameID string, ssl *CustomHostnameSSL) (*CustomHostnameResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	ch := CustomHostname{
		SSL: ssl,
	}
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, ch)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return response, nil
}

// UpdateCustomHostname modifies configuration for the given custom
// hostname in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
func (api *API) UpdateCustomHostname(ctx context.Context, zoneID string, customHostnameID string, ch CustomHostname) (*CustomHostnameResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, ch)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return response, nil
}

// DeleteCustomHostname deletes a custom hostname (and any issued SSL
// certificates).
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-delete-a-custom-hostname-and-any-issued-ssl-certificates-
func (api *API) DeleteCustomHostname(ctx context.Context, zoneID string, customHostnameID string) error {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}

	return nil
}

// CreateCustomHostname creates a new custom hostname and requests that an SSL certificate be issued for it.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-create-custom-hostname
func (api *API) CreateCustomHostname(ctx context.Context, zoneID string, ch CustomHostname) (*CustomHostnameResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, ch)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return response, nil
}

// CustomHostnames fetches custom hostnames for the given zone,
// by applying filter.Hostname if not empty and scoping the result to page'th 50 items.
//
// The returned ResultInfo can be used to implement pagination.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-list-custom-hostnames
func (api *API) CustomHostnames(ctx context.Context, zoneID string, page int, filter CustomHostname) ([]CustomHostname, ResultInfo, error) {
	v := url.Values{}
	v.Set("per_page", "50")
	v.Set("page", strconv.Itoa(page))
	if filter.Hostname != "" {
		v.Set("hostname", filter.Hostname)
	}

	uri := fmt.Sprintf("/zones/%s/custom_hostnames?%s", zoneID, v.Encode())
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []CustomHostname{}, ResultInfo{}, err
	}
	var customHostnameListResponse CustomHostnameListResponse
	err = json.Unmarshal(res, &customHostnameListResponse)
	if err != nil {
		return []CustomHostname{}, ResultInfo{}, err
	}

	return customHostnameListResponse.Result, customHostnameListResponse.ResultInfo, nil
}

// CustomHostname inspects the given custom hostname in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-custom-hostname-configuration-details
func (api *API) CustomHostname(ctx context.Context, zoneID string, customHostnameID string) (CustomHostname, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/%s", zoneID, customHostnameID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return CustomHostname{}, err
	}

	var response CustomHostnameResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return CustomHostname{}, errors.Wrap(err, errUnmarshalError)
	}

	return response.Result, nil
}

// CustomHostnameIDByName retrieves the ID for the given hostname in the given zone.
func (api *API) CustomHostnameIDByName(ctx context.Context, zoneID string, hostname string) (string, error) {
	customHostnames, _, err := api.CustomHostnames(ctx, zoneID, 1, CustomHostname{Hostname: hostname})
	if err != nil {
		return "", errors.Wrap(err, "CustomHostnames command failed")
	}
	for _, ch := range customHostnames {
		if ch.Hostname == hostname {
			return ch.ID, nil
		}
	}
	return "", errors.New("CustomHostname could not be found")
}

// UpdateCustomHostnameFallbackOrigin modifies the Custom Hostname Fallback origin in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-fallback-origin-for-a-zone-update-fallback-origin-for-custom-hostnames
func (api *API) UpdateCustomHostnameFallbackOrigin(ctx context.Context, zoneID string, chfo CustomHostnameFallbackOrigin) (*CustomHostnameFallbackOriginResponse, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/fallback_origin", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, chfo)
	if err != nil {
		return nil, err
	}

	var response *CustomHostnameFallbackOriginResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return response, nil
}

// DeleteCustomHostnameFallbackOrigin deletes the Custom Hostname Fallback origin in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-fallback-origin-for-a-zone-delete-fallback-origin-for-custom-hostnames
func (api *API) DeleteCustomHostnameFallbackOrigin(ctx context.Context, zoneID string) error {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/fallback_origin", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
	if err != nil {
		return err
	}

	var response *CustomHostnameFallbackOriginResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}

// CustomHostnameFallbackOrigin inspects the Custom Hostname Fallback origin in the given zone.
//
// API reference: https://api.cloudflare.com/#custom-hostname-fallback-origin-for-a-zone-properties
func (api *API) CustomHostnameFallbackOrigin(ctx context.Context, zoneID string) (CustomHostnameFallbackOrigin, error) {
	uri := fmt.Sprintf("/zones/%s/custom_hostnames/fallback_origin", zoneID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return CustomHostnameFallbackOrigin{}, err
	}

	var response CustomHostnameFallbackOriginResponse
	err = json.Unmarshal(res, &response)
	if err != nil {
		return CustomHostnameFallbackOrigin{}, errors.Wrap(err, errUnmarshalError)
	}

	return response.Result, nil
}
<|endoftext|>


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

package main

import (
	"fmt"
	"net"
	"runtime"
	"time"

	"github.com/ciao-project/ciao/ciao-controller/types"
	"github.com/ciao-project/ciao/payloads"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.State != "exited" {
		return errors.New("You may only restart paused instances")
	}

	w, err := c.ds.GetWorkload(i.WorkloadID)
	if err != nil {
		return err
	}

	t, err := c.ds.GetTenant(i.TenantID)
	if err != nil {
		return err
	}

	if !i.CNCI {
		err = t.CNCIctrl.WaitForActive(i.Subnet)
		if err != nil {
			return errors.Wrap(err, "Error waiting for active subnet")
		}
	}

	go func() {
		if err := c.client.RestartInstance(i, &w, t); err != nil {
			glog.Warningf("Error restarting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return types.ErrInstanceNotAssigned
	}

	if i.State == payloads.ComputeStatusPending {
		return errors.New("You may not stop a pending instance")
	}

	go func() {
		if err := c.client.StopInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error stopping instance: %v", err)
		}
	}()

	return nil
}

// delete an instance, wait for the deleted event.
func (c *controller) deleteInstanceSync(instanceID string) error {
	wait := make(chan struct{})

	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	err = c.deleteInstance(instanceID)
	if err != nil {
		return err
	}

	go func() {
		i.StateChange.L.Lock()
		for {
			i.StateLock.RLock()
			if i.State == payloads.Deleted || i.State == payloads.Hung {
				break
			}
			glog.V(2).Infof("waiting for %s to be deleted", i.ID)
			i.StateLock.RUnlock()
			i.StateChange.Wait()
		}

		i.StateLock.RUnlock()
		i.StateChange.L.Unlock()

		glog.V(2).Infof("%s is hung or deleted", i.ID)
		close(wait)
	}()

	select {
	case <-wait:
		return nil
	case <-time.After(2 * time.Minute):
		err = i.TransitionInstanceState(payloads.Hung)
		if err != nil {
			glog.Warningf("Error transitioning instance to hung state: %v", err)
		}
		return fmt.Errorf("timeout waiting for delete")
	}
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id and the instance is
	// pending we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" && i.State == payloads.Pending {
		return types.ErrInstanceNotAssigned
	}

	// check for any external IPs
	IPs := c.ds.GetMappedIPs(&i.TenantID)
	for _, m := range IPs {
		if m.InstanceID == instanceID {
			return types.ErrInstanceMapped
		}
	}

	go func() {
		if err := c.client.DeleteInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error deleting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) confirmTenantRaw(tenantID string) error {
	tenant, err := c.ds.GetTenant(tenantID)
	if err != nil {
		return err
	}

	if tenant != nil {
		return nil
	}

	// if we are adding tenant this way, we need to use defaults
	config := types.TenantConfig{
		Name:       "",
		SubnetBits: 24,
	}

	tenant, err = c.ds.AddTenant(tenantID, config)
	if err != nil {
		return err
	}

	tenant.CNCIctrl, err = newCNCIManager(c, tenantID)
	if err != nil {
		return err
	}

	return nil
}

func (c *controller) confirmTenant(tenantID string) error {
	c.tenantReadinessLock.Lock()
	memo := c.tenantReadiness[tenantID]
	if memo != nil {

		// Someone else has already or is in the process of confirming
		// this tenant.  We need to wait until memo.ch is closed before
		// continuing.

		c.tenantReadinessLock.Unlock()
		<-memo.ch
		if memo.err != nil {
			return memo.err
		}

		// If we get here we know that confirmTenantRaw has already
		// been successfully called for this tenant during the life
		// time of this controller invocation.

		return nil
	}

	ch := make(chan struct{})
	c.tenantReadiness[tenantID] = &tenantConfirmMemo{ch: ch}
	c.tenantReadinessLock.Unlock()
	err := c.confirmTenantRaw(tenantID)
	if err != nil {
		c.tenantReadinessLock.Lock()
		c.tenantReadiness[tenantID].err = err
		delete(c.tenantReadiness, tenantID)
		c.tenantReadinessLock.Unlock()
	}
	close(ch)
	return err
}

func (c *controller) createInstance(w types.WorkloadRequest, wl types.Workload, name string, newIP net.IP) (*types.Instance, error) {
	startTime := time.Now()

	instance, err := newInstance(c, w.TenantID, &wl, w.Volumes, name, w.Subnet, newIP)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating instance")
	}
	instance.startTime = startTime

	ok, err := instance.Allowed()
	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error checking if instance allowed")
	}

	if !ok {
		_ = instance.Clean()
		return nil, errors.New("Over quota")
	}

	err = instance.Add()
	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error adding instance")
	}

	if w.TraceLabel == "" {
		err = c.client.StartWorkload(instance.newConfig.config)
	} else {
		err = c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, w.TraceLabel)
	}

	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error starting workload")
	}

	return instance.Instance, nil
}

func (c *controller) startWorkload(w types.WorkloadRequest) ([]*types.Instance, error) {
	var e error
	var sem = make(chan int, runtime.NumCPU())

	if w.Instances <= 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(w.WorkloadID)
	if err != nil {
		return nil, err
	}

	var IPPool []net.IP

	// if this is for a CNCI, we don't want to allocate any IPs.
	if w.Subnet == "" {
		IPPool, err = c.ds.AllocateTenantIPPool(w.TenantID, w.Instances)
		if err != nil {
			return nil, err
		}
	}

	var newInstances []*types.Instance
	type result struct {
		instance *types.Instance
		err      error
	}

	errChan := make(chan result)

	for i := 0; i < w.Instances; i++ {
		var newIP net.IP

		if w.Subnet == "" {
			newIP = IPPool[i]
		}

		name := w.Name
		if name != "" {
			if w.Instances > 1 {
				name = fmt.Sprintf("%s-%d", name, i)
			}
		}

		go func(newIP net.IP, name string) {
			sem <- 1
			var err error
			var instance *types.Instance
			defer func() {
				ret := result{
					err:      err,
					instance: instance,
				}
				<-sem
				errChan <- ret
			}()

			instance, err = c.createInstance(w, wl, name, newIP)
			if err != nil {
				err = errors.Wrap(err, "Error creating instance")
				return
			}
		}(newIP, name)
	}

	for i := 0; i < w.Instances; i++ {
		retVal := <-errChan
		if e == nil {
			// return the first error
			e = retVal.err
		}
		newInstances = append(newInstances, retVal.instance)
	}

	return newInstances, e
}

func (c *controller) deleteEphemeralStorage(instanceID string) error {
	attachments := c.ds.GetStorageAttachments(instanceID)
	for _, attachment := range attachments {
		if !attachment.Ephemeral {
			continue
		}
		err := c.ds.DeleteStorageAttachment(attachment.ID)
		if err != nil {
			return errors.Wrap(err, "Error deleting storage attachment from datastore")
		}
		bd, err := c.ds.GetBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error getting block device from datastore")
		}
		err = c.ds.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device from datastore")
		}
		err = c.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device")
		}
		if !bd.Internal {
			c.qs.Release(bd.TenantID,
				payloads.RequestedResource{Type: payloads.Volume, Value: 1},
				payloads.RequestedResource{Type: payloads.SharedDiskGiB, Value: bd.Size})
		}
	}
	return nil
}
<commit_msg>ciao-controller: Don't allow missing instances to be deleted<commit_after>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

package main

import (
	"fmt"
	"net"
	"runtime"
	"time"

	"github.com/ciao-project/ciao/ciao-controller/types"
	"github.com/ciao-project/ciao/payloads"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.State != "exited" {
		return errors.New("You may only restart paused instances")
	}

	w, err := c.ds.GetWorkload(i.WorkloadID)
	if err != nil {
		return err
	}

	t, err := c.ds.GetTenant(i.TenantID)
	if err != nil {
		return err
	}

	if !i.CNCI {
		err = t.CNCIctrl.WaitForActive(i.Subnet)
		if err != nil {
			return errors.Wrap(err, "Error waiting for active subnet")
		}
	}

	go func() {
		if err := c.client.RestartInstance(i, &w, t); err != nil {
			glog.Warningf("Error restarting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return types.ErrInstanceNotAssigned
	}

	if i.State == payloads.ComputeStatusPending {
		return errors.New("You may not stop a pending instance")
	}

	go func() {
		if err := c.client.StopInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error stopping instance: %v", err)
		}
	}()

	return nil
}

// delete an instance, wait for the deleted event.
func (c *controller) deleteInstanceSync(instanceID string) error {
	wait := make(chan struct{})

	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	err = c.deleteInstance(instanceID)
	if err != nil {
		return err
	}

	go func() {
		i.StateChange.L.Lock()
		for {
			i.StateLock.RLock()
			if i.State == payloads.Deleted || i.State == payloads.Hung {
				break
			}
			glog.V(2).Infof("waiting for %s to be deleted", i.ID)
			i.StateLock.RUnlock()
			i.StateChange.Wait()
		}

		i.StateLock.RUnlock()
		i.StateChange.L.Unlock()

		glog.V(2).Infof("%s is hung or deleted", i.ID)
		close(wait)
	}()

	select {
	case <-wait:
		return nil
	case <-time.After(2 * time.Minute):
		err = i.TransitionInstanceState(payloads.Hung)
		if err != nil {
			glog.Warningf("Error transitioning instance to hung state: %v", err)
		}
		return fmt.Errorf("timeout waiting for delete")
	}
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id and the instance is
	// pending we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" && i.State == payloads.Pending {
		return types.ErrInstanceNotAssigned
	}

	if i.State == payloads.Missing {
		return types.ErrInstanceNotAssigned
	}

	// check for any external IPs
	IPs := c.ds.GetMappedIPs(&i.TenantID)
	for _, m := range IPs {
		if m.InstanceID == instanceID {
			return types.ErrInstanceMapped
		}
	}

	go func() {
		if err := c.client.DeleteInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error deleting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) confirmTenantRaw(tenantID string) error {
	tenant, err := c.ds.GetTenant(tenantID)
	if err != nil {
		return err
	}

	if tenant != nil {
		return nil
	}

	// if we are adding tenant this way, we need to use defaults
	config := types.TenantConfig{
		Name:       "",
		SubnetBits: 24,
	}

	tenant, err = c.ds.AddTenant(tenantID, config)
	if err != nil {
		return err
	}

	tenant.CNCIctrl, err = newCNCIManager(c, tenantID)
	if err != nil {
		return err
	}

	return nil
}

func (c *controller) confirmTenant(tenantID string) error {
	c.tenantReadinessLock.Lock()
	memo := c.tenantReadiness[tenantID]
	if memo != nil {

		// Someone else has already or is in the process of confirming
		// this tenant.  We need to wait until memo.ch is closed before
		// continuing.

		c.tenantReadinessLock.Unlock()
		<-memo.ch
		if memo.err != nil {
			return memo.err
		}

		// If we get here we know that confirmTenantRaw has already
		// been successfully called for this tenant during the life
		// time of this controller invocation.

		return nil
	}

	ch := make(chan struct{})
	c.tenantReadiness[tenantID] = &tenantConfirmMemo{ch: ch}
	c.tenantReadinessLock.Unlock()
	err := c.confirmTenantRaw(tenantID)
	if err != nil {
		c.tenantReadinessLock.Lock()
		c.tenantReadiness[tenantID].err = err
		delete(c.tenantReadiness, tenantID)
		c.tenantReadinessLock.Unlock()
	}
	close(ch)
	return err
}

func (c *controller) createInstance(w types.WorkloadRequest, wl types.Workload, name string, newIP net.IP) (*types.Instance, error) {
	startTime := time.Now()

	instance, err := newInstance(c, w.TenantID, &wl, w.Volumes, name, w.Subnet, newIP)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating instance")
	}
	instance.startTime = startTime

	ok, err := instance.Allowed()
	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error checking if instance allowed")
	}

	if !ok {
		_ = instance.Clean()
		return nil, errors.New("Over quota")
	}

	err = instance.Add()
	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error adding instance")
	}

	if w.TraceLabel == "" {
		err = c.client.StartWorkload(instance.newConfig.config)
	} else {
		err = c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, w.TraceLabel)
	}

	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error starting workload")
	}

	return instance.Instance, nil
}

func (c *controller) startWorkload(w types.WorkloadRequest) ([]*types.Instance, error) {
	var e error
	var sem = make(chan int, runtime.NumCPU())

	if w.Instances <= 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(w.WorkloadID)
	if err != nil {
		return nil, err
	}

	var IPPool []net.IP

	// if this is for a CNCI, we don't want to allocate any IPs.
	if w.Subnet == "" {
		IPPool, err = c.ds.AllocateTenantIPPool(w.TenantID, w.Instances)
		if err != nil {
			return nil, err
		}
	}

	var newInstances []*types.Instance
	type result struct {
		instance *types.Instance
		err      error
	}

	errChan := make(chan result)

	for i := 0; i < w.Instances; i++ {
		var newIP net.IP

		if w.Subnet == "" {
			newIP = IPPool[i]
		}

		name := w.Name
		if name != "" {
			if w.Instances > 1 {
				name = fmt.Sprintf("%s-%d", name, i)
			}
		}

		go func(newIP net.IP, name string) {
			sem <- 1
			var err error
			var instance *types.Instance
			defer func() {
				ret := result{
					err:      err,
					instance: instance,
				}
				<-sem
				errChan <- ret
			}()

			instance, err = c.createInstance(w, wl, name, newIP)
			if err != nil {
				err = errors.Wrap(err, "Error creating instance")
				return
			}
		}(newIP, name)
	}

	for i := 0; i < w.Instances; i++ {
		retVal := <-errChan
		if e == nil {
			// return the first error
			e = retVal.err
		}
		newInstances = append(newInstances, retVal.instance)
	}

	return newInstances, e
}

func (c *controller) deleteEphemeralStorage(instanceID string) error {
	attachments := c.ds.GetStorageAttachments(instanceID)
	for _, attachment := range attachments {
		if !attachment.Ephemeral {
			continue
		}
		err := c.ds.DeleteStorageAttachment(attachment.ID)
		if err != nil {
			return errors.Wrap(err, "Error deleting storage attachment from datastore")
		}
		bd, err := c.ds.GetBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error getting block device from datastore")
		}
		err = c.ds.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device from datastore")
		}
		err = c.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device")
		}
		if !bd.Internal {
			c.qs.Release(bd.TenantID,
				payloads.RequestedResource{Type: payloads.Volume, Value: 1},
				payloads.RequestedResource{Type: payloads.SharedDiskGiB, Value: bd.Size})
		}
	}
	return nil
}
<|endoftext|>