<commit_before>package function

import (
	"fmt"
	"reflect"
)

var (
	// ErrNotPointer is returned when the value is not a pointer.
	ErrNotPointer = fmt.Errorf("the value is not a pointer")

	// ErrNotSliceOrArray is returned when the value is not a slice or array.
	ErrNotSliceOrArray = fmt.Errorf("The value is not slice or array")

	// ErrInvalidIndex is returned when the index exceeds over the length of
	// the slice.
	ErrInvalidIndex = fmt.Errorf("the index is exceeds the length of the slice")

	// ErrTypeNotCompatible is returned when the type is not compatible.
	ErrTypeNotCompatible = fmt.Errorf("the type is not compatible")
)

// GetSliceValue returns the ith element of slice.
//
// If slice is not a slice or array type, it will return ErrNotSliceOrArray.
// If the index i exceeds over the length of slice, that's, i>=len(slice),
// it will return ErrInvalidIndex.
//
// For []interface{}, []string and []int, they have already been optimized.
func GetSliceValue(slice interface{}, i int) (interface{}, error) {
	switch s := slice.(type) {
	case nil:
		return nil, ErrNotSliceOrArray
	case []string:
		if len(s) > i {
			return s[i], nil
		}
		return nil, ErrInvalidIndex
	case []int:
		if len(s) > i {
			return s[i], nil
		}
		return nil, ErrInvalidIndex
	case []interface{}:
		if len(s) > i {
			return s[i], nil
		}
		return nil, ErrInvalidIndex
	}

	s := reflect.ValueOf(slice)
	kind := s.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return nil, ErrNotSliceOrArray
	}

	if s.Len() > i {
		return s.Index(i).Interface(), nil
	}

	return nil, ErrInvalidIndex
}

func pullSliceValue(out interface{}, slice interface{}, index int,
	_default interface{}, yes bool) error {

	if out == nil {
		return ErrNotPointer
	}

	_out := reflect.ValueOf(out)
	kind := _out.Kind()
	if kind != reflect.Ptr && kind != reflect.UnsafePointer {
		return ErrNotPointer
	}
	_out = reflect.Indirect(_out)

	_slice := reflect.ValueOf(slice)
	kind = _slice.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return ErrNotSliceOrArray
	}

	var value interface{}
	if _slice.Len() > index {
		value = _slice.Index(index).Interface()
	} else if yes {
		value = _default
	} else {
		return ErrInvalidIndex
	}

	v := reflect.ValueOf(value)
	if v.Kind() != _out.Kind() {
		return ErrTypeNotCompatible
	}

	_out.Set(v)
	return nil
}

// PullSliceValueWithDefault is the same as PullSliceValue,
// but if index >= len(slice), set the value of out to _default.
func PullSliceValueWithDefault(out interface{}, slice interface{}, i int,
	_default interface{}) error {
	return pullSliceValue(out, slice, i, _default, true)
}

// PullSliceValue gets the ith element from the slice and puts it into the
// variable out, then returns nil if successfully.
//
// Return an error If out isn't a pointer, or slice isn't a slice or array type,
// or the length of slice is less than or equal to the index i, or the type of
// slice[i] and the underlying type of out are not compatible.
func PullSliceValue(out interface{}, slice interface{}, i int) error {
	return pullSliceValue(out, slice, i, nil, false)
}

// InSlice returns true if v is in slice, or returns false.
//
// It returns false if slice is not a slice type or the type is not compatible.
//
// For []interface{}, []string and []int, they have already been optimized.
func InSlice(v interface{}, slice interface{}) bool {
	// Optimize the types of []interface{}, []string and []int.
	switch s := slice.(type) {
	case nil:
		return false
	case []interface{}:
		for _, _v := range s {
			if reflect.DeepEqual(v, _v) {
				return true
			}
		}
		return false
	case []string:
		if _v, ok := v.(string); ok {
			for _, _s := range s {
				if _v == _s {
					return true
				}
			}
		}
		return false
	case []int:
		if _v, ok := v.(int); ok {
			for _, _s := range s {
				if _v == _s {
					return true
				}
			}
		}
		return false
	}

	s := reflect.ValueOf(slice)
	kind := s.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return false
	}

	slen := s.Len()
	for i := 0; i < slen; i++ {
		if reflect.DeepEqual(v, s.Index(i).Interface()) {
			return true
		}
	}
	return false
}

// Reverse reverses the slice.
//
// The type of the argument must be one of []interface{}, []string,
// []int, []int64, []uint, []uint64.
func Reverse(slice interface{}) interface{} {
	switch slice.(type) {
	case []interface{}:
		ss := slice.([]interface{})
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp interface{}
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []string:
		ss := slice.([]string)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp string
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []int:
		ss := slice.([]int)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp int
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []int64:
		ss := slice.([]int64)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp int64
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []uint:
		ss := slice.([]uint)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp uint
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []uint64:
		ss := slice.([]uint64)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp uint64
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	default:
		panic(fmt.Errorf("not support the type '%T'", slice))
	}
	return slice
}
<commit_msg>update the doc<commit_after>package function

import (
	"fmt"
	"reflect"
)

var (
	// ErrNotPointer is returned when the value is not a pointer.
	ErrNotPointer = fmt.Errorf("the value is not a pointer")

	// ErrNotSliceOrArray is returned when the value is not a slice or array.
	ErrNotSliceOrArray = fmt.Errorf("The value is not slice or array")

	// ErrInvalidIndex is returned when the index exceeds over the length of
	// the slice.
	ErrInvalidIndex = fmt.Errorf("the index is exceeds the length of the slice")

	// ErrTypeNotCompatible is returned when the type is not compatible.
	ErrTypeNotCompatible = fmt.Errorf("the type is not compatible")
)

// GetSliceValue returns the ith element of slice.
//
// If slice is not a slice or array type, it will return ErrNotSliceOrArray.
// If the index i exceeds over the length of slice, that's, i>=len(slice),
// it will return ErrInvalidIndex.
//
// For []interface{}, []string and []int, they have already been optimized.
func GetSliceValue(slice interface{}, i int) (interface{}, error) {
	switch s := slice.(type) {
	case nil:
		return nil, ErrNotSliceOrArray
	case []string:
		if len(s) > i {
			return s[i], nil
		}
		return nil, ErrInvalidIndex
	case []int:
		if len(s) > i {
			return s[i], nil
		}
		return nil, ErrInvalidIndex
	case []interface{}:
		if len(s) > i {
			return s[i], nil
		}
		return nil, ErrInvalidIndex
	}

	s := reflect.ValueOf(slice)
	kind := s.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return nil, ErrNotSliceOrArray
	}

	if s.Len() > i {
		return s.Index(i).Interface(), nil
	}

	return nil, ErrInvalidIndex
}

func pullSliceValue(out interface{}, slice interface{}, index int,
	_default interface{}, yes bool) error {

	if out == nil {
		return ErrNotPointer
	}

	_out := reflect.ValueOf(out)
	kind := _out.Kind()
	if kind != reflect.Ptr && kind != reflect.UnsafePointer {
		return ErrNotPointer
	}
	_out = reflect.Indirect(_out)

	_slice := reflect.ValueOf(slice)
	kind = _slice.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return ErrNotSliceOrArray
	}

	var value interface{}
	if _slice.Len() > index {
		value = _slice.Index(index).Interface()
	} else if yes {
		value = _default
	} else {
		return ErrInvalidIndex
	}

	v := reflect.ValueOf(value)
	if v.Kind() != _out.Kind() {
		return ErrTypeNotCompatible
	}

	_out.Set(v)
	return nil
}

// PullSliceValueWithDefault is the same as PullSliceValue,
// but if index >= len(slice), set the value of out to _default.
func PullSliceValueWithDefault(out interface{}, slice interface{}, i int,
	_default interface{}) error {
	return pullSliceValue(out, slice, i, _default, true)
}

// PullSliceValue gets the ith element from the slice and puts it into the
// variable out, then returns nil if successfully.
//
// Return an error If out isn't a pointer, or slice isn't a slice or array type,
// or the length of slice is less than or equal to the index i, or the type of
// slice[i] and the underlying type of out are not compatible.
func PullSliceValue(out interface{}, slice interface{}, i int) error {
	return pullSliceValue(out, slice, i, nil, false)
}

// InSlice returns true if v is in slice, or returns false.
//
// It returns false if slice is not a slice type or the type is not compatible.
//
// For []interface{}, []string and []int, they have already been optimized.
func InSlice(v interface{}, slice interface{}) bool {
	// Optimize the types of []interface{}, []string and []int.
	switch s := slice.(type) {
	case nil:
		return false
	case []interface{}:
		for _, _v := range s {
			if reflect.DeepEqual(v, _v) {
				return true
			}
		}
		return false
	case []string:
		if _v, ok := v.(string); ok {
			for _, _s := range s {
				if _v == _s {
					return true
				}
			}
		}
		return false
	case []int:
		if _v, ok := v.(int); ok {
			for _, _s := range s {
				if _v == _s {
					return true
				}
			}
		}
		return false
	}

	s := reflect.ValueOf(slice)
	kind := s.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return false
	}

	slen := s.Len()
	for i := 0; i < slen; i++ {
		if reflect.DeepEqual(v, s.Index(i).Interface()) {
			return true
		}
	}
	return false
}

// Reverse reverses the source slice then returns it.
//
// The type of the argument must be one of []interface{}, []string,
// []int, []int64, []uint, []uint64.
func Reverse(slice interface{}) interface{} {
	switch slice.(type) {
	case []interface{}:
		ss := slice.([]interface{})
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp interface{}
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []string:
		ss := slice.([]string)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp string
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []int:
		ss := slice.([]int)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp int
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []int64:
		ss := slice.([]int64)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp int64
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []uint:
		ss := slice.([]uint)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp uint
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	case []uint64:
		ss := slice.([]uint64)
		_len := len(ss)
		if _len < 2 {
			return slice
		}

		var tmp uint64
		halflen := _len / 2
		_len--
		for i := 0; i < halflen; i++ {
			tmp = ss[i]
			ss[i] = ss[_len-i]
			ss[_len-i] = tmp
		}
	default:
		panic(fmt.Errorf("not support the type '%T'", slice))
	}
	return slice
}
<|endoftext|>


<commit_before>package futures

import (
	"fmt"
	"sync"
)

func Start(fn func() (interface{}, error)) *F {
	f := &F{
		done: make(chan struct{}),
	}
	go func() {
		f.setResult(fn())
	}()
	return f
}

type F struct {
	name   string
	mu     sync.Mutex
	result interface{}
	err    error
	done   chan struct{}
}

func (f *F) String() string {
	if f.name != "" {
		return f.name
	}
	return fmt.Sprintf("future %p", f)
}

func (f *F) SetName(s string) {
	f.name = s
}

func (f *F) Result() (interface{}, error) {
	<-f.done
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.result, f.err
}

func (f *F) MustResult() interface{} {
	val, err := f.Result()
	if err != nil {
		panic(err)
	}
	return val
}

func (f *F) Done() <-chan struct{} {
	return f.done
}

func (f *F) setResult(result interface{}, err error) {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.result = result
	f.err = err
	close(f.done)
}
<commit_msg>Add futures.StartNoError<commit_after>package futures

import (
	"fmt"
	"sync"
)

func Start(fn func() (interface{}, error)) *F {
	f := &F{
		done: make(chan struct{}),
	}
	go func() {
		f.setResult(fn())
	}()
	return f
}

func StartNoError(fn func() interface{}) *F {
	return Start(func() (interface{}, error) {
		return fn(), nil
	})
}

type F struct {
	name   string
	mu     sync.Mutex
	result interface{}
	err    error
	done   chan struct{}
}

func (f *F) String() string {
	if f.name != "" {
		return f.name
	}
	return fmt.Sprintf("future %p", f)
}

func (f *F) SetName(s string) {
	f.name = s
}

func (f *F) Result() (interface{}, error) {
	<-f.done
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.result, f.err
}

func (f *F) MustResult() interface{} {
	val, err := f.Result()
	if err != nil {
		panic(err)
	}
	return val
}

func (f *F) Done() <-chan struct{} {
	return f.done
}

func (f *F) setResult(result interface{}, err error) {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.result = result
	f.err = err
	close(f.done)
}
<|endoftext|>


<commit_before>package jobs

import (
	"net/http"
	"time"

	log "github.com/meifamily/logrus"
)

type pttMonitor struct {
	duration time.Duration
}

func NewPttMonitor() *pttMonitor {
	return &pttMonitor{
		duration: 1 * time.Minute,
	}
}

func (pm pttMonitor) Run() {
	log.Info("Start Ptt Monitor")

	var errorCounter = 0
	var url = "https://www.ptt.cc/index.html"
	for {
		resp, err := http.Get(url)
		if err != nil {
			log.WithError(err).Error("HTTP Get Error")
		}
		if err == nil && resp.StatusCode == http.StatusOK {
			log.Info("Ptt is alive")
		}
		if err == nil && resp.StatusCode != http.StatusOK {
			if errorCounter < 3 {
				log.Info("Ptt is dying")
			}
			errorCounter++
		}
		if err == nil && resp.StatusCode == http.StatusOK && errorCounter != 0 {
			log.Info("Ptt is back to life")
			errorCounter = 0
			go NewChecker().Run()
			go NewPushSumChecker().Run()
			go NewPushListChecker().Run()
		}
		if errorCounter == 3 {
			log.Info("Ptt is Dead")
			go NewChecker().Stop()
			go NewPushSumChecker().Stop()
			go NewPushListChecker().Stop()
		}
		time.Sleep(pm.duration)
	}
}
<commit_msg>refactor<commit_after>package jobs

import (
	"net/http"
	"time"

	log "github.com/meifamily/logrus"
)

type pttMonitor struct {
	duration time.Duration
}

func NewPttMonitor() *pttMonitor {
	return &pttMonitor{
		duration: 1 * time.Minute,
	}
}

func (pm pttMonitor) Run() {
	log.Info("Start Ptt Monitor")

	var errorCounter = 0
	var url = "https://www.ptt.cc/index.html"
	for {
		resp, err := http.Get(url)
		if err != nil {
			log.WithError(err).Error("HTTP Get Error")
		}
		if err == nil && resp.StatusCode == http.StatusOK {
			log.Info("Ptt is alive")
			if errorCounter >= 3 {
				log.Info("Ptt is back to life")
				go NewChecker().Run()
				go NewPushSumChecker().Run()
				go NewPushListChecker().Run()
			}
			errorCounter = 0
		}
		if err == nil && resp.StatusCode != http.StatusOK {
			if errorCounter < 3 {
				log.Info("Ptt is dying")
			}
			if errorCounter == 3 {
				log.Info("Ptt is Dead")
				go NewChecker().Stop()
				go NewPushSumChecker().Stop()
				go NewPushListChecker().Stop()
			}
			errorCounter++
		}
		time.Sleep(pm.duration)
	}
}
<|endoftext|>


<commit_before>package concrete

import (
	"github.com/gonum/graph"
	"math"
)

type DenseGraph struct {
	adjacencyMatrix []float64
	numNodes        int
}

func NewDenseGraph(numNodes int, passable bool) *DenseGraph {
	dg := &DenseGraph{adjacencyMatrix: make([]float64, numNodes*numNodes), numNodes: numNodes}
	if passable {
		for i := range dg.adjacencyMatrix {
			dg.adjacencyMatrix[i] = 1.0
		}
	} else {
		for i := range dg.adjacencyMatrix {
			dg.adjacencyMatrix[i] = math.Inf(1)
		}
	}

	return dg
}

func (dg *DenseGraph) NodeExists(node graph.Node) bool {
	return node.ID() < dg.numNodes
}

func (dg *DenseGraph) Degree(node graph.Node) int {
	deg := 0
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
			deg++
		}

		if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
			deg++
		}
	}

	return deg
}

func (dg *DenseGraph) NodeList() []graph.Node {
	nodes := make([]graph.Node, dg.numNodes)
	for i := 0; i < dg.numNodes; i++ {
		nodes[i] = GonumNode(i)
	}

	return nodes
}

func (dg *DenseGraph) DirectedEdgeList() []graph.Edge {
	edges := make([]graph.Edge, 0, len(dg.adjacencyMatrix))
	for i := 0; i < dg.numNodes; i++ {
		for j := 0; j < dg.numNodes; j++ {
			if dg.adjacencyMatrix[i*dg.numNodes+j] != math.Inf(1) {
				edges = append(edges, GonumEdge{GonumNode(i), GonumNode(j)})
			}
		}
	}

	return edges
}

func (dg *DenseGraph) Neighbors(node graph.Node) []graph.Node {
	neighbors := make([]graph.Node, 0)
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) ||
			dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
			neighbors = append(neighbors, GonumNode(i))
		}
	}

	return neighbors
}

func (dg *DenseGraph) IsNeighbor(node, neighbor graph.Node) bool {
	return dg.adjacencyMatrix[neighbor.ID()*dg.numNodes+node.ID()] != math.Inf(1) ||
		dg.adjacencyMatrix[node.ID()*dg.numNodes+neighbor.ID()] != math.Inf(1)
}

func (dg *DenseGraph) Successors(node graph.Node) []graph.Node {
	neighbors := make([]graph.Node, 0)
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
			neighbors = append(neighbors, GonumNode(i))
		}
	}

	return neighbors
}

func (dg *DenseGraph) IsSuccessor(node, succ graph.Node) bool {
	return dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] != math.Inf(1)
}

func (dg *DenseGraph) Predecessors(node graph.Node) []graph.Node {
	neighbors := make([]graph.Node, 0)
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
			neighbors = append(neighbors, GonumNode(i))
		}
	}

	return neighbors
}

func (dg *DenseGraph) IsPredecessor(node, pred graph.Node) bool {
	return dg.adjacencyMatrix[pred.ID()*dg.numNodes+node.ID()] != math.Inf(1)
}

// Naturally dense, we don't need to do anything
func (dg *DenseGraph) Crunch() {
}

func (dg *DenseGraph) Cost(node, succ graph.Node) float64 {
	return dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()]
}

func (dg *DenseGraph) SetEdgeCost(node, succ graph.Node, cost float64, directed bool) {
	dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] = cost
	if !directed {
		dg.adjacencyMatrix[succ.ID()*dg.numNodes+node.ID()] = cost
	}
}

<<<<<<< HEAD
// More or less equivalent to SetEdgeCost(node, succ, math.Inf(1), directed)
=======
>>>>>>> Made a basic dense graph
func (dg *DenseGraph) RemoveEdge(node, succ graph.Node, directed bool) {
	dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] = math.Inf(1)
	if !directed {
		dg.adjacencyMatrix[succ.ID()*dg.numNodes+node.ID()] = math.Inf(1)
	}
}
<commit_msg>Resolved conflict I think<commit_after>package concrete

import (
	"github.com/gonum/graph"
	"math"
)

type DenseGraph struct {
	adjacencyMatrix []float64
	numNodes        int
}

func NewDenseGraph(numNodes int, passable bool) *DenseGraph {
	dg := &DenseGraph{adjacencyMatrix: make([]float64, numNodes*numNodes), numNodes: numNodes}
	if passable {
		for i := range dg.adjacencyMatrix {
			dg.adjacencyMatrix[i] = 1.0
		}
	} else {
		for i := range dg.adjacencyMatrix {
			dg.adjacencyMatrix[i] = math.Inf(1)
		}
	}

	return dg
}

func (dg *DenseGraph) NodeExists(node graph.Node) bool {
	return node.ID() < dg.numNodes
}

func (dg *DenseGraph) Degree(node graph.Node) int {
	deg := 0
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
			deg++
		}

		if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
			deg++
		}
	}

	return deg
}

func (dg *DenseGraph) NodeList() []graph.Node {
	nodes := make([]graph.Node, dg.numNodes)
	for i := 0; i < dg.numNodes; i++ {
		nodes[i] = GonumNode(i)
	}

	return nodes
}

func (dg *DenseGraph) DirectedEdgeList() []graph.Edge {
	edges := make([]graph.Edge, 0, len(dg.adjacencyMatrix))
	for i := 0; i < dg.numNodes; i++ {
		for j := 0; j < dg.numNodes; j++ {
			if dg.adjacencyMatrix[i*dg.numNodes+j] != math.Inf(1) {
				edges = append(edges, GonumEdge{GonumNode(i), GonumNode(j)})
			}
		}
	}

	return edges
}

func (dg *DenseGraph) Neighbors(node graph.Node) []graph.Node {
	neighbors := make([]graph.Node, 0)
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) ||
			dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
			neighbors = append(neighbors, GonumNode(i))
		}
	}

	return neighbors
}

func (dg *DenseGraph) IsNeighbor(node, neighbor graph.Node) bool {
	return dg.adjacencyMatrix[neighbor.ID()*dg.numNodes+node.ID()] != math.Inf(1) ||
		dg.adjacencyMatrix[node.ID()*dg.numNodes+neighbor.ID()] != math.Inf(1)
}

func (dg *DenseGraph) Successors(node graph.Node) []graph.Node {
	neighbors := make([]graph.Node, 0)
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
			neighbors = append(neighbors, GonumNode(i))
		}
	}

	return neighbors
}

func (dg *DenseGraph) IsSuccessor(node, succ graph.Node) bool {
	return dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] != math.Inf(1)
}

func (dg *DenseGraph) Predecessors(node graph.Node) []graph.Node {
	neighbors := make([]graph.Node, 0)
	for i := 0; i < dg.numNodes; i++ {
		if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
			neighbors = append(neighbors, GonumNode(i))
		}
	}

	return neighbors
}

func (dg *DenseGraph) IsPredecessor(node, pred graph.Node) bool {
	return dg.adjacencyMatrix[pred.ID()*dg.numNodes+node.ID()] != math.Inf(1)
}

// Naturally dense, we don't need to do anything
func (dg *DenseGraph) Crunch() {
}

func (dg *DenseGraph) Cost(node, succ graph.Node) float64 {
	return dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()]
}

func (dg *DenseGraph) SetEdgeCost(node, succ graph.Node, cost float64, directed bool) {
	dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] = cost
	if !directed {
		dg.adjacencyMatrix[succ.ID()*dg.numNodes+node.ID()] = cost
	}
}

// More or less equivalent to SetEdgeCost(node, succ, math.Inf(1), directed)
func (dg *DenseGraph) RemoveEdge(node, succ graph.Node, directed bool) {
	dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] = math.Inf(1)
	if !directed {
		dg.adjacencyMatrix[succ.ID()*dg.numNodes+node.ID()] = math.Inf(1)
	}
}
<|endoftext|>


<commit_before>package conf

import (
	"testing"
)

func TestInStr0(t *testing.T) {
	if inStr("a", []string{}) != false {
		t.Errorf("fals in TestInStr0")
	}
}

func TestInStr1(t *testing.T) {
	if inStr("a", []string{"a", "b", "c"}) != true {
		t.Errorf("fals in TestInStr1")
	}
}

func TestInStr2(t *testing.T) {
	if inStr("c", []string{"a", "b", "c"}) != true {
		t.Errorf("fals in TestInStr2")
	}
}

func TestInStr3(t *testing.T) {
	if inStr("b", []string{"a", "b", "c"}) != true {
		t.Errorf("fals in TestInStr3")
	}
}

func TestInStr4(t *testing.T) {
	if inStr("d", []string{"a", "b", "c"}) != false {
		t.Errorf("fals in TestInStr4")
	}
}

func TestIsNum1(t *testing.T) {
	if isNum("0") != true {
		t.Errorf("falis in TestIsNum1")
	}
}

func TestIsNum2(t *testing.T) {
	if isNum("+2") != true {
		t.Errorf("falis in TestIsNum2")
	}
}

func TestIsNum3(t *testing.T) {
	if isNum("+2.3") != true {
		t.Errorf("falis in TestIsNum3")
	}
}

func TestIsNum4(t *testing.T) {
	if isNum("2.3.5") != false {
		t.Errorf("falis in TestIsNum4")
	}
}

func TestIsNum5(t *testing.T) {
	if isNum("-2") != true {
		t.Errorf("falis in TestIsNum5")
	}
}

func TestIsNum6(t *testing.T) {
	if isNum("-2.8") != true {
		t.Errorf("falis in TestIsNum6")
	}
}

func TestIsNum7(t *testing.T) {
	if isNum("-2.8.3") != false {
		t.Errorf("falis in TestIsNum7")
	}
}
<commit_msg>inStr -> utils.InStr<commit_after>package conf

import (
	"github.com/yassu/gnuplot.go/utils"
	"testing"
)

func TestInStr0(t *testing.T) {
	if utils.InStr("a", []string{}) != false {
		t.Errorf("fals in TestInStr0")
	}
}

func TestInStr1(t *testing.T) {
	if utils.InStr("a", []string{"a", "b", "c"}) != true {
		t.Errorf("fals in TestInStr1")
	}
}

func TestInStr2(t *testing.T) {
	if utils.InStr("c", []string{"a", "b", "c"}) != true {
		t.Errorf("fals in TestInStr2")
	}
}

func TestInStr3(t *testing.T) {
	if utils.InStr("b", []string{"a", "b", "c"}) != true {
		t.Errorf("fals in TestInStr3")
	}
}

func TestInStr4(t *testing.T) {
	if utils.InStr("d", []string{"a", "b", "c"}) != false {
		t.Errorf("fals in TestInStr4")
	}
}

func TestIsNum1(t *testing.T) {
	if isNum("0") != true {
		t.Errorf("falis in TestIsNum1")
	}
}

func TestIsNum2(t *testing.T) {
	if isNum("+2") != true {
		t.Errorf("falis in TestIsNum2")
	}
}

func TestIsNum3(t *testing.T) {
	if isNum("+2.3") != true {
		t.Errorf("falis in TestIsNum3")
	}
}

func TestIsNum4(t *testing.T) {
	if isNum("2.3.5") != false {
		t.Errorf("falis in TestIsNum4")
	}
}

func TestIsNum5(t *testing.T) {
	if isNum("-2") != true {
		t.Errorf("falis in TestIsNum5")
	}
}

func TestIsNum6(t *testing.T) {
	if isNum("-2.8") != true {
		t.Errorf("falis in TestIsNum6")
	}
}

func TestIsNum7(t *testing.T) {
	if isNum("-2.8.3") != false {
		t.Errorf("falis in TestIsNum7")
	}
}
<|endoftext|>


<commit_before>// package conf 为配置相关.
package conf

import (
	"strings"

	"github.com/astaxie/beego"
	"strconv"
	"path/filepath"
	"os"
	"fmt"
)

// 登录用户的Session名
const LoginSessionName = "LoginSessionName"

const CaptchaSessionName = "__captcha__"

const RegexpEmail = "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"

//允许用户名中出现点号
const RegexpAccount = `^[a-zA-Z][a-zA-Z0-9\.]{2,50}$`

// PageSize 默认分页条数.
const PageSize = 10

// 用户权限
const (
	// 超级管理员.
	MemberSuperRole SystemRole = iota
	//普通管理员.
	MemberAdminRole
	//普通用户.
	MemberGeneralRole
)

//系统角色
type SystemRole int

const (
	// 创始人.
	BookFounder BookRole = iota
	//管理者
	BookAdmin
	//编辑者.
	BookEditor
	//观察者
	BookObserver
)

//项目角色
type BookRole int

const (
	LoggerOperate   = "operate"
	LoggerSystem    = "system"
	LoggerException = "exception"
	LoggerDocument  = "document"
)
const (
	//本地账户校验
	AuthMethodLocal = "local"
	//LDAP用户校验
	AuthMethodLDAP = "ldap"
)

var (
	VERSION    string
	BUILD_TIME string
	GO_VERSION string
)

var (
	ConfigurationFile = "./conf/app.conf"
	WorkingDirectory  = "./"
	LogFile           = "./runtime/logs"
	BaseUrl           = ""
	AutoLoadDelay     = 0
)

// app_key
func GetAppKey() string {
	return beego.AppConfig.DefaultString("app_key", "mindoc")
}

func GetDatabasePrefix() string {
	return beego.AppConfig.DefaultString("db_prefix", "md_")
}

//获取默认头像
func GetDefaultAvatar() string {
	return URLForWithCdnImage(beego.AppConfig.DefaultString("avatar", "/static/images/headimgurl.jpg"))
}

//获取阅读令牌长度.
func GetTokenSize() int {
	return beego.AppConfig.DefaultInt("token_size", 12)
}

//获取默认文档封面.
func GetDefaultCover() string {

	return URLForWithCdnImage(beego.AppConfig.DefaultString("cover", "/static/images/book.jpg"))
}

//获取允许的商城文件的类型.
func GetUploadFileExt() []string {
	ext := beego.AppConfig.DefaultString("upload_file_ext", "png|jpg|jpeg|gif|txt|doc|docx|pdf")

	temp := strings.Split(ext, "|")

	exts := make([]string, len(temp))

	i := 0
	for _, item := range temp {
		if item != "" {
			exts[i] = item
			i++
		}
	}
	return exts
}

// 获取上传文件允许的最大值
func GetUploadFileSize() int64 {
	size := beego.AppConfig.DefaultString("upload_file_size", "0")

	if strings.HasSuffix(size, "MB") {
		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
			return s * 1024 * 1024
		}
	}
	if strings.HasSuffix(size, "GB") {
		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
			return s * 1024 * 1024 * 1024
		}
	}
	if strings.HasSuffix(size, "KB") {
		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
			return s * 1024
		}
	}
	if s, e := strconv.ParseInt(size, 10, 64); e == nil {
		return s * 1024
	}
	return 0
}

//是否启用导出
func GetEnableExport() bool {
	return beego.AppConfig.DefaultBool("enable_export", true)
}

//同一项目导出线程的并发数
func GetExportProcessNum() int {
	exportProcessNum := beego.AppConfig.DefaultInt("export_process_num", 1)

	if exportProcessNum <= 0 || exportProcessNum > 4 {
		exportProcessNum = 1
	}
	return exportProcessNum;
}

//导出项目队列的并发数量
func GetExportLimitNum() int {
	exportLimitNum := beego.AppConfig.DefaultInt("export_limit_num", 1)

	if exportLimitNum < 0 {
		exportLimitNum = 1
	}
	return exportLimitNum;
}

//等待导出队列的长度
func GetExportQueueLimitNum() int {
	exportQueueLimitNum := beego.AppConfig.DefaultInt("export_queue_limit_num", 10)

	if exportQueueLimitNum <= 0 {
		exportQueueLimitNum = 100
	}
	return exportQueueLimitNum
}

//默认导出项目的缓存目录
func GetExportOutputPath() string {
	exportOutputPath := filepath.Join(beego.AppConfig.DefaultString("export_output_path", filepath.Join(WorkingDirectory, "cache")), "books")

	return exportOutputPath
}

//判断是否是允许商城的文件类型.
func IsAllowUploadFileExt(ext string) bool {

	if strings.HasPrefix(ext, ".") {
		ext = string(ext[1:])
	}
	exts := GetUploadFileExt()

	for _, item := range exts {
		if item == "*" {
			return true
		}
		if strings.EqualFold(item, ext) {
			return true
		}
	}
	return false
}

//重写生成URL的方法，加上完整的域名
func URLFor(endpoint string, values ...interface{}) string {
	baseUrl := beego.AppConfig.DefaultString("baseurl", "")
	pathUrl := beego.URLFor(endpoint, values ...)

	if baseUrl == "" {
		baseUrl = BaseUrl
	}
	if strings.HasPrefix(pathUrl, "http://") {
		return pathUrl
	}
	if strings.HasPrefix(pathUrl, "/") && strings.HasSuffix(baseUrl, "/") {
		return baseUrl + pathUrl[1:]
	}
	if !strings.HasPrefix(pathUrl, "/") && !strings.HasSuffix(baseUrl, "/") {
		return baseUrl + "/" + pathUrl
	}
	return baseUrl + beego.URLFor(endpoint, values ...)
}

func URLForNotHost(endpoint string,values ...interface{}) string  {
	baseUrl := beego.AppConfig.DefaultString("baseurl", "")
	pathUrl := beego.URLFor(endpoint, values ...)

	if baseUrl == "" {
		baseUrl = "/"
	}
	if strings.HasPrefix(pathUrl, "http://") {
		return pathUrl
	}
	if strings.HasPrefix(pathUrl, "/") && strings.HasSuffix(baseUrl, "/") {
		return baseUrl + pathUrl[1:]
	}
	if !strings.HasPrefix(pathUrl, "/") && !strings.HasSuffix(baseUrl, "/") {
		return baseUrl + "/" + pathUrl
	}
	return baseUrl + beego.URLFor(endpoint, values ...)
}

func URLForWithCdnImage(p string) string {
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p
	}
	cdn := beego.AppConfig.DefaultString("cdnimg", "")
	//如果没有设置cdn，则使用baseURL拼接
	if cdn == "" {
		baseUrl := beego.AppConfig.DefaultString("baseurl", "/")

		if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
			return baseUrl + p[1:]
		}
		if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
			return baseUrl + "/" + p
		}
		return baseUrl + p
	}
	if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
		return cdn + string(p[1:])
	}
	if !strings.HasPrefix(p, "/") && !strings.HasSuffix(cdn, "/") {
		return cdn + "/" + p
	}
	return cdn + p
}

func URLForWithCdnCss(p string, v ...string) string {
	cdn := beego.AppConfig.DefaultString("cdncss", "")
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p
	}
	filePath := WorkingDir(p)

	if f, err := os.Stat(filePath); err == nil && !strings.Contains(p, "?") && len(v) > 0 && v[0] == "version" {
		p = p + fmt.Sprintf("?v=%s", f.ModTime().Format("20060102150405"))
	}
	//如果没有设置cdn，则使用baseURL拼接
	if cdn == "" {
		baseUrl := beego.AppConfig.DefaultString("baseurl", "/")

		if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
			return baseUrl + p[1:]
		}
		if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
			return baseUrl + "/" + p
		}
		return baseUrl + p
	}
	if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
		return cdn + string(p[1:])
	}
	if !strings.HasPrefix(p, "/") && !strings.HasSuffix(cdn, "/") {
		return cdn + "/" + p
	}
	return cdn + p
}

func URLForWithCdnJs(p string, v ...string) string {
	cdn := beego.AppConfig.DefaultString("cdnjs", "")
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p
	}

	filePath := WorkingDir(p)

	if f, err := os.Stat(filePath); err == nil && !strings.Contains(p, "?") && len(v) > 0 && v[0] == "version" {
		p = p + fmt.Sprintf("?v=%s", f.ModTime().Format("20060102150405"))
	}

	//如果没有设置cdn，则使用baseURL拼接
	if cdn == "" {
		baseUrl := beego.AppConfig.DefaultString("baseurl", "/")

		if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
			return baseUrl + p[1:]
		}
		if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
			return baseUrl + "/" + p
		}
		return baseUrl + p
	}
	if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
		return cdn + string(p[1:])
	}
	if !strings.HasPrefix(p, "/") && !strings.HasSuffix(cdn, "/") {
		return cdn + "/" + p
	}
	return cdn + p
}

func WorkingDir(elem ...string) string {

	elems := append([]string{WorkingDirectory}, elem...)

	return filepath.Join(elems...)
}

func init() {
	if p, err := filepath.Abs("./conf/app.conf"); err == nil {
		ConfigurationFile = p
	}
	if p, err := filepath.Abs("./"); err == nil {
		WorkingDirectory = p
	}
	if p, err := filepath.Abs("./runtime/logs"); err == nil {
		LogFile = p
	}
}
<commit_msg>fix:账号正则修改<commit_after>// package conf 为配置相关.
package conf

import (
	"strings"

	"github.com/astaxie/beego"
	"strconv"
	"path/filepath"
	"os"
	"fmt"
)

// 登录用户的Session名
const LoginSessionName = "LoginSessionName"

const CaptchaSessionName = "__captcha__"

const RegexpEmail = "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"

//允许用户名中出现点号
const RegexpAccount = `^[a-zA-Z][a-zA-Z0-9\.-]{2,50}$`

// PageSize 默认分页条数.
const PageSize = 10

// 用户权限
const (
	// 超级管理员.
	MemberSuperRole SystemRole = iota
	//普通管理员.
	MemberAdminRole
	//普通用户.
	MemberGeneralRole
)

//系统角色
type SystemRole int

const (
	// 创始人.
	BookFounder BookRole = iota
	//管理者
	BookAdmin
	//编辑者.
	BookEditor
	//观察者
	BookObserver
)

//项目角色
type BookRole int

const (
	LoggerOperate   = "operate"
	LoggerSystem    = "system"
	LoggerException = "exception"
	LoggerDocument  = "document"
)
const (
	//本地账户校验
	AuthMethodLocal = "local"
	//LDAP用户校验
	AuthMethodLDAP = "ldap"
)

var (
	VERSION    string
	BUILD_TIME string
	GO_VERSION string
)

var (
	ConfigurationFile = "./conf/app.conf"
	WorkingDirectory  = "./"
	LogFile           = "./runtime/logs"
	BaseUrl           = ""
	AutoLoadDelay     = 0
)

// app_key
func GetAppKey() string {
	return beego.AppConfig.DefaultString("app_key", "mindoc")
}

func GetDatabasePrefix() string {
	return beego.AppConfig.DefaultString("db_prefix", "md_")
}

//获取默认头像
func GetDefaultAvatar() string {
	return URLForWithCdnImage(beego.AppConfig.DefaultString("avatar", "/static/images/headimgurl.jpg"))
}

//获取阅读令牌长度.
func GetTokenSize() int {
	return beego.AppConfig.DefaultInt("token_size", 12)
}

//获取默认文档封面.
func GetDefaultCover() string {

	return URLForWithCdnImage(beego.AppConfig.DefaultString("cover", "/static/images/book.jpg"))
}

//获取允许的商城文件的类型.
func GetUploadFileExt() []string {
	ext := beego.AppConfig.DefaultString("upload_file_ext", "png|jpg|jpeg|gif|txt|doc|docx|pdf")

	temp := strings.Split(ext, "|")

	exts := make([]string, len(temp))

	i := 0
	for _, item := range temp {
		if item != "" {
			exts[i] = item
			i++
		}
	}
	return exts
}

// 获取上传文件允许的最大值
func GetUploadFileSize() int64 {
	size := beego.AppConfig.DefaultString("upload_file_size", "0")

	if strings.HasSuffix(size, "MB") {
		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
			return s * 1024 * 1024
		}
	}
	if strings.HasSuffix(size, "GB") {
		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
			return s * 1024 * 1024 * 1024
		}
	}
	if strings.HasSuffix(size, "KB") {
		if s, e := strconv.ParseInt(size[0:len(size)-2], 10, 64); e == nil {
			return s * 1024
		}
	}
	if s, e := strconv.ParseInt(size, 10, 64); e == nil {
		return s * 1024
	}
	return 0
}

//是否启用导出
func GetEnableExport() bool {
	return beego.AppConfig.DefaultBool("enable_export", true)
}

//同一项目导出线程的并发数
func GetExportProcessNum() int {
	exportProcessNum := beego.AppConfig.DefaultInt("export_process_num", 1)

	if exportProcessNum <= 0 || exportProcessNum > 4 {
		exportProcessNum = 1
	}
	return exportProcessNum;
}

//导出项目队列的并发数量
func GetExportLimitNum() int {
	exportLimitNum := beego.AppConfig.DefaultInt("export_limit_num", 1)

	if exportLimitNum < 0 {
		exportLimitNum = 1
	}
	return exportLimitNum;
}

//等待导出队列的长度
func GetExportQueueLimitNum() int {
	exportQueueLimitNum := beego.AppConfig.DefaultInt("export_queue_limit_num", 10)

	if exportQueueLimitNum <= 0 {
		exportQueueLimitNum = 100
	}
	return exportQueueLimitNum
}

//默认导出项目的缓存目录
func GetExportOutputPath() string {
	exportOutputPath := filepath.Join(beego.AppConfig.DefaultString("export_output_path", filepath.Join(WorkingDirectory, "cache")), "books")

	return exportOutputPath
}

//判断是否是允许商城的文件类型.
func IsAllowUploadFileExt(ext string) bool {

	if strings.HasPrefix(ext, ".") {
		ext = string(ext[1:])
	}
	exts := GetUploadFileExt()

	for _, item := range exts {
		if item == "*" {
			return true
		}
		if strings.EqualFold(item, ext) {
			return true
		}
	}
	return false
}

//重写生成URL的方法，加上完整的域名
func URLFor(endpoint string, values ...interface{}) string {
	baseUrl := beego.AppConfig.DefaultString("baseurl", "")
	pathUrl := beego.URLFor(endpoint, values ...)

	if baseUrl == "" {
		baseUrl = BaseUrl
	}
	if strings.HasPrefix(pathUrl, "http://") {
		return pathUrl
	}
	if strings.HasPrefix(pathUrl, "/") && strings.HasSuffix(baseUrl, "/") {
		return baseUrl + pathUrl[1:]
	}
	if !strings.HasPrefix(pathUrl, "/") && !strings.HasSuffix(baseUrl, "/") {
		return baseUrl + "/" + pathUrl
	}
	return baseUrl + beego.URLFor(endpoint, values ...)
}

func URLForNotHost(endpoint string,values ...interface{}) string  {
	baseUrl := beego.AppConfig.DefaultString("baseurl", "")
	pathUrl := beego.URLFor(endpoint, values ...)

	if baseUrl == "" {
		baseUrl = "/"
	}
	if strings.HasPrefix(pathUrl, "http://") {
		return pathUrl
	}
	if strings.HasPrefix(pathUrl, "/") && strings.HasSuffix(baseUrl, "/") {
		return baseUrl + pathUrl[1:]
	}
	if !strings.HasPrefix(pathUrl, "/") && !strings.HasSuffix(baseUrl, "/") {
		return baseUrl + "/" + pathUrl
	}
	return baseUrl + beego.URLFor(endpoint, values ...)
}

func URLForWithCdnImage(p string) string {
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p
	}
	cdn := beego.AppConfig.DefaultString("cdnimg", "")
	//如果没有设置cdn，则使用baseURL拼接
	if cdn == "" {
		baseUrl := beego.AppConfig.DefaultString("baseurl", "/")

		if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
			return baseUrl + p[1:]
		}
		if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
			return baseUrl + "/" + p
		}
		return baseUrl + p
	}
	if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
		return cdn + string(p[1:])
	}
	if !strings.HasPrefix(p, "/") && !strings.HasSuffix(cdn, "/") {
		return cdn + "/" + p
	}
	return cdn + p
}

func URLForWithCdnCss(p string, v ...string) string {
	cdn := beego.AppConfig.DefaultString("cdncss", "")
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p
	}
	filePath := WorkingDir(p)

	if f, err := os.Stat(filePath); err == nil && !strings.Contains(p, "?") && len(v) > 0 && v[0] == "version" {
		p = p + fmt.Sprintf("?v=%s", f.ModTime().Format("20060102150405"))
	}
	//如果没有设置cdn，则使用baseURL拼接
	if cdn == "" {
		baseUrl := beego.AppConfig.DefaultString("baseurl", "/")

		if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
			return baseUrl + p[1:]
		}
		if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
			return baseUrl + "/" + p
		}
		return baseUrl + p
	}
	if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
		return cdn + string(p[1:])
	}
	if !strings.HasPrefix(p, "/") && !strings.HasSuffix(cdn, "/") {
		return cdn + "/" + p
	}
	return cdn + p
}

func URLForWithCdnJs(p string, v ...string) string {
	cdn := beego.AppConfig.DefaultString("cdnjs", "")
	if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") {
		return p
	}

	filePath := WorkingDir(p)

	if f, err := os.Stat(filePath); err == nil && !strings.Contains(p, "?") && len(v) > 0 && v[0] == "version" {
		p = p + fmt.Sprintf("?v=%s", f.ModTime().Format("20060102150405"))
	}

	//如果没有设置cdn，则使用baseURL拼接
	if cdn == "" {
		baseUrl := beego.AppConfig.DefaultString("baseurl", "/")

		if strings.HasPrefix(p, "/") && strings.HasSuffix(baseUrl, "/") {
			return baseUrl + p[1:]
		}
		if !strings.HasPrefix(p, "/") && !strings.HasSuffix(baseUrl, "/") {
			return baseUrl + "/" + p
		}
		return baseUrl + p
	}
	if strings.HasPrefix(p, "/") && strings.HasSuffix(cdn, "/") {
		return cdn + string(p[1:])
	}
	if !strings.HasPrefix(p, "/") && !strings.HasSuffix(cdn, "/") {
		return cdn + "/" + p
	}
	return cdn + p
}

func WorkingDir(elem ...string) string {

	elems := append([]string{WorkingDirectory}, elem...)

	return filepath.Join(elems...)
}

func init() {
	if p, err := filepath.Abs("./conf/app.conf"); err == nil {
		ConfigurationFile = p
	}
	if p, err := filepath.Abs("./"); err == nil {
		WorkingDirectory = p
	}
	if p, err := filepath.Abs("./runtime/logs"); err == nil {
		LogFile = p
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"context"
	"os/exec"
	"strings"

	"github.com/gopasspw/gopass/pkg/backend/crypto/gpg"
	"github.com/gopasspw/gopass/pkg/out"
)

// listKey lists all keys of the given type and matching the search strings
func (g *GPG) listKeys(ctx context.Context, typ string, search ...string) (gpg.KeyList, error) {
	args := []string{"--with-colons", "--with-fingerprint", "--fixed-list-mode", "--list-" + typ + "-keys"}
	args = append(args, search...)
	if e, found := g.listCache.Get(strings.Join(args, ",")); found {
		if ev, ok := e.(gpg.KeyList); ok {
			return ev, nil
		}
	}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stderr = nil

	out.Debug(ctx, "gpg.listKeys: %s %+v\n", cmd.Path, cmd.Args)
	cmdout, err := cmd.Output()
	if err != nil {
		if bytes.Contains(cmdout, []byte("secret key not available")) {
			return gpg.KeyList{}, nil
		}
		return gpg.KeyList{}, err
	}

	kl := parseColons(bytes.NewBuffer(cmdout))
	g.listCache.Add(strings.Join(args, ","), kl)
	return kl, nil
}

// ListPublicKeyIDs returns a parsed list of GPG public keys
func (g *GPG) ListPublicKeyIDs(ctx context.Context) ([]string, error) {
	if g.pubKeys == nil {
		kl, err := g.listKeys(ctx, "public")
		if err != nil {
			return nil, err
		}
		g.pubKeys = kl
	}
	if gpg.IsAlwaysTrust(ctx) {
		return g.pubKeys.Recipients(), nil
	}
	return g.pubKeys.UseableKeys().Recipients(), nil
}

// FindPublicKeys searches for the given public keys
func (g *GPG) FindPublicKeys(ctx context.Context, search ...string) ([]string, error) {
	kl, err := g.listKeys(ctx, "public", search...)
	if err != nil || kl == nil {
		return nil, err
	}
	if gpg.IsAlwaysTrust(ctx) {
		return kl.Recipients(), nil
	}
	return kl.UseableKeys().Recipients(), nil
}

// ListPrivateKeyIDs returns a parsed list of GPG secret keys
func (g *GPG) ListPrivateKeyIDs(ctx context.Context) ([]string, error) {
	if g.privKeys == nil {
		kl, err := g.listKeys(ctx, "secret")
		if err != nil {
			return nil, err
		}
		g.privKeys = kl
	}
	if gpg.IsAlwaysTrust(ctx) {
		return g.privKeys.Recipients(), nil
	}
	return g.privKeys.UseableKeys().Recipients(), nil
}

// FindPrivateKeys searches for the given private keys
func (g *GPG) FindPrivateKeys(ctx context.Context, search ...string) ([]string, error) {
	kl, err := g.listKeys(ctx, "secret", search...)
	if err != nil || kl == nil {
		return nil, err
	}
	if gpg.IsAlwaysTrust(ctx) {
		return kl.Recipients(), nil
	}
	return kl.UseableKeys().Recipients(), nil
}

func (g *GPG) findKey(ctx context.Context, id string) gpg.Key {
	kl, _ := g.listKeys(ctx, "secret", id)
	if len(kl) == 1 {
		return kl[0]
	}
	kl, _ = g.listKeys(ctx, "public", id)
	if len(kl) == 1 {
		return kl[0]
	}
	return gpg.Key{
		Fingerprint: id,
	}
}

// EmailFromKey extracts the email from a key id
func (g *GPG) EmailFromKey(ctx context.Context, id string) string {
	return g.findKey(ctx, id).Identity().Email
}

// NameFromKey extracts the name from a key id
func (g *GPG) NameFromKey(ctx context.Context, id string) string {
	return g.findKey(ctx, id).Identity().Name
}

// FormatKey formats the details of a key id
func (g *GPG) FormatKey(ctx context.Context, id string) string {
	return g.findKey(ctx, id).OneLine()
}

// Fingerprint returns the full-length native fingerprint
func (g *GPG) Fingerprint(ctx context.Context, id string) string {
	return g.findKey(ctx, id).Fingerprint
}
<commit_msg>Handle multiple private keys matching an identifier (#1059)<commit_after>package cli

import (
	"bytes"
	"context"
	"os/exec"
	"strings"

	"github.com/gopasspw/gopass/pkg/backend/crypto/gpg"
	"github.com/gopasspw/gopass/pkg/out"
)

// listKey lists all keys of the given type and matching the search strings
func (g *GPG) listKeys(ctx context.Context, typ string, search ...string) (gpg.KeyList, error) {
	args := []string{"--with-colons", "--with-fingerprint", "--fixed-list-mode", "--list-" + typ + "-keys"}
	args = append(args, search...)
	if e, found := g.listCache.Get(strings.Join(args, ",")); found {
		if ev, ok := e.(gpg.KeyList); ok {
			return ev, nil
		}
	}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stderr = nil

	out.Debug(ctx, "gpg.listKeys: %s %+v\n", cmd.Path, cmd.Args)
	cmdout, err := cmd.Output()
	if err != nil {
		if bytes.Contains(cmdout, []byte("secret key not available")) {
			return gpg.KeyList{}, nil
		}
		return gpg.KeyList{}, err
	}

	kl := parseColons(bytes.NewBuffer(cmdout))
	g.listCache.Add(strings.Join(args, ","), kl)
	return kl, nil
}

// ListPublicKeyIDs returns a parsed list of GPG public keys
func (g *GPG) ListPublicKeyIDs(ctx context.Context) ([]string, error) {
	if g.pubKeys == nil {
		kl, err := g.listKeys(ctx, "public")
		if err != nil {
			return nil, err
		}
		g.pubKeys = kl
	}
	if gpg.IsAlwaysTrust(ctx) {
		return g.pubKeys.Recipients(), nil
	}
	return g.pubKeys.UseableKeys().Recipients(), nil
}

// FindPublicKeys searches for the given public keys
func (g *GPG) FindPublicKeys(ctx context.Context, search ...string) ([]string, error) {
	kl, err := g.listKeys(ctx, "public", search...)
	if err != nil || kl == nil {
		return nil, err
	}
	if gpg.IsAlwaysTrust(ctx) {
		return kl.Recipients(), nil
	}
	return kl.UseableKeys().Recipients(), nil
}

// ListPrivateKeyIDs returns a parsed list of GPG secret keys
func (g *GPG) ListPrivateKeyIDs(ctx context.Context) ([]string, error) {
	if g.privKeys == nil {
		kl, err := g.listKeys(ctx, "secret")
		if err != nil {
			return nil, err
		}
		g.privKeys = kl
	}
	if gpg.IsAlwaysTrust(ctx) {
		return g.privKeys.Recipients(), nil
	}
	return g.privKeys.UseableKeys().Recipients(), nil
}

// FindPrivateKeys searches for the given private keys
func (g *GPG) FindPrivateKeys(ctx context.Context, search ...string) ([]string, error) {
	kl, err := g.listKeys(ctx, "secret", search...)
	if err != nil || kl == nil {
		return nil, err
	}
	if gpg.IsAlwaysTrust(ctx) {
		return kl.Recipients(), nil
	}
	return kl.UseableKeys().Recipients(), nil
}

func (g *GPG) findKey(ctx context.Context, id string) gpg.Key {
	kl, _ := g.listKeys(ctx, "secret", id)
	if len(kl) >= 1 {
		return kl[0]
	}
	kl, _ = g.listKeys(ctx, "public", id)
	if len(kl) >= 1 {
		return kl[0]
	}
	return gpg.Key{
		Fingerprint: id,
	}
}

// EmailFromKey extracts the email from a key id
func (g *GPG) EmailFromKey(ctx context.Context, id string) string {
	return g.findKey(ctx, id).Identity().Email
}

// NameFromKey extracts the name from a key id
func (g *GPG) NameFromKey(ctx context.Context, id string) string {
	return g.findKey(ctx, id).Identity().Name
}

// FormatKey formats the details of a key id
func (g *GPG) FormatKey(ctx context.Context, id string) string {
	return g.findKey(ctx, id).OneLine()
}

// Fingerprint returns the full-length native fingerprint
func (g *GPG) Fingerprint(ctx context.Context, id string) string {
	return g.findKey(ctx, id).Fingerprint
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mgr

import (
	"fmt"
	"strconv"

	mgrdaemon "github.com/rook/rook/pkg/daemon/ceph/mgr"
	opmon "github.com/rook/rook/pkg/operator/ceph/cluster/mon"
	opspec "github.com/rook/rook/pkg/operator/ceph/spec"
	"github.com/rook/rook/pkg/operator/k8sutil"
	"k8s.io/api/core/v1"
	extensions "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	mgrDaemonCommand = "ceph-mgr"
)

func (c *Cluster) makeDeployment(mgrConfig *mgrConfig, port int) *extensions.Deployment {
	podSpec := v1.PodTemplateSpec{
		ObjectMeta: metav1.ObjectMeta{
			Name:   mgrConfig.ResourceName,
			Labels: c.getPodLabels(mgrConfig.DaemonName),
			Annotations: map[string]string{"prometheus.io/scrape": "true",
				"prometheus.io/port": strconv.Itoa(metricsPort)},
		},
		Spec: v1.PodSpec{
			InitContainers: []v1.Container{
				// Config file init performed by Rook
				c.makeConfigInitContainer(mgrConfig),
			},
			Containers: []v1.Container{
				c.makeMgrDaemonContainer(mgrConfig, port),
			},
			ServiceAccountName: serviceAccountName,
			RestartPolicy:      v1.RestartPolicyAlways,
			Volumes:            opspec.PodVolumes(""),
			HostNetwork:        c.HostNetwork,
		},
	}
	if c.HostNetwork {
		podSpec.Spec.DNSPolicy = v1.DNSClusterFirstWithHostNet
	}
	c.placement.ApplyToPodSpec(&podSpec.Spec)

	replicas := int32(1)
	d := &extensions.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      mgrConfig.ResourceName,
			Namespace: c.Namespace,
		},
		Spec: extensions.DeploymentSpec{Template: podSpec, Replicas: &replicas},
	}
	k8sutil.SetOwnerRef(c.context.Clientset, c.Namespace, &d.ObjectMeta, &c.ownerRef)
	return d
}

func (c *Cluster) makeConfigInitContainer(mgrConfig *mgrConfig) v1.Container {
	return v1.Container{
		Name: opspec.ConfigInitContainerName,
		Args: []string{
			"ceph",
			mgrdaemon.InitCommand,
			fmt.Sprintf("--config-dir=%s", k8sutil.DataDir),
			fmt.Sprintf("--mgr-name=%s", mgrConfig.DaemonName),
		},
		Image: k8sutil.MakeRookImage(c.rookVersion),
		Env: []v1.EnvVar{
			// Set '--mgr-keyring' flag with an env var sourced from the secret
			{Name: "ROOK_MGR_KEYRING",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{Name: mgrConfig.ResourceName},
						Key:                  opspec.KeyringSecretKeyName,
					}}},
			k8sutil.PodIPEnvVar(k8sutil.PrivateIPEnvVar),
			k8sutil.PodIPEnvVar(k8sutil.PublicIPEnvVar),
			opmon.EndpointEnvVar(),
			opmon.SecretEnvVar(),
			opmon.AdminSecretEnvVar(),
			k8sutil.ConfigOverrideEnvVar(),
		},
		VolumeMounts: opspec.RookVolumeMounts(),
		// config file creation does not require ports to be open
		Resources: c.resources,
	}
}

func (c *Cluster) makeMgrDaemonContainer(mgrConfig *mgrConfig, port int) v1.Container {
	container := v1.Container{
		Name: "mgr",
		Command: []string{
			mgrDaemonCommand,
		},
		Args: []string{
			"--foreground",
			"--id", mgrConfig.DaemonName,
			// do not add the '--cluster/--conf/--keyring' flags; rook wants their default values
		},
		Image:        c.cephVersion.Image,
		VolumeMounts: opspec.CephVolumeMounts(),
		Ports: []v1.ContainerPort{
			{
				Name:          "mgr",
				ContainerPort: int32(6800),
				Protocol:      v1.ProtocolTCP,
			},
			{
				Name:          "http-metrics",
				ContainerPort: int32(metricsPort),
				Protocol:      v1.ProtocolTCP,
			},
			{
				Name:          "dashboard",
				ContainerPort: int32(port),
				Protocol:      v1.ProtocolTCP,
			},
		},
		Env:       k8sutil.ClusterDaemonEnvVars(),
		Resources: c.resources,
	}
	container.Env = append(container.Env, opmon.ClusterNameEnvVar(c.Namespace))
	return container
}

func (c *Cluster) makeMetricsService(name string) *v1.Service {
	labels := opspec.AppLabels(appName, c.Namespace)
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: c.Namespace,
			Labels:    labels,
		},
		Spec: v1.ServiceSpec{
			Selector: labels,
			Type:     v1.ServiceTypeClusterIP,
			Ports: []v1.ServicePort{
				{
					Name:     "http-metrics",
					Port:     int32(metricsPort),
					Protocol: v1.ProtocolTCP,
				},
			},
		},
	}

	k8sutil.SetOwnerRef(c.context.Clientset, c.Namespace, &svc.ObjectMeta, &c.ownerRef)
	return svc
}

func (c *Cluster) makeDashboardService(name string, port int) *v1.Service {
	labels := opspec.AppLabels(appName, c.Namespace)
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf("%s-dashboard", name),
			Namespace: c.Namespace,
			Labels:    labels,
		},
		Spec: v1.ServiceSpec{
			Selector: labels,
			Type:     v1.ServiceTypeClusterIP,
			Ports: []v1.ServicePort{
				{
					Name:     "https-dashboard",
					Port:     int32(port),
					Protocol: v1.ProtocolTCP,
				},
			},
		},
	}
	k8sutil.SetOwnerRef(c.context.Clientset, c.Namespace, &svc.ObjectMeta, &c.ownerRef)
	return svc
}

func (c *Cluster) getPodLabels(daemonName string) map[string]string {
	labels := opspec.PodLabels(appName, c.Namespace, "mgr", daemonName)
	// leave "instance" key for legacy usage
	labels["instance"] = daemonName
	return labels
}
<commit_msg>mgr: use Recreate update strategy<commit_after>/*
Copyright 2018 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mgr

import (
	"fmt"
	"strconv"

	mgrdaemon "github.com/rook/rook/pkg/daemon/ceph/mgr"
	opmon "github.com/rook/rook/pkg/operator/ceph/cluster/mon"
	opspec "github.com/rook/rook/pkg/operator/ceph/spec"
	"github.com/rook/rook/pkg/operator/k8sutil"
	"k8s.io/api/core/v1"
	extensions "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	mgrDaemonCommand = "ceph-mgr"
)

func (c *Cluster) makeDeployment(mgrConfig *mgrConfig, port int) *extensions.Deployment {
	podSpec := v1.PodTemplateSpec{
		ObjectMeta: metav1.ObjectMeta{
			Name:   mgrConfig.ResourceName,
			Labels: c.getPodLabels(mgrConfig.DaemonName),
			Annotations: map[string]string{"prometheus.io/scrape": "true",
				"prometheus.io/port": strconv.Itoa(metricsPort)},
		},
		Spec: v1.PodSpec{
			InitContainers: []v1.Container{
				// Config file init performed by Rook
				c.makeConfigInitContainer(mgrConfig),
			},
			Containers: []v1.Container{
				c.makeMgrDaemonContainer(mgrConfig, port),
			},
			ServiceAccountName: serviceAccountName,
			RestartPolicy:      v1.RestartPolicyAlways,
			Volumes:            opspec.PodVolumes(""),
			HostNetwork:        c.HostNetwork,
		},
	}
	if c.HostNetwork {
		podSpec.Spec.DNSPolicy = v1.DNSClusterFirstWithHostNet
	}
	c.placement.ApplyToPodSpec(&podSpec.Spec)

	replicas := int32(1)
	d := &extensions.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:      mgrConfig.ResourceName,
			Namespace: c.Namespace,
		},
		Spec: extensions.DeploymentSpec{
			Template: podSpec,
			Replicas: &replicas,
			Strategy: extensions.DeploymentStrategy{
				Type: extensions.RecreateDeploymentStrategyType,
			},
		},
	}
	k8sutil.SetOwnerRef(c.context.Clientset, c.Namespace, &d.ObjectMeta, &c.ownerRef)
	return d
}

func (c *Cluster) makeConfigInitContainer(mgrConfig *mgrConfig) v1.Container {
	return v1.Container{
		Name: opspec.ConfigInitContainerName,
		Args: []string{
			"ceph",
			mgrdaemon.InitCommand,
			fmt.Sprintf("--config-dir=%s", k8sutil.DataDir),
			fmt.Sprintf("--mgr-name=%s", mgrConfig.DaemonName),
		},
		Image: k8sutil.MakeRookImage(c.rookVersion),
		Env: []v1.EnvVar{
			// Set '--mgr-keyring' flag with an env var sourced from the secret
			{Name: "ROOK_MGR_KEYRING",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{Name: mgrConfig.ResourceName},
						Key:                  opspec.KeyringSecretKeyName,
					}}},
			k8sutil.PodIPEnvVar(k8sutil.PrivateIPEnvVar),
			k8sutil.PodIPEnvVar(k8sutil.PublicIPEnvVar),
			opmon.EndpointEnvVar(),
			opmon.SecretEnvVar(),
			opmon.AdminSecretEnvVar(),
			k8sutil.ConfigOverrideEnvVar(),
		},
		VolumeMounts: opspec.RookVolumeMounts(),
		// config file creation does not require ports to be open
		Resources: c.resources,
	}
}

func (c *Cluster) makeMgrDaemonContainer(mgrConfig *mgrConfig, port int) v1.Container {
	container := v1.Container{
		Name: "mgr",
		Command: []string{
			mgrDaemonCommand,
		},
		Args: []string{
			"--foreground",
			"--id", mgrConfig.DaemonName,
			// do not add the '--cluster/--conf/--keyring' flags; rook wants their default values
		},
		Image:        c.cephVersion.Image,
		VolumeMounts: opspec.CephVolumeMounts(),
		Ports: []v1.ContainerPort{
			{
				Name:          "mgr",
				ContainerPort: int32(6800),
				Protocol:      v1.ProtocolTCP,
			},
			{
				Name:          "http-metrics",
				ContainerPort: int32(metricsPort),
				Protocol:      v1.ProtocolTCP,
			},
			{
				Name:          "dashboard",
				ContainerPort: int32(port),
				Protocol:      v1.ProtocolTCP,
			},
		},
		Env:       k8sutil.ClusterDaemonEnvVars(),
		Resources: c.resources,
	}
	container.Env = append(container.Env, opmon.ClusterNameEnvVar(c.Namespace))
	return container
}

func (c *Cluster) makeMetricsService(name string) *v1.Service {
	labels := opspec.AppLabels(appName, c.Namespace)
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: c.Namespace,
			Labels:    labels,
		},
		Spec: v1.ServiceSpec{
			Selector: labels,
			Type:     v1.ServiceTypeClusterIP,
			Ports: []v1.ServicePort{
				{
					Name:     "http-metrics",
					Port:     int32(metricsPort),
					Protocol: v1.ProtocolTCP,
				},
			},
		},
	}

	k8sutil.SetOwnerRef(c.context.Clientset, c.Namespace, &svc.ObjectMeta, &c.ownerRef)
	return svc
}

func (c *Cluster) makeDashboardService(name string, port int) *v1.Service {
	labels := opspec.AppLabels(appName, c.Namespace)
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf("%s-dashboard", name),
			Namespace: c.Namespace,
			Labels:    labels,
		},
		Spec: v1.ServiceSpec{
			Selector: labels,
			Type:     v1.ServiceTypeClusterIP,
			Ports: []v1.ServicePort{
				{
					Name:     "https-dashboard",
					Port:     int32(port),
					Protocol: v1.ProtocolTCP,
				},
			},
		},
	}
	k8sutil.SetOwnerRef(c.context.Clientset, c.Namespace, &svc.ObjectMeta, &c.ownerRef)
	return svc
}

func (c *Cluster) getPodLabels(daemonName string) map[string]string {
	labels := opspec.PodLabels(appName, c.Namespace, "mgr", daemonName)
	// leave "instance" key for legacy usage
	labels["instance"] = daemonName
	return labels
}
<|endoftext|>


<commit_before>package cniserver

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/golang/glog"
	"github.com/gorilla/mux"
)

// *** The CNIServer is PRIVATE API between OpenShift SDN components and may be
// changed at any time.  It is in no way a supported interface or API. ***
//
// The CNIServer accepts pod setup/teardown requests from the OpenShift SDN
// CNI plugin, which is itself called by openshift-node when pod networking
// should be set up or torn down.  The OpenShift SDN CNI plugin gathers up
// the standard CNI environment variables and network configuration provided
// on stdin and forwards them to the CNIServer over a private, root-only
// Unix domain socket, using HTTP as the transport and JSON as the protocol.
//
// The CNIServer interprets standard CNI environment variables as specified
// by the Container Network Interface (CNI) specification available here:
// https://github.com/containernetworking/cni/blob/master/SPEC.md
// While the CNIServer interface is not itself versioned, as the CNI
// specification requires that CNI network configuration is versioned, and
// since the OpenShift SDN CNI plugin passes that configuration to the
// CNIServer, versioning is ensured in exactly the same way as an executable
// CNI plugin would be versioned.
//
// Security: since the Unix domain socket created by the CNIServer is owned
// by root and inaccessible to any other user, no unprivileged process may
// access the CNIServer.  The Unix domain socket and its parent directory are
// removed and re-created with 0700 permissions each time openshift-node is
// started.

// Default CNIServer unix domain socket path which the OpenShift SDN CNI
// plugin uses to talk to the CNIServer
const CNIServerSocketPath string = "/var/run/openshift-sdn/cni-server.sock"

// Explicit type for CNI commands the server handles
type CNICommand string

const CNI_ADD CNICommand = "ADD"
const CNI_UPDATE CNICommand = "UPDATE"
const CNI_DEL CNICommand = "DEL"

// Request sent to the CNIServer by the OpenShift SDN CNI plugin
type CNIRequest struct {
	// CNI environment variables, like CNI_COMMAND and CNI_NETNS
	Env map[string]string `json:"env,omitempty"`
	// CNI configuration passed via stdin to the CNI plugin
	Config []byte `json:"config,omitempty"`
}

// Request structure built from CNIRequest which is passed to the
// handler function given to the CNIServer at creation time
type PodRequest struct {
	// The CNI command of the operation
	Command CNICommand
	// kubernetes namespace name
	PodNamespace string
	// kubernetes pod name
	PodName string
	// kubernetes container ID
	ContainerId string
	// kernel network namespace path
	Netns string
	// Channel for returning the operation result to the CNIServer
	Result chan *PodResult
}

// Result of a PodRequest sent through the PodRequest's Result channel.
type PodResult struct {
	// Response to be returned to the OpenShift SDN CNI plugin on success
	Response []byte
	// Error to be returned to the OpenShift SDN CNI plugin on failure
	Err error
}

type cniRequestFunc func(request *PodRequest) ([]byte, error)

// CNI server object that listens for JSON-marshaled CNIRequest objects
// on a private root-only Unix domain socket.
type CNIServer struct {
	http.Server
	requestFunc cniRequestFunc
	path        string
}

// Create and return a new CNIServer object which will listen on the given
// socket path
func NewCNIServer(socketPath string) *CNIServer {
	router := mux.NewRouter()

	s := &CNIServer{
		Server: http.Server{
			Handler: router,
		},
		path: socketPath,
	}
	router.NotFoundHandler = http.HandlerFunc(http.NotFound)
	router.HandleFunc("/", s.handleCNIRequest).Methods("POST")
	return s
}

// Start the CNIServer's local HTTP server on a root-owned Unix domain socket.
// requestFunc will be called to handle pod setup/teardown operations on each
// request to the CNIServer's HTTP server, and should return a PodResult
// when the operation has completed.
func (s *CNIServer) Start(requestFunc cniRequestFunc) error {
	if requestFunc == nil {
		return fmt.Errorf("no pod request handler")
	}
	s.requestFunc = requestFunc

	// Remove and re-create the socket directory with root-only permissions
	dirName := path.Dir(s.path)
	if err := os.RemoveAll(dirName); err != nil {
		return fmt.Errorf("failed to removing old pod info socket: %v", err)
	}
	if err := os.MkdirAll(dirName, 0700); err != nil {
		return fmt.Errorf("failed to create pod info socket directory: %v", err)
	}

	// On Linux the socket is created with the permissions of the directory
	// it is in, so as long as the directory is root-only we can avoid
	// racy umask manipulation.
	l, err := net.Listen("unix", s.path)
	if err != nil {
		return fmt.Errorf("failed to listen on pod info socket: %v", err)
	}
	if err := os.Chmod(s.path, 0600); err != nil {
		l.Close()
		return fmt.Errorf("failed to set pod info socket mode: %v", err)
	}

	s.SetKeepAlivesEnabled(false)
	go s.Serve(l)
	return nil
}

// Split the "CNI_ARGS" environment variable's value into a map.  CNI_ARGS
// contains arbitrary key/value pairs separated by ';' and is for runtime or
// plugin specific uses.  Kubernetes passes the pod namespace and name in
// CNI_ARGS.
func gatherCNIArgs(env map[string]string) (map[string]string, error) {
	cniArgs, ok := env["CNI_ARGS"]
	if !ok {
		return nil, fmt.Errorf("missing CNI_ARGS: '%s'", env)
	}

	mapArgs := make(map[string]string)
	for _, arg := range strings.Split(cniArgs, ";") {
		parts := strings.Split(arg, "=")
		if len(parts) != 2 {
			return nil, fmt.Errorf("invalid CNI_ARG '%s'", arg)
		}
		mapArgs[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
	}
	return mapArgs, nil
}

func cniRequestToPodRequest(r *http.Request) (*PodRequest, error) {
	var cr CNIRequest
	b, _ := ioutil.ReadAll(r.Body)
	if err := json.Unmarshal(b, &cr); err != nil {
		return nil, fmt.Errorf("JSON unmarshal error: %v", err)
	}

	cmd, ok := cr.Env["CNI_COMMAND"]
	if !ok {
		return nil, fmt.Errorf("Unexpected or missing CNI_COMMAND")
	}

	req := &PodRequest{
		Command: CNICommand(cmd),
		Result:  make(chan *PodResult),
	}

	req.ContainerId, ok = cr.Env["CNI_CONTAINERID"]
	if !ok {
		return nil, fmt.Errorf("missing CNI_CONTAINERID")
	}
	req.Netns, ok = cr.Env["CNI_NETNS"]
	if !ok {
		return nil, fmt.Errorf("missing CNI_NETNS")
	}

	cniArgs, err := gatherCNIArgs(cr.Env)
	if err != nil {
		return nil, err
	}

	req.PodNamespace, ok = cniArgs["K8S_POD_NAMESPACE"]
	if err != nil {
		return nil, fmt.Errorf("missing K8S_POD_NAMESPACE")
	}

	req.PodName, ok = cniArgs["K8S_POD_NAME"]
	if err != nil {
		return nil, fmt.Errorf("missing K8S_POD_NAME")
	}

	return req, nil
}

// Dispatch a pod request to the request handler and return the result to the
// CNI server client
func (s *CNIServer) handleCNIRequest(w http.ResponseWriter, r *http.Request) {
	req, err := cniRequestToPodRequest(r)
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	glog.V(5).Infof("Waiting for %s result for pod %s/%s", req.Command, req.PodNamespace, req.PodName)
	result, err := s.requestFunc(req)
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
	} else {
		// Empty response JSON means success with no body
		w.Header().Set("Content-Type", "application/json")
		if _, err := w.Write(result); err != nil {
			glog.Warningf("Error writing %s HTTP response: %v", req.Command, err)
		}
	}
}
<commit_msg>sdn: run pod manager server forever<commit_after>package cniserver

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/golang/glog"
	"github.com/gorilla/mux"

	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	utilwait "k8s.io/kubernetes/pkg/util/wait"
)

// *** The CNIServer is PRIVATE API between OpenShift SDN components and may be
// changed at any time.  It is in no way a supported interface or API. ***
//
// The CNIServer accepts pod setup/teardown requests from the OpenShift SDN
// CNI plugin, which is itself called by openshift-node when pod networking
// should be set up or torn down.  The OpenShift SDN CNI plugin gathers up
// the standard CNI environment variables and network configuration provided
// on stdin and forwards them to the CNIServer over a private, root-only
// Unix domain socket, using HTTP as the transport and JSON as the protocol.
//
// The CNIServer interprets standard CNI environment variables as specified
// by the Container Network Interface (CNI) specification available here:
// https://github.com/containernetworking/cni/blob/master/SPEC.md
// While the CNIServer interface is not itself versioned, as the CNI
// specification requires that CNI network configuration is versioned, and
// since the OpenShift SDN CNI plugin passes that configuration to the
// CNIServer, versioning is ensured in exactly the same way as an executable
// CNI plugin would be versioned.
//
// Security: since the Unix domain socket created by the CNIServer is owned
// by root and inaccessible to any other user, no unprivileged process may
// access the CNIServer.  The Unix domain socket and its parent directory are
// removed and re-created with 0700 permissions each time openshift-node is
// started.

// Default CNIServer unix domain socket path which the OpenShift SDN CNI
// plugin uses to talk to the CNIServer
const CNIServerSocketPath string = "/var/run/openshift-sdn/cni-server.sock"

// Explicit type for CNI commands the server handles
type CNICommand string

const CNI_ADD CNICommand = "ADD"
const CNI_UPDATE CNICommand = "UPDATE"
const CNI_DEL CNICommand = "DEL"

// Request sent to the CNIServer by the OpenShift SDN CNI plugin
type CNIRequest struct {
	// CNI environment variables, like CNI_COMMAND and CNI_NETNS
	Env map[string]string `json:"env,omitempty"`
	// CNI configuration passed via stdin to the CNI plugin
	Config []byte `json:"config,omitempty"`
}

// Request structure built from CNIRequest which is passed to the
// handler function given to the CNIServer at creation time
type PodRequest struct {
	// The CNI command of the operation
	Command CNICommand
	// kubernetes namespace name
	PodNamespace string
	// kubernetes pod name
	PodName string
	// kubernetes container ID
	ContainerId string
	// kernel network namespace path
	Netns string
	// Channel for returning the operation result to the CNIServer
	Result chan *PodResult
}

// Result of a PodRequest sent through the PodRequest's Result channel.
type PodResult struct {
	// Response to be returned to the OpenShift SDN CNI plugin on success
	Response []byte
	// Error to be returned to the OpenShift SDN CNI plugin on failure
	Err error
}

type cniRequestFunc func(request *PodRequest) ([]byte, error)

// CNI server object that listens for JSON-marshaled CNIRequest objects
// on a private root-only Unix domain socket.
type CNIServer struct {
	http.Server
	requestFunc cniRequestFunc
	path        string
}

// Create and return a new CNIServer object which will listen on the given
// socket path
func NewCNIServer(socketPath string) *CNIServer {
	router := mux.NewRouter()

	s := &CNIServer{
		Server: http.Server{
			Handler: router,
		},
		path: socketPath,
	}
	router.NotFoundHandler = http.HandlerFunc(http.NotFound)
	router.HandleFunc("/", s.handleCNIRequest).Methods("POST")
	return s
}

// Start the CNIServer's local HTTP server on a root-owned Unix domain socket.
// requestFunc will be called to handle pod setup/teardown operations on each
// request to the CNIServer's HTTP server, and should return a PodResult
// when the operation has completed.
func (s *CNIServer) Start(requestFunc cniRequestFunc) error {
	if requestFunc == nil {
		return fmt.Errorf("no pod request handler")
	}
	s.requestFunc = requestFunc

	// Remove and re-create the socket directory with root-only permissions
	dirName := path.Dir(s.path)
	if err := os.RemoveAll(dirName); err != nil {
		return fmt.Errorf("failed to removing old pod info socket: %v", err)
	}
	if err := os.MkdirAll(dirName, 0700); err != nil {
		return fmt.Errorf("failed to create pod info socket directory: %v", err)
	}

	// On Linux the socket is created with the permissions of the directory
	// it is in, so as long as the directory is root-only we can avoid
	// racy umask manipulation.
	l, err := net.Listen("unix", s.path)
	if err != nil {
		return fmt.Errorf("failed to listen on pod info socket: %v", err)
	}
	if err := os.Chmod(s.path, 0600); err != nil {
		l.Close()
		return fmt.Errorf("failed to set pod info socket mode: %v", err)
	}

	s.SetKeepAlivesEnabled(false)
	go utilwait.Forever(func() {
		if err := s.Serve(l); err != nil {
			utilruntime.HandleError(fmt.Errorf("CNI server Serve() failed: %v", err))
		}
	}, 0)
	return nil
}

// Split the "CNI_ARGS" environment variable's value into a map.  CNI_ARGS
// contains arbitrary key/value pairs separated by ';' and is for runtime or
// plugin specific uses.  Kubernetes passes the pod namespace and name in
// CNI_ARGS.
func gatherCNIArgs(env map[string]string) (map[string]string, error) {
	cniArgs, ok := env["CNI_ARGS"]
	if !ok {
		return nil, fmt.Errorf("missing CNI_ARGS: '%s'", env)
	}

	mapArgs := make(map[string]string)
	for _, arg := range strings.Split(cniArgs, ";") {
		parts := strings.Split(arg, "=")
		if len(parts) != 2 {
			return nil, fmt.Errorf("invalid CNI_ARG '%s'", arg)
		}
		mapArgs[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
	}
	return mapArgs, nil
}

func cniRequestToPodRequest(r *http.Request) (*PodRequest, error) {
	var cr CNIRequest
	b, _ := ioutil.ReadAll(r.Body)
	if err := json.Unmarshal(b, &cr); err != nil {
		return nil, fmt.Errorf("JSON unmarshal error: %v", err)
	}

	cmd, ok := cr.Env["CNI_COMMAND"]
	if !ok {
		return nil, fmt.Errorf("Unexpected or missing CNI_COMMAND")
	}

	req := &PodRequest{
		Command: CNICommand(cmd),
		Result:  make(chan *PodResult),
	}

	req.ContainerId, ok = cr.Env["CNI_CONTAINERID"]
	if !ok {
		return nil, fmt.Errorf("missing CNI_CONTAINERID")
	}
	req.Netns, ok = cr.Env["CNI_NETNS"]
	if !ok {
		return nil, fmt.Errorf("missing CNI_NETNS")
	}

	cniArgs, err := gatherCNIArgs(cr.Env)
	if err != nil {
		return nil, err
	}

	req.PodNamespace, ok = cniArgs["K8S_POD_NAMESPACE"]
	if err != nil {
		return nil, fmt.Errorf("missing K8S_POD_NAMESPACE")
	}

	req.PodName, ok = cniArgs["K8S_POD_NAME"]
	if err != nil {
		return nil, fmt.Errorf("missing K8S_POD_NAME")
	}

	return req, nil
}

// Dispatch a pod request to the request handler and return the result to the
// CNI server client
func (s *CNIServer) handleCNIRequest(w http.ResponseWriter, r *http.Request) {
	req, err := cniRequestToPodRequest(r)
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
		return
	}

	glog.V(5).Infof("Waiting for %s result for pod %s/%s", req.Command, req.PodNamespace, req.PodName)
	result, err := s.requestFunc(req)
	if err != nil {
		http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
	} else {
		// Empty response JSON means success with no body
		w.Header().Set("Content-Type", "application/json")
		if _, err := w.Write(result); err != nil {
			glog.Warningf("Error writing %s HTTP response: %v", req.Command, err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015-present Oursky Ltd.
//
// 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 migration

import (
	"fmt"

	"github.com/jmoiron/sqlx"
	"github.com/skygeario/skygear-server/pkg/server/uuid"
	"golang.org/x/crypto/bcrypt"
)

type fullMigration struct {
}

func (r *fullMigration) Version() string { return "f0c53134d25d" }

func (r *fullMigration) createTable(tx *sqlx.Tx) error {
	const stmt = `
CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA public;
CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public;
CREATE TABLE IF NOT EXISTS public.pending_notification (
	id SERIAL NOT NULL PRIMARY KEY,
	op text NOT NULL,
	appname text NOT NULL,
	recordtype text NOT NULL,
	record jsonb NOT NULL
);
CREATE OR REPLACE FUNCTION public.notify_record_change() RETURNS TRIGGER AS $$
	DECLARE
		affected_record RECORD;
		inserted_id integer;
	BEGIN
		IF (TG_OP = 'DELETE') THEN
			affected_record := OLD;
		ELSE
			affected_record := NEW;
		END IF;
		INSERT INTO public.pending_notification (op, appname, recordtype, record)
			VALUES (TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME, row_to_json(affected_record)::jsonb)
			RETURNING id INTO inserted_id;
		PERFORM pg_notify('record_change', inserted_id::TEXT);
		RETURN affected_record;
	END;
$$ LANGUAGE plpgsql;

CREATE TABLE _auth (
	id text PRIMARY KEY,
	password text,
	provider_info jsonb,
	token_valid_since timestamp without time zone,
	last_login_at timestamp without time zone,
	last_seen_at timestamp without time zone
);

CREATE VIEW _user AS
SELECT * FROM _auth;

CREATE TABLE _role (
	id text PRIMARY KEY,
	by_default boolean DEFAULT FALSE,
	is_admin boolean DEFAULT FALSE
);

CREATE TABLE _auth_role (
	auth_id text REFERENCES _auth (id) NOT NULL,
	role_id text REFERENCES _role (id) NOT NULL,
	PRIMARY KEY (auth_id, role_id)
);

CREATE TABLE _asset (
	id text PRIMARY KEY,
	content_type text NOT NULL,
	size bigint NOT NULL
);
CREATE TABLE _device (
	id text PRIMARY KEY,
	auth_id text REFERENCES _auth (id),
	type text NOT NULL,
	token text,
	topic text,
	last_registered_at timestamp without time zone NOT NULL,
	UNIQUE (auth_id, type, token)
);
CREATE INDEX ON _device (token, last_registered_at);
CREATE TABLE _subscription (
	id text NOT NULL,
	auth_id text NOT NULL,
	device_id text REFERENCES _device (id) ON DELETE CASCADE NOT NULL,
	type text NOT NULL,
	notification_info jsonb,
	query jsonb,
	PRIMARY KEY(auth_id, device_id, id)
);
CREATE TABLE _friend (
	left_id text NOT NULL,
	right_id text REFERENCES _auth (id) NOT NULL,
	PRIMARY KEY(left_id, right_id)
);
CREATE TABLE _follow (
	left_id text NOT NULL,
	right_id text REFERENCES _auth (id) NOT NULL,
	PRIMARY KEY(left_id, right_id)
);
CREATE TABLE _record_creation (
    record_type text NOT NULL,
    role_id text,
    UNIQUE (record_type, role_id),
    FOREIGN KEY (role_id) REFERENCES _role(id)
);
CREATE INDEX _record_creation_unique_record_type ON _record_creation (record_type);
CREATE TABLE _record_default_access (
    record_type text NOT NULL,
    default_access jsonb,
    UNIQUE (record_type)
);
CREATE INDEX _record_default_access_unique_record_type ON _record_default_access (record_type);
CREATE TABLE _record_field_access (
    record_type text NOT NULL,
    record_field text NOT NULL,
    user_role text NOT NULL,
    writable boolean NOT NULL,
    readable boolean NOT NULL,
    comparable boolean NOT NULL,
    discoverable boolean NOT NULL,
    PRIMARY KEY (record_type, record_field, user_role)
);
CREATE TABLE "user" (
    _id text,
    _database_id text,
    _owner_id text,
    _access jsonb,
    _created_at timestamp without time zone NOT NULL,
    _created_by text,
    _updated_at timestamp without time zone NOT NULL,
    _updated_by text,
    username citext,
    email citext,
    PRIMARY KEY(_id, _database_id, _owner_id),
    UNIQUE (_id),
    UNIQUE (username),
    UNIQUE (email)
);
`
	_, err := tx.Exec(stmt)
	return err
}

func (r *fullMigration) insertSeedData(tx *sqlx.Tx) error {
	newUserID := uuid.New()
	hashedPassword, err := bcrypt.GenerateFromPassword(
		[]byte(adminUserDefaultPassword),
		bcrypt.DefaultCost,
	)

	if err != nil {
		return err
	}

	stmts := []string{
		fmt.Sprintf(
			`INSERT INTO _role (id, is_admin) VALUES ('%s', TRUE)`,
			adminRoleDefaultName,
		),
		fmt.Sprintf(
			`INSERT INTO _auth (id, password) VALUES ('%s', '%s')`,
			newUserID,
			hashedPassword,
		),
		fmt.Sprintf(
			`INSERT INTO _auth_role (auth_id, role_id) VALUES('%s', '%s')`,
			newUserID,
			adminRoleDefaultName,
		),
	}

	for _, stmt := range stmts {
		if _, err := tx.Exec(stmt); err != nil {
			return err
		}
	}

	return nil
}

func (r *fullMigration) Up(tx *sqlx.Tx) error {
	var err error
	if err = r.createTable(tx); err != nil {
		return err
	}

	if err = r.insertSeedData(tx); err != nil {
		return err
	}

	return nil
}

func (r *fullMigration) Down(tx *sqlx.Tx) error {
	panic("cannot downgrade from a base revision")
}
<commit_msg>Update view `_user` to include username and email in full.go<commit_after>// Copyright 2015-present Oursky Ltd.
//
// 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 migration

import (
	"fmt"

	"github.com/jmoiron/sqlx"
	"github.com/skygeario/skygear-server/pkg/server/uuid"
	"golang.org/x/crypto/bcrypt"
)

type fullMigration struct {
}

func (r *fullMigration) Version() string { return "f0c53134d25d" }

func (r *fullMigration) createTable(tx *sqlx.Tx) error {
	const stmt = `
CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA public;
CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public;
CREATE TABLE IF NOT EXISTS public.pending_notification (
	id SERIAL NOT NULL PRIMARY KEY,
	op text NOT NULL,
	appname text NOT NULL,
	recordtype text NOT NULL,
	record jsonb NOT NULL
);
CREATE OR REPLACE FUNCTION public.notify_record_change() RETURNS TRIGGER AS $$
	DECLARE
		affected_record RECORD;
		inserted_id integer;
	BEGIN
		IF (TG_OP = 'DELETE') THEN
			affected_record := OLD;
		ELSE
			affected_record := NEW;
		END IF;
		INSERT INTO public.pending_notification (op, appname, recordtype, record)
			VALUES (TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME, row_to_json(affected_record)::jsonb)
			RETURNING id INTO inserted_id;
		PERFORM pg_notify('record_change', inserted_id::TEXT);
		RETURN affected_record;
	END;
$$ LANGUAGE plpgsql;

CREATE TABLE _auth (
	id text PRIMARY KEY,
	password text,
	provider_info jsonb,
	token_valid_since timestamp without time zone,
	last_login_at timestamp without time zone,
	last_seen_at timestamp without time zone
);

CREATE TABLE _role (
	id text PRIMARY KEY,
	by_default boolean DEFAULT FALSE,
	is_admin boolean DEFAULT FALSE
);

CREATE TABLE _auth_role (
	auth_id text REFERENCES _auth (id) NOT NULL,
	role_id text REFERENCES _role (id) NOT NULL,
	PRIMARY KEY (auth_id, role_id)
);

CREATE TABLE _asset (
	id text PRIMARY KEY,
	content_type text NOT NULL,
	size bigint NOT NULL
);
CREATE TABLE _device (
	id text PRIMARY KEY,
	auth_id text REFERENCES _auth (id),
	type text NOT NULL,
	token text,
	topic text,
	last_registered_at timestamp without time zone NOT NULL,
	UNIQUE (auth_id, type, token)
);
CREATE INDEX ON _device (token, last_registered_at);
CREATE TABLE _subscription (
	id text NOT NULL,
	auth_id text NOT NULL,
	device_id text REFERENCES _device (id) ON DELETE CASCADE NOT NULL,
	type text NOT NULL,
	notification_info jsonb,
	query jsonb,
	PRIMARY KEY(auth_id, device_id, id)
);
CREATE TABLE _friend (
	left_id text NOT NULL,
	right_id text REFERENCES _auth (id) NOT NULL,
	PRIMARY KEY(left_id, right_id)
);
CREATE TABLE _follow (
	left_id text NOT NULL,
	right_id text REFERENCES _auth (id) NOT NULL,
	PRIMARY KEY(left_id, right_id)
);
CREATE TABLE _record_creation (
    record_type text NOT NULL,
    role_id text,
    UNIQUE (record_type, role_id),
    FOREIGN KEY (role_id) REFERENCES _role(id)
);
CREATE INDEX _record_creation_unique_record_type ON _record_creation (record_type);
CREATE TABLE _record_default_access (
    record_type text NOT NULL,
    default_access jsonb,
    UNIQUE (record_type)
);
CREATE INDEX _record_default_access_unique_record_type ON _record_default_access (record_type);
CREATE TABLE _record_field_access (
    record_type text NOT NULL,
    record_field text NOT NULL,
    user_role text NOT NULL,
    writable boolean NOT NULL,
    readable boolean NOT NULL,
    comparable boolean NOT NULL,
    discoverable boolean NOT NULL,
    PRIMARY KEY (record_type, record_field, user_role)
);
CREATE TABLE "user" (
    _id text,
    _database_id text,
    _owner_id text,
    _access jsonb,
    _created_at timestamp without time zone NOT NULL,
    _created_by text,
    _updated_at timestamp without time zone NOT NULL,
    _updated_by text,
    username citext,
    email citext,
    PRIMARY KEY(_id, _database_id, _owner_id),
    UNIQUE (_id),
    UNIQUE (username),
    UNIQUE (email)
);
CREATE VIEW _user AS
	SELECT
		_auth.id,
		_auth.password,
		"user".username,
		"user".email,
		_auth.provider_info AS auth,
		_auth.token_valid_since,
		_auth.last_login_at,
		_auth.last_seen_at
	FROM _auth
	JOIN "user" ON "user"._id = _auth.id;
`
	_, err := tx.Exec(stmt)
	return err
}

func (r *fullMigration) insertSeedData(tx *sqlx.Tx) error {
	newUserID := uuid.New()
	hashedPassword, err := bcrypt.GenerateFromPassword(
		[]byte(adminUserDefaultPassword),
		bcrypt.DefaultCost,
	)

	if err != nil {
		return err
	}

	stmts := []string{
		fmt.Sprintf(
			`INSERT INTO _role (id, is_admin) VALUES ('%s', TRUE)`,
			adminRoleDefaultName,
		),
		fmt.Sprintf(
			`INSERT INTO _auth (id, password) VALUES ('%s', '%s')`,
			newUserID,
			hashedPassword,
		),
		fmt.Sprintf(
			`INSERT INTO _auth_role (auth_id, role_id) VALUES('%s', '%s')`,
			newUserID,
			adminRoleDefaultName,
		),
	}

	for _, stmt := range stmts {
		if _, err := tx.Exec(stmt); err != nil {
			return err
		}
	}

	return nil
}

func (r *fullMigration) Up(tx *sqlx.Tx) error {
	var err error
	if err = r.createTable(tx); err != nil {
		return err
	}

	if err = r.insertSeedData(tx); err != nil {
		return err
	}

	return nil
}

func (r *fullMigration) Down(tx *sqlx.Tx) error {
	panic("cannot downgrade from a base revision")
}
<|endoftext|>


<commit_before>// Copyright Istio 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 endpoint

import (
	"bytes"
	"context"
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"

	"istio.io/istio/pkg/test/echo/common"
	"istio.io/istio/pkg/test/echo/common/response"
	"istio.io/istio/pkg/test/echo/proto"
	"istio.io/istio/pkg/test/echo/server/forwarder"
	"istio.io/istio/pkg/test/util/retry"
)

var _ Instance = &grpcInstance{}

type grpcInstance struct {
	Config
	server *grpc.Server
}

func newGRPC(config Config) Instance {
	return &grpcInstance{
		Config: config,
	}
}

func (s *grpcInstance) GetConfig() Config {
	return s.Config
}

func (s *grpcInstance) Start(onReady OnReadyFunc) error {
	// Listen on the given port and update the port if it changed from what was passed in.
	listener, p, err := listenOnPort(s.Port.Port)
	if err != nil {
		return err
	}
	// Store the actual listening port back to the argument.
	s.Port.Port = p

	if s.Port.TLS {
		fmt.Printf("Listening GRPC (over TLS) on %v\n", p)
		// Create the TLS credentials
		creds, errCreds := credentials.NewServerTLSFromFile(s.TLSCert, s.TLSKey)
		if errCreds != nil {
			epLog.Errorf("could not load TLS keys: %s", errCreds)
		}
		s.server = grpc.NewServer(grpc.Creds(creds))
	} else {
		fmt.Printf("Listening GRPC on %v\n", p)
		s.server = grpc.NewServer()
	}
	proto.RegisterEchoTestServiceServer(s.server, &grpcHandler{
		Config: s.Config,
	})

	// Start serving GRPC traffic.
	go func() {
		_ = s.server.Serve(listener)
	}()

	// Notify the WaitGroup once the port has transitioned to ready.
	go s.awaitReady(onReady, listener)

	return nil
}

func (s *grpcInstance) awaitReady(onReady OnReadyFunc, listener net.Listener) {
	defer onReady()

	err := retry.UntilSuccess(func() error {
		f, err := forwarder.New(forwarder.Config{
			Request: &proto.ForwardEchoRequest{
				Url:           "grpc://" + listener.Addr().String(),
				Message:       "hello",
				TimeoutMicros: common.DurationToMicros(readyInterval),
			},
		})
		defer func() {
			_ = f.Close()
		}()

		if err != nil {
			return err
		}
		_, err = f.Run(context.Background())
		return err
	}, retry.Timeout(readyTimeout), retry.Delay(readyInterval))
	if err != nil {
		epLog.Errorf("readiness failed for GRPC endpoint %s: %v", listener.Addr().String(), err)
	} else {
		epLog.Infof("ready for GRPC endpoint %s", listener.Addr().String())
	}
}

func (s *grpcInstance) Close() error {
	if s.server != nil {
		s.server.Stop()
	}
	return nil
}

type grpcHandler struct {
	Config
}

func (h *grpcHandler) Echo(ctx context.Context, req *proto.EchoRequest) (*proto.EchoResponse, error) {
	defer common.Metrics.GrpcRequests.With(common.PortLabel.Value(strconv.Itoa(h.Port.Port))).Increment()
	host := "-"
	body := bytes.Buffer{}
	md, ok := metadata.FromIncomingContext(ctx)
	if ok {
		for key, values := range md {
			if strings.HasSuffix(key, "-bin") {
				continue
			}
			field := response.Field(key)
			if key == ":authority" {
				field = response.HostField
				host = values[0]
			}
			for _, value := range values {
				writeField(&body, field, value)
			}
		}
	}

	epLog.Infof("GRPC Request:\n  Host: %s\n  Message: %s\n  Headers: %v\n", host, req.GetMessage(), md)

	portNumber := 0
	if h.Port != nil {
		portNumber = h.Port.Port
	}

	writeField(&body, response.StatusCodeField, response.StatusCodeOK)
	writeField(&body, response.ServiceVersionField, h.Version)
	writeField(&body, response.ServicePortField, strconv.Itoa(portNumber))
	writeField(&body, response.ClusterField, h.Cluster)
	writeField(&body, "Echo", req.GetMessage())

	if hostname, err := os.Hostname(); err == nil {
		writeField(&body, response.HostnameField, hostname)
	}

	return &proto.EchoResponse{Message: body.String()}, nil
}

func (h *grpcHandler) ForwardEcho(ctx context.Context, req *proto.ForwardEchoRequest) (*proto.ForwardEchoResponse, error) {
	epLog.Infof("ForwardEcho[%s] request", req.Url)
	instance, err := forwarder.New(forwarder.Config{
		Request: req,
		Dialer:  h.Dialer,
		TLSCert: h.TLSCert,
	})
	if err != nil {
		return nil, err
	}
	defer instance.Close()

	ret, err := instance.Run(ctx)
	epLog.Infof("ForwardEcho[%s] response: %v and error %v", req.Url, ret.GetOutput(), err)
	return ret, err
}
<commit_msg>Add reflection server to echo (#28002)<commit_after>// Copyright Istio 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 endpoint

import (
	"bytes"
	"context"
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/reflection"

	"istio.io/istio/pkg/test/echo/common"
	"istio.io/istio/pkg/test/echo/common/response"
	"istio.io/istio/pkg/test/echo/proto"
	"istio.io/istio/pkg/test/echo/server/forwarder"
	"istio.io/istio/pkg/test/util/retry"
)

var _ Instance = &grpcInstance{}

type grpcInstance struct {
	Config
	server *grpc.Server
}

func newGRPC(config Config) Instance {
	return &grpcInstance{
		Config: config,
	}
}

func (s *grpcInstance) GetConfig() Config {
	return s.Config
}

func (s *grpcInstance) Start(onReady OnReadyFunc) error {
	// Listen on the given port and update the port if it changed from what was passed in.
	listener, p, err := listenOnPort(s.Port.Port)
	if err != nil {
		return err
	}
	// Store the actual listening port back to the argument.
	s.Port.Port = p

	if s.Port.TLS {
		fmt.Printf("Listening GRPC (over TLS) on %v\n", p)
		// Create the TLS credentials
		creds, errCreds := credentials.NewServerTLSFromFile(s.TLSCert, s.TLSKey)
		if errCreds != nil {
			epLog.Errorf("could not load TLS keys: %s", errCreds)
		}
		s.server = grpc.NewServer(grpc.Creds(creds))
	} else {
		fmt.Printf("Listening GRPC on %v\n", p)
		s.server = grpc.NewServer()
	}
	proto.RegisterEchoTestServiceServer(s.server, &grpcHandler{
		Config: s.Config,
	})
	reflection.Register(s.server)

	// Start serving GRPC traffic.
	go func() {
		_ = s.server.Serve(listener)
	}()

	// Notify the WaitGroup once the port has transitioned to ready.
	go s.awaitReady(onReady, listener)

	return nil
}

func (s *grpcInstance) awaitReady(onReady OnReadyFunc, listener net.Listener) {
	defer onReady()

	err := retry.UntilSuccess(func() error {
		f, err := forwarder.New(forwarder.Config{
			Request: &proto.ForwardEchoRequest{
				Url:           "grpc://" + listener.Addr().String(),
				Message:       "hello",
				TimeoutMicros: common.DurationToMicros(readyInterval),
			},
		})
		defer func() {
			_ = f.Close()
		}()

		if err != nil {
			return err
		}
		_, err = f.Run(context.Background())
		return err
	}, retry.Timeout(readyTimeout), retry.Delay(readyInterval))
	if err != nil {
		epLog.Errorf("readiness failed for GRPC endpoint %s: %v", listener.Addr().String(), err)
	} else {
		epLog.Infof("ready for GRPC endpoint %s", listener.Addr().String())
	}
}

func (s *grpcInstance) Close() error {
	if s.server != nil {
		s.server.Stop()
	}
	return nil
}

type grpcHandler struct {
	Config
}

func (h *grpcHandler) Echo(ctx context.Context, req *proto.EchoRequest) (*proto.EchoResponse, error) {
	defer common.Metrics.GrpcRequests.With(common.PortLabel.Value(strconv.Itoa(h.Port.Port))).Increment()
	host := "-"
	body := bytes.Buffer{}
	md, ok := metadata.FromIncomingContext(ctx)
	if ok {
		for key, values := range md {
			if strings.HasSuffix(key, "-bin") {
				continue
			}
			field := response.Field(key)
			if key == ":authority" {
				field = response.HostField
				host = values[0]
			}
			for _, value := range values {
				writeField(&body, field, value)
			}
		}
	}

	epLog.Infof("GRPC Request:\n  Host: %s\n  Message: %s\n  Headers: %v\n", host, req.GetMessage(), md)

	portNumber := 0
	if h.Port != nil {
		portNumber = h.Port.Port
	}

	writeField(&body, response.StatusCodeField, response.StatusCodeOK)
	writeField(&body, response.ServiceVersionField, h.Version)
	writeField(&body, response.ServicePortField, strconv.Itoa(portNumber))
	writeField(&body, response.ClusterField, h.Cluster)
	writeField(&body, "Echo", req.GetMessage())

	if hostname, err := os.Hostname(); err == nil {
		writeField(&body, response.HostnameField, hostname)
	}

	return &proto.EchoResponse{Message: body.String()}, nil
}

func (h *grpcHandler) ForwardEcho(ctx context.Context, req *proto.ForwardEchoRequest) (*proto.ForwardEchoResponse, error) {
	epLog.Infof("ForwardEcho[%s] request", req.Url)
	instance, err := forwarder.New(forwarder.Config{
		Request: req,
		Dialer:  h.Dialer,
		TLSCert: h.TLSCert,
	})
	if err != nil {
		return nil, err
	}
	defer instance.Close()

	ret, err := instance.Run(ctx)
	epLog.Infof("ForwardEcho[%s] response: %v and error %v", req.Url, ret.GetOutput(), err)
	return ret, err
}
<|endoftext|>