<commit_before>package carbonserver

import (
	"errors"
	"log"
	"path/filepath"
	"strings"
	"unsafe"
)

type globState struct {
	// c        byte
	// ranges   *[256]bool      // [a-z0-9A-Z]
	// depth int
	exact bool
	root  *globNode

	cur *globNode
}

type globNode struct {
	end        bool
	star       bool
	parent     *globNode
	starParent *globNode
	children   [256]*globNode // make it smaller
}

func (gn *globNode) isNodeEnd() bool {
	return gn.end || gn.children['/'] != nil
}

// TODO: add range value validation
func newGlobState(expr string) (*globState, error) {
	var gs = globState{root: &globNode{}, exact: true}
	var cur = gs.root
	var curStar *globNode
	for i := 0; i < len(expr); i++ {
		c := expr[i]
		switch c {
		case '[':
			gs.exact = false
			// var ranges [256]bool
			s := &globNode{parent: cur, starParent: curStar}
			for i < len(expr) && expr[i] != ']' {
				if expr[i] == '-' {
					if i+1 >= len(expr) {
						return nil, errors.New("glob: missing closing range")
					}

					for j := expr[i-1]; j <= expr[i+1]; j++ {
						cur.children[j] = s
					}

					i += 2
					continue
				}

				cur.children[expr[i]] = s
				i++
			}
			if expr[i] != ']' {
				return nil, errors.New("glob: missing ]")
			}

			// cur.children[c] = s
			cur = s
		case '*':
			gs.exact = false
			// de-dup multi stars: *** -> *
			for ; i+1 < len(expr) && expr[i+1] == '*'; i++ {
			}
			s := &globNode{parent: cur, starParent: curStar, star: true}
			cur.children[c] = s
			if curStar == nil {
				s.starParent = s
			}

			cur = s
			curStar = cur
		default:
			s := &globNode{parent: cur, starParent: curStar}
			cur.children[c] = s
			cur = s
		}
	}
	cur.end = true

	return &gs, nil
}

// func (gs *globState) dump() string {
// 	var r string
// 	cur := gs
// 	for cur != nil {
// 		var v string
// 		if cur.c != 0 {
// 			v = string(cur.c)
// 		} else {
// 			for c, ok := range cur.ranges {

// 			}
// 		}
// 		fmt.Printf("%s%s", pad, v)
// 	}
// }

type trieIndex struct {
	root *trieNode
	// nodeDepth int
	// byteDepth int
	depth     int
	fileExt   string
	fileCount int

	// stringIDs map[string]int
}

type trieNode struct {
	c byte
	// node      string
	parent    *trieNode
	childrens []*trieNode
	isFile    bool
	isNodeEnd bool
}

func newTrie(fileExt string) *trieIndex {
	return &trieIndex{
		root:    &trieNode{},
		fileExt: fileExt,
		// stringIDs: map[string]int{},
	}
}

// abc.def.ghi
// abc.def2.ghi
func (ti *trieIndex) insert(path string) {
	path = filepath.Clean(path)
	if path[0] == '/' {
		path = path[1:]
	}
	if path == "" || path == "." {
		return
	}

	// nodes := strings.Split(path, string(os.PathSeparator))
	cur := ti.root
	// if len(nodes) > ti.nodeDepth {
	// 	ti.nodeDepth = len(nodes)
	// }
	// var bdepth int
	// for _, node := range nodes {
	// 	bdepth += len(node)
	// }
	// if bdepth > ti.byteDepth {
	// 	ti.byteDepth = bdepth
	// }
	if len(path) > ti.depth {
		ti.depth = len(path)
	}

	isFile := strings.HasSuffix(path, ti.fileExt)
	if isFile {
		path = path[:len(path)-len(ti.fileExt)]
	}

outer:
	for i := 0; i < len(path); i++ {
		c := path[i]
		for _, child := range cur.childrens {
			if child.c == byte(c) {
				// if child.val == node {
				cur = child
				continue outer
			}
		}

		tnode := &trieNode{c: c, parent: cur}
		cur.childrens = append(cur.childrens, tnode)
		cur = tnode
		cur.parent.isNodeEnd = c == '/'
	}

	if isFile {
		cur.isFile = true
		cur.isNodeEnd = true
		ti.fileCount++
	}

	// var isFile bool
	// if i == len(path)-1 {
	// 	isFile = strings.HasSuffix(node, ti.fileExt)
	// 	node = strings.TrimSuffix(node, ti.fileExt)
	// 	ti.fileCount++
	// }
}

// depth first search
func (ti *trieIndex) search(pattern string, limit int) (files []string, isFile []bool, err error) {
	var gstates []*globState
	for _, node := range strings.Split(pattern, "/") {
		if node == "" {
			return nil, nil, errors.New("empty node in query")
		}
		gs, err := newGlobState(node)
		if err != nil {
			return nil, nil, err
		}
		gstates = append(gstates, gs)
	}

	// matched = make([]string, 0, limit)
	// isFile = make([]bool, 0, limit)

	// var nodes = strings.Split(pattern, "/")
	var childIndex = make([]int, ti.depth+1)
	var gsIndex int
	var curLevel int
	var cur = ti.root
	var curGS = gstates[0]
	curGS.cur = curGS.root
	// var prevGS = gs

	var gst *globNode
	var matched bool
	var matchedByStar bool
	_ = matched
	_ = matchedByStar
	for {
		if len(cur.childrens) == 0 {
			goto parent
		}

		if childIndex[curLevel] == len(cur.childrens) {
			goto parent
		}

		cur = cur.childrens[childIndex[curLevel]]
		curLevel++

		// if curLevel > len(pattern) {
		// 	goto parent
		// }

		// TODO:
		//  * quit early for exact match
		//  * use string id for exact match

		// rework
		// if ok, _ := filepath.Match(nodes[curLevel-1], cur.val); ok {
		// 	if curLevel < len(nodes) {
		// 		continue
		// 	}

		// TODO: not right?
		log.Printf("cur.c = %+v\n", string(cur.c))
		if cur.c == '/' {
			// if !gstates[gsIndex].cur.end || gsIndex+1 >= len(gstates) {
			// 	goto parent
			// }

			// gsIndex++
			// curGS = gstates[gsIndex]
			// curGS.cur = curGS.root

			// if gsIndex+1 < len(gstates) {
			// 	// nextNode:
			// 	// 	for _, child := range cur.childrens {
			// 	// 		if child.c == '/' {
			// 	// 			gsIndex++
			// 	// 			curGS = gstates[gsIndex]
			// 	// 			curGS.cur = curGS.root

			// 	// 			cur = child
			// 	// 			curLevel++

			// 	// 			break nextNode
			// 	// 		}
			// 	// 	}
			// 	// 	continue
			// }

			if gsIndex+1 >= len(gstates) {
				goto parent
			}
			gsIndex++
			curGS = gstates[gsIndex]
			curGS.cur = curGS.root

			// log.Printf("gsIndex = %+v\n", gsIndex)

			// cur = cur.childrens[childIndex[curLevel]]
			// curLevel++
			continue
		}

		gst = curGS.cur.children[cur.c]
		matched = gst != nil
		matchedByStar = false

		if !matched {
			if !curGS.cur.star {
				gst = curGS.cur.children['*']
				matchedByStar = gst != nil
				matched = matchedByStar
			} else {
				gst = curGS.cur
				matched = true
			}
		}

		if !matched {
			// if curGS.cur.starParent == nil {
			// goto parent
			// }
			goto parent

			// gst = curGS.cur.starParent
			// continue
		}

		// log.Printf("c = %s matched %t\n", string(cur.c), curGS.children[cur.c] != nil)

		curGS.cur = gst
		if !curGS.cur.end {
			// if !matchedByStar {
			// 	curGS.cur = gst
			// 	continue
			// }

			// if gst.children['/'] != nil {
			// 	//
			// }
			continue
		}

		// continue matching if the current glob state a trailing star
		// log.Printf("cur.c = %+v\n", string(cur.c))
		// log.Printf("!cur.isNodeEnd  = %+v\n", !cur.isNodeEnd)
		// log.Printf("curGS.cur.parent != nil  = %+v\n", curGS.cur.parent != nil)
		// log.Printf("curGS.cur == curGS.cur.parent.children['*'] = %+v\n", curGS.cur == curGS.cur.parent.children['*'])

		if !cur.isNodeEnd && curGS.cur.star {
			// .parent != nil && curGS.cur == curGS.cur.parent.children['*']
			continue
		}

		// log.Printf("cur.fullPath(., ti.depth) = %+v\n", cur.fullPath('.', ti.depth))
		// log.Printf("gst.end = %+v\n", gst.end)
		// log.Printf("gsIndex = %+v\n", gsIndex)
		if gsIndex+1 < len(gstates) {
			// gsIndex++
			// curGS = gstates[gsIndex]
			// curGS.cur = curGS.root
			continue
		}

		files = append(files, cur.fullPath('.', ti.depth))
		isFile = append(isFile, cur.isFile)
		if len(files) >= limit {
			break
		}
		goto parent

		// continue

	parent:
		// use exact for fast exit
		childIndex[curLevel] = 0
		curLevel--
		if curLevel < 0 {
			break
		}
		childIndex[curLevel]++
		cur = cur.parent

		if cur.c == '/' && childIndex[curLevel] >= len(cur.childrens) {
			gsIndex--
			curGS = gstates[gsIndex]
			goto parent
		}
		continue
	}

	return files, isFile, nil
}

func (tn *trieNode) fullPath(sep byte, depth int) string {
	// var nodes = make([]string, 0, depth)
	// cur := tn
	// for cur.parent != nil {
	// 	nodes = append(nodes, cur.val)
	// 	cur = cur.parent
	// }
	// for i := 0; i < len(nodes)/2; i++ {
	// 	tmp := nodes[i]
	// 	nodes[i] = nodes[len(nodes)-i-1]
	// 	nodes[len(nodes)-i-1] = tmp
	// }

	// return strings.Join(nodes, sep)

	var r = make([]byte, depth)
	var index = depth - 1
	var cur = tn
	for cur.parent != nil {
		if cur.c == '/' {
			r[index] = sep
		} else {
			r[index] = cur.c
		}
		index--
		cur = cur.parent
	}
	r = r[index+1:]

	return *(*string)(unsafe.Pointer(&r))
}

func (ti *trieIndex) allFiles(sep byte) []string {
	var files = make([]string, 0, ti.fileCount)
	var childIndex = make([]int, ti.depth+1)
	var curLevel int
	var cur = ti.root
	for {
		if len(cur.childrens) == 0 {
			files = append(files, cur.fullPath(sep, ti.depth))
			goto parent
		}

		if childIndex[curLevel] == len(cur.childrens) {
			goto parent
		}

		cur = cur.childrens[childIndex[curLevel]]
		curLevel++
		continue

	parent:
		childIndex[curLevel] = 0
		curLevel--
		if curLevel < 0 {
			break
		}
		childIndex[curLevel]++
		cur = cur.parent
		continue
	}
	return files
}
<commit_msg>trie: fix trailing [] matching<commit_after>package carbonserver

import (
	"errors"
	"log"
	"path/filepath"
	"strings"
	"unsafe"
)

type globState struct {
	// c        byte
	// ranges   *[256]bool      // [a-z0-9A-Z]
	// depth int
	exact bool
	root  *globNode

	cur  *globNode
	expr string
}

type globNode struct {
	end        bool
	star       bool
	parent     *globNode
	starParent *globNode
	children   [256]*globNode // make it smaller

	posStart, posEnd int
}

func (gn *globNode) isNodeEnd() bool {
	return gn.end || gn.children['/'] != nil
}

func (gs *globState) state(gn *globNode) string {
	return gs.expr[gn.posStart:gn.posEnd]
}

// TODO: add range value validation
func newGlobState(expr string) (*globState, error) {
	var gs = globState{root: &globNode{}, exact: true, expr: expr}
	var cur = gs.root
	var curStar *globNode
	for i := 0; i < len(expr); i++ {
		c := expr[i]
		switch c {
		case '[':
			gs.exact = false
			// var ranges [256]bool
			s := &globNode{parent: cur, starParent: curStar, posStart: i}
			for i < len(expr) && expr[i] != ']' {
				if expr[i] == '-' {
					if i+1 >= len(expr) {
						return nil, errors.New("glob: missing closing range")
					}

					for j := expr[i-1]; j <= expr[i+1]; j++ {
						cur.children[j] = s
					}

					i += 2
					continue
				}

				cur.children[expr[i]] = s
				i++
			}
			if expr[i] != ']' {
				return nil, errors.New("glob: missing ]")
			}

			s.posEnd = i + 1
			cur = s
		case '*':
			gs.exact = false
			// de-dup multi stars: *** -> *
			for ; i+1 < len(expr) && expr[i+1] == '*'; i++ {
			}
			s := &globNode{parent: cur, starParent: curStar, star: true, posStart: i, posEnd: i + 1}
			cur.children[c] = s
			if curStar == nil {
				s.starParent = s
			}

			cur = s
			curStar = cur
		default:
			s := &globNode{parent: cur, starParent: curStar, posStart: i, posEnd: i + 1}
			cur.children[c] = s
			cur = s
		}
	}
	cur.end = true

	return &gs, nil
}

// func (gs *globState) dump() string {
// 	var r string
// 	cur := gs
// 	for cur != nil {
// 		var v string
// 		if cur.c != 0 {
// 			v = string(cur.c)
// 		} else {
// 			for c, ok := range cur.ranges {

// 			}
// 		}
// 		fmt.Printf("%s%s", pad, v)
// 	}
// }

type trieIndex struct {
	root *trieNode
	// nodeDepth int
	// byteDepth int
	depth     int
	fileExt   string
	fileCount int

	// stringIDs map[string]int
}

type trieNode struct {
	c byte
	// node      string
	parent    *trieNode
	childrens []*trieNode
	isFile    bool
	isNodeEnd bool
}

func newTrie(fileExt string) *trieIndex {
	return &trieIndex{
		root:    &trieNode{},
		fileExt: fileExt,
		// stringIDs: map[string]int{},
	}
}

// abc.def.ghi
// abc.def2.ghi
func (ti *trieIndex) insert(path string) {
	path = filepath.Clean(path)
	if path[0] == '/' {
		path = path[1:]
	}
	if path == "" || path == "." {
		return
	}

	// nodes := strings.Split(path, string(os.PathSeparator))
	cur := ti.root
	// if len(nodes) > ti.nodeDepth {
	// 	ti.nodeDepth = len(nodes)
	// }
	// var bdepth int
	// for _, node := range nodes {
	// 	bdepth += len(node)
	// }
	// if bdepth > ti.byteDepth {
	// 	ti.byteDepth = bdepth
	// }
	if len(path) > ti.depth {
		ti.depth = len(path)
	}

	isFile := strings.HasSuffix(path, ti.fileExt)
	if isFile {
		path = path[:len(path)-len(ti.fileExt)]
	}

outer:
	for i := 0; i < len(path); i++ {
		c := path[i]
		for _, child := range cur.childrens {
			if child.c == byte(c) {
				// if child.val == node {
				cur = child
				continue outer
			}
		}

		tnode := &trieNode{c: c, parent: cur}
		cur.childrens = append(cur.childrens, tnode)
		cur = tnode
		cur.parent.isNodeEnd = c == '/'
	}

	if isFile {
		cur.isFile = true
		cur.isNodeEnd = true
		ti.fileCount++
	}

	// var isFile bool
	// if i == len(path)-1 {
	// 	isFile = strings.HasSuffix(node, ti.fileExt)
	// 	node = strings.TrimSuffix(node, ti.fileExt)
	// 	ti.fileCount++
	// }
}

// depth first search
func (ti *trieIndex) search(pattern string, limit int) (files []string, isFile []bool, err error) {
	var gstates []*globState
	for _, node := range strings.Split(pattern, "/") {
		if node == "" {
			return nil, nil, errors.New("empty node in query")
		}
		gs, err := newGlobState(node)
		if err != nil {
			return nil, nil, err
		}
		gstates = append(gstates, gs)
	}

	// matched = make([]string, 0, limit)
	// isFile = make([]bool, 0, limit)

	// var nodes = strings.Split(pattern, "/")
	var childIndex = make([]int, ti.depth+1)
	var gsIndex int
	var curLevel int
	var cur = ti.root
	var curGS = gstates[0]
	curGS.cur = curGS.root
	// var prevGS = gs

	var gst *globNode
	var matched bool
	var matchedByStar bool
	_ = matched
	_ = matchedByStar
	for {
		if len(cur.childrens) == 0 {
			goto parent
		}

		if childIndex[curLevel] == len(cur.childrens) {
			goto parent
		}

		cur = cur.childrens[childIndex[curLevel]]
		curLevel++

		log.Printf("cur.fullPath(., ti.depth) = %+v\n", cur.fullPath('.', ti.depth))

		// if curLevel > len(pattern) {
		// 	goto parent
		// }

		// TODO:
		//  * quit early for exact match
		//  * use string id for exact match

		// rework
		// if ok, _ := filepath.Match(nodes[curLevel-1], cur.val); ok {
		// 	if curLevel < len(nodes) {
		// 		continue
		// 	}

		// TODO: not right?
		// log.Printf("cur.c = %+v\n", string(cur.c))
		if cur.c == '/' {
			// if !gstates[gsIndex].cur.end || gsIndex+1 >= len(gstates) {
			// 	goto parent
			// }

			// gsIndex++
			// curGS = gstates[gsIndex]
			// curGS.cur = curGS.root

			// if gsIndex+1 < len(gstates) {
			// 	// nextNode:
			// 	// 	for _, child := range cur.childrens {
			// 	// 		if child.c == '/' {
			// 	// 			gsIndex++
			// 	// 			curGS = gstates[gsIndex]
			// 	// 			curGS.cur = curGS.root

			// 	// 			cur = child
			// 	// 			curLevel++

			// 	// 			break nextNode
			// 	// 		}
			// 	// 	}
			// 	// 	continue
			// }

			if gsIndex+1 >= len(gstates) {
				goto parent
			}
			gsIndex++
			curGS = gstates[gsIndex]
			curGS.cur = curGS.root

			// log.Printf("gsIndex = %+v\n", gsIndex)

			// cur = cur.childrens[childIndex[curLevel]]
			// curLevel++
			continue
		}

		gst = curGS.cur.children[cur.c]
		matched = gst != nil
		matchedByStar = false

		log.Printf("cur.fullPath(., ti.depth) = %+v\n", cur.fullPath('.', ti.depth))
		log.Printf("curGS.state(curGS.cur) = %+v\n", curGS.state(curGS.cur))
		log.Printf("matched = %+v\n", matched)

		if !matched {
			if curGS.cur.star {
				gst = curGS.cur
				matched = true
			} else {
				gst = curGS.cur.children['*']
				matchedByStar = gst != nil
				matched = matchedByStar
			}
		}

		if !matched {
			// if curGS.cur.starParent == nil {
			// goto parent
			// }
			goto parent

			// gst = curGS.cur.starParent
			// continue
		}

		// log.Printf("c = %s matched %t\n", string(cur.c), curGS.children[cur.c] != nil)

		curGS.cur = gst
		if !curGS.cur.end {
			// if !matchedByStar {
			// 	curGS.cur = gst
			// 	continue
			// }

			// if gst.children['/'] != nil {
			// 	//
			// }
			continue
		}

		// continue matching if the current glob state a trailing star
		// log.Printf("cur.c = %+v\n", string(cur.c))
		// log.Printf("!cur.isNodeEnd  = %+v\n", !cur.isNodeEnd)
		// log.Printf("curGS.cur.parent != nil  = %+v\n", curGS.cur.parent != nil)
		// log.Printf("curGS.cur == curGS.cur.parent.children['*'] = %+v\n", curGS.cur == curGS.cur.parent.children['*'])

		if !cur.isNodeEnd && curGS.cur.star {
			// .parent != nil && curGS.cur == curGS.cur.parent.children['*']
			continue
		}

		// log.Printf("cur.fullPath(., ti.depth) = %+v\n", cur.fullPath('.', ti.depth))
		// log.Printf("gst.end = %+v\n", gst.end)
		// log.Printf("gsIndex = %+v\n", gsIndex)
		if gsIndex+1 < len(gstates) {
			// gsIndex++
			// curGS = gstates[gsIndex]
			// curGS.cur = curGS.root
			continue
		}

		files = append(files, cur.fullPath('.', ti.depth))
		isFile = append(isFile, cur.isFile)
		if len(files) >= limit {
			break
		}
		goto parent

		// continue

	parent:
		// use exact for fast exit
		childIndex[curLevel] = 0
		curLevel--
		if curLevel < 0 {
			break
		}
		childIndex[curLevel]++
		cur = cur.parent
		// if curGS.cur != nil {
		// 	curGS.cur = curGS.cur.parent
		// }

		log.Printf("cur.parent.fullPath(., ti.depth) = %+v\n", cur.fullPath('.', ti.depth))

		if cur.c == '/' && childIndex[curLevel] >= len(cur.childrens) {
			gsIndex--
			curGS = gstates[gsIndex]
			curGS.cur = curGS.cur.parent
			goto parent
		}
		continue
	}

	return files, isFile, nil
}

func (tn *trieNode) fullPath(sep byte, depth int) string {
	// var nodes = make([]string, 0, depth)
	// cur := tn
	// for cur.parent != nil {
	// 	nodes = append(nodes, cur.val)
	// 	cur = cur.parent
	// }
	// for i := 0; i < len(nodes)/2; i++ {
	// 	tmp := nodes[i]
	// 	nodes[i] = nodes[len(nodes)-i-1]
	// 	nodes[len(nodes)-i-1] = tmp
	// }

	// return strings.Join(nodes, sep)

	var r = make([]byte, depth)
	var index = depth - 1
	var cur = tn
	for cur.parent != nil {
		if cur.c == '/' {
			r[index] = sep
		} else {
			r[index] = cur.c
		}
		index--
		cur = cur.parent
	}
	r = r[index+1:]

	return *(*string)(unsafe.Pointer(&r))
}

func (ti *trieIndex) allFiles(sep byte) []string {
	var files = make([]string, 0, ti.fileCount)
	var childIndex = make([]int, ti.depth+1)
	var curLevel int
	var cur = ti.root
	for {
		if len(cur.childrens) == 0 {
			files = append(files, cur.fullPath(sep, ti.depth))
			goto parent
		}

		if childIndex[curLevel] == len(cur.childrens) {
			goto parent
		}

		cur = cur.childrens[childIndex[curLevel]]
		curLevel++
		continue

	parent:
		childIndex[curLevel] = 0
		curLevel--
		if curLevel < 0 {
			break
		}
		childIndex[curLevel]++
		cur = cur.parent
		continue
	}
	return files
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

type BasicAuthItem struct {
	UserName string `json:"username"`
	Password string `json:"password"`
	Url      string `json:"url"`
}

type DnspodItem struct {
	UserName  string `json:"username"`
	Password  string `json:"password"`
	Domain    string `json:"domain"`
	SubDomain string `json:"sub_domain"`
}

type Setting struct {
	BasicAuthItems []BasicAuthItem `json:"basic"`
	DnspodItems    []DnspodItem    `json:"dnspod"`
}

type DomainItem struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
}

type DomainList struct {
	Domains []DomainItem `json:"domains"`
}

type RecordItem struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

type RecordList struct {
	Records []RecordItem `json:"records"`
}

var (
	currentExternalIP string
	lastExternalIP    string
	domainList        = &DomainList{}
)

func getCurrentExternalIP() string {
	client := &http.Client{}
	ifconfigUrl := "https://ifconfig.minidump.info"
	req, err := http.NewRequest("GET", ifconfigUrl, nil)
	req.Header.Set("User-Agent", "curl/7.41.0")
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("request %s failed", ifconfigUrl)
		return ""
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("reading ifconfig response failed")
		return ""
	}
	return string(body)
}

func basicAuthorizeHttpRequest(user string, password string, requestUrl string) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", requestUrl, nil)
	req.SetBasicAuth(user, password)
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("request %s failed", requestUrl)
		return
	}

	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("reading response failed")
		return
	}
}

func dnspodRequest(user string, password string, domain string, sub_domain string) {
	needDomainList := false
	if len(domainList.Domains) == 0 {
		needDomainList = true
	}
	var domainId int = 0
	if needDomainList == false {
		needDomainList = true
		for _, v := range domainList.Domains {
			if v.Name == domain {
				needDomainList = false
				domainId = v.Id
				break
			}
		}
	}

	client := &http.Client{}
	if needDomainList {
		// get domainn id first
		domainListUrl := "https://dnsapi.cn/Domain.List"
		resp, err := client.PostForm(domainListUrl, url.Values{
			"login_email":    {user},
			"login_password": {password},
			"format":         {"json"},
		})
		if err != nil {
			fmt.Printf("request domain list failed")
			return
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("reading domain list failed")
			return
		}

		if err = json.Unmarshal(body, &domainList); err != nil {
			fmt.Printf("unmarshalling domain list %s failed", string(body))
			return
		}
	}
	foundDomain := false
	for _, v := range domainList.Domains {
		if v.Name == domain {
			foundDomain = true
			domainId = v.Id
			break
		}
	}

	if foundDomain == false {
		fmt.Printf("domain %s doesn't exists", domain)
		return
	}

	// check record list
	recordListUrl := "https://dnsapi.cn/Record.List"
	resp, err := client.PostForm(recordListUrl, url.Values{
		"login_email":    {user},
		"login_password": {password},
		"format":         {"json"},
		"domain_id":      {strconv.Itoa(domainId)},
	})
	if err != nil {
		fmt.Printf("request record list failed")
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("reading record list failed")
		return
	}

	recordList := new(RecordList)
	if err = json.Unmarshal(body, recordList); err != nil {
		fmt.Printf("unmarshalling record list %s failed", string(body))
		fmt.Println(err)
		return
	}
	foundRecord := false
	var recordId string
	for _, v := range recordList.Records {
		if v.Name == sub_domain {
			foundRecord = true
			recordId = v.Id
			break
		}
	}
	fmt.Println("4")
	if foundRecord == false {
		// if the sub domain doesn't exist, add one
		addRecordUrl := "https://dnsapi.cn/Record.Create"
		resp, err := client.PostForm(addRecordUrl, url.Values{
			"login_email":    {user},
			"login_password": {password},
			"format":         {"json"},
			"domain_id":      {strconv.Itoa(domainId)},
			"sub_domain":     {sub_domain},
			"record_type":    {"A"},
			"record_line":    {"默认"},
			"value":          {currentExternalIP},
		})
		if err != nil {
			fmt.Printf("request record insert failed")
			return
		}

		if _, err = ioutil.ReadAll(resp.Body); err != nil {
			fmt.Printf("reading record insert response failed")
			return
		}

		fmt.Printf("A record inserted: %s.%s => %s", sub_domain, domain, getCurrentExternalIP())
	} else {
		// otherwise just update it
		modifyRecordUrl := "https://dnsapi.cn/Record.Modify"
		resp, err := client.PostForm(modifyRecordUrl, url.Values{
			"login_email":    {user},
			"login_password": {password},
			"format":         {"json"},
			"record_id":      {recordId},
			"domain_id":      {strconv.Itoa(domainId)},
			"sub_domain":     {sub_domain},
			"record_type":    {"A"},
			"record_line":    {"默认"},
			"value":          {currentExternalIP},
		})
		if err != nil {
			fmt.Printf("request record modify failed")
			return
		}

		if _, err = ioutil.ReadAll(resp.Body); err != nil {
			fmt.Printf("reading record modify response failed")
			return
		}
		fmt.Printf("A record updated: %s.%s => %s", sub_domain, domain, getCurrentExternalIP())
	}
}

func main() {
	fmt.Println("Dynamic DNS client")

	appConf, err := os.Open("app.conf")
	if err != nil {
		fmt.Println("opening app.conf failed:", err)
		return
	}

	defer func() {
		appConf.Close()
	}()

	b, err := ioutil.ReadAll(appConf)
	if err != nil {
		fmt.Println("reading app.conf failed:", err)
		return
	}
	setting := &Setting{}
	err = json.Unmarshal(b, &setting)
	if err != nil {
		fmt.Println("unmarshalling app.conf failed:", err)
		return
	}

	timer := time.NewTicker(time.Duration(1) * time.Minute) // every 1 minute
	for {
		select {
		case <-timer.C:
			currentExternalIP = getCurrentExternalIP()
			if len(currentExternalIP) != 0 && lastExternalIP != currentExternalIP {
				for _, v := range setting.BasicAuthItems {
					basicAuthorizeHttpRequest(v.UserName, v.Password, v.Url)
				}

				for _, v := range setting.DnspodItems {
					dnspodRequest(v.UserName, v.Password, v.Domain, v.SubDomain)
				}
				lastExternalIP = currentExternalIP
			}
		}
	}
	timer.Stop()
}
<commit_msg>(*)refactoring: rename structures<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

type BasicAuthConfigurationItem struct {
	UserName string `json:"username"`
	Password string `json:"password"`
	Url      string `json:"url"`
}

type DnspodConfigurationItem struct {
	UserName  string `json:"username"`
	Password  string `json:"password"`
	Domain    string `json:"domain"`
	SubDomain string `json:"sub_domain"`
}

type Setting struct {
	BasicAuthItems []BasicAuthConfigurationItem `json:"basic"`
	DnspodItems    []DnspodConfigurationItem    `json:"dnspod"`
}

type DnspodDomainItem struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
}

type DnspodDomainList struct {
	Domains []DnspodDomainItem `json:"domains"`
}

type DnspodRecordItem struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

type DnspodRecordList struct {
	Records []DnspodRecordItem `json:"records"`
}

var (
	currentExternalIP string
	lastExternalIP    string
	dnspodDomainList  = &DnspodDomainList{}
)

func getCurrentExternalIP() string {
	client := &http.Client{}
	ifconfigUrl := "https://ifconfig.minidump.info"
	req, err := http.NewRequest("GET", ifconfigUrl, nil)
	req.Header.Set("User-Agent", "curl/7.41.0")
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("request %s failed", ifconfigUrl)
		return ""
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("reading ifconfig response failed")
		return ""
	}
	return string(body)
}

func basicAuthorizeHttpRequest(user string, password string, requestUrl string) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", requestUrl, nil)
	req.SetBasicAuth(user, password)
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("request %s failed", requestUrl)
		return
	}

	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("reading response failed")
		return
	}
}

func dnspodRequest(user string, password string, domain string, sub_domain string) {
	needDomainList := false
	if len(dnspodDomainList.Domains) == 0 {
		needDomainList = true
	}
	var domainId int = 0
	if needDomainList == false {
		needDomainList = true
		for _, v := range dnspodDomainList.Domains {
			if v.Name == domain {
				needDomainList = false
				domainId = v.Id
				break
			}
		}
	}

	client := &http.Client{}
	if needDomainList {
		// get domainn id first
		domainListUrl := "https://dnsapi.cn/Domain.List"
		resp, err := client.PostForm(domainListUrl, url.Values{
			"login_email":    {user},
			"login_password": {password},
			"format":         {"json"},
		})
		if err != nil {
			fmt.Printf("request domain list failed")
			return
		}

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("reading domain list failed")
			return
		}

		if err = json.Unmarshal(body, &dnspodDomainList); err != nil {
			fmt.Printf("unmarshalling domain list %s failed", string(body))
			return
		}
	}
	foundDomain := false
	for _, v := range dnspodDomainList.Domains {
		if v.Name == domain {
			foundDomain = true
			domainId = v.Id
			break
		}
	}

	if foundDomain == false {
		fmt.Printf("domain %s doesn't exists", domain)
		return
	}

	// check record list
	recordListUrl := "https://dnsapi.cn/Record.List"
	resp, err := client.PostForm(recordListUrl, url.Values{
		"login_email":    {user},
		"login_password": {password},
		"format":         {"json"},
		"domain_id":      {strconv.Itoa(domainId)},
	})
	if err != nil {
		fmt.Printf("request record list failed")
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("reading record list failed")
		return
	}

	recordList := new(DnspodRecordList)
	if err = json.Unmarshal(body, recordList); err != nil {
		fmt.Printf("unmarshalling record list %s failed", string(body))
		fmt.Println(err)
		return
	}
	foundRecord := false
	var recordId string
	for _, v := range recordList.Records {
		if v.Name == sub_domain {
			foundRecord = true
			recordId = v.Id
			break
		}
	}
	fmt.Println("4")
	if foundRecord == false {
		// if the sub domain doesn't exist, add one
		addRecordUrl := "https://dnsapi.cn/Record.Create"
		resp, err := client.PostForm(addRecordUrl, url.Values{
			"login_email":    {user},
			"login_password": {password},
			"format":         {"json"},
			"domain_id":      {strconv.Itoa(domainId)},
			"sub_domain":     {sub_domain},
			"record_type":    {"A"},
			"record_line":    {"默认"},
			"value":          {currentExternalIP},
		})
		if err != nil {
			fmt.Printf("request record insert failed")
			return
		}

		if _, err = ioutil.ReadAll(resp.Body); err != nil {
			fmt.Printf("reading record insert response failed")
			return
		}

		fmt.Printf("A record inserted: %s.%s => %s", sub_domain, domain, getCurrentExternalIP())
	} else {
		// otherwise just update it
		modifyRecordUrl := "https://dnsapi.cn/Record.Modify"
		resp, err := client.PostForm(modifyRecordUrl, url.Values{
			"login_email":    {user},
			"login_password": {password},
			"format":         {"json"},
			"record_id":      {recordId},
			"domain_id":      {strconv.Itoa(domainId)},
			"sub_domain":     {sub_domain},
			"record_type":    {"A"},
			"record_line":    {"默认"},
			"value":          {currentExternalIP},
		})
		if err != nil {
			fmt.Printf("request record modify failed")
			return
		}

		if _, err = ioutil.ReadAll(resp.Body); err != nil {
			fmt.Printf("reading record modify response failed")
			return
		}
		fmt.Printf("A record updated: %s.%s => %s", sub_domain, domain, getCurrentExternalIP())
	}
}

func main() {
	fmt.Println("Dynamic DNS client")

	appConf, err := os.Open("app.conf")
	if err != nil {
		fmt.Println("opening app.conf failed:", err)
		return
	}

	defer func() {
		appConf.Close()
	}()

	b, err := ioutil.ReadAll(appConf)
	if err != nil {
		fmt.Println("reading app.conf failed:", err)
		return
	}
	setting := &Setting{}
	err = json.Unmarshal(b, &setting)
	if err != nil {
		fmt.Println("unmarshalling app.conf failed:", err)
		return
	}

	timer := time.NewTicker(time.Duration(1) * time.Minute) // every 1 minute
	for {
		select {
		case <-timer.C:
			currentExternalIP = getCurrentExternalIP()
			if len(currentExternalIP) != 0 && lastExternalIP != currentExternalIP {
				for _, v := range setting.BasicAuthItems {
					basicAuthorizeHttpRequest(v.UserName, v.Password, v.Url)
				}

				for _, v := range setting.DnspodItems {
					dnspodRequest(v.UserName, v.Password, v.Domain, v.SubDomain)
				}
				lastExternalIP = currentExternalIP
			}
		}
	}
	timer.Stop()
}
<|endoftext|>


<commit_before>// +build windows

package entry

import (
	"time"

	log "github.com/golang/glog"
	"golang.org/x/sys/windows/svc"
)

type fleetspeakService struct {
	innerMain func()
}

func (m *fleetspeakService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, errno uint32) {
	const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
	changes <- svc.Status{State: svc.StartPending}
	changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
	timer := time.Tick(2 * time.Second)
	go m.innerMain()
loop:
	for {
		select {
		case <-timer:
		case c := <-r:
			switch c.Cmd {
			case svc.Interrogate:
				changes <- c.CurrentStatus
			case svc.Stop, svc.Shutdown:
				break loop
			case svc.Pause:
				changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
			case svc.Continue:
				changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
			default:
			}
		}
	}
	changes <- svc.Status{State: svc.StopPending}

	log.Exit("Stopping the service.")
	return
}

// RunMain starts the application.
func RunMain(innerMain func(), windowsServiceName string) {
	isIntSess, err := svc.IsAnInteractiveSession()
	if err != nil {
		log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
	}
	if isIntSess {
		innerMain()
	} else {
		svc.Run(windowsServiceName, &fleetspeakService{innerMain})
	}
}
<commit_msg>Don't call glog.Exit on Windows (#297)<commit_after>// +build windows

package entry

import (
	"os"
	"time"

	log "github.com/golang/glog"
	"golang.org/x/sys/windows/svc"
)

type fleetspeakService struct {
	innerMain func()
}

func (m *fleetspeakService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, errno uint32) {
	const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
	changes <- svc.Status{State: svc.StartPending}
	changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
	timer := time.Tick(2 * time.Second)
	go m.innerMain()
loop:
	for {
		select {
		case <-timer:
		case c := <-r:
			switch c.Cmd {
			case svc.Interrogate:
				changes <- c.CurrentStatus
			case svc.Stop, svc.Shutdown:
				break loop
			case svc.Pause:
				changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
			case svc.Continue:
				changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
			default:
			}
		}
	}
	changes <- svc.Status{State: svc.StopPending}

	log.Info("Stopping the service.")
	os.Exit(2)
	return
}

// RunMain starts the application.
func RunMain(innerMain func(), windowsServiceName string) {
	isIntSess, err := svc.IsAnInteractiveSession()
	if err != nil {
		log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
	}
	if isIntSess {
		innerMain()
	} else {
		svc.Run(windowsServiceName, &fleetspeakService{innerMain})
	}
}
<|endoftext|>


<commit_before>package backend

import (
	"atlantis/router/logger"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Server struct {
	Address   string
	Status    ServerStatus
	Metrics   ServerMetrics
	Transport *http.Transport
	copier    *Copier
}

func NewServer(address string) *Server {
	return &Server{
		Address: address,
		Status:  NewServerStatus(),
		Metrics: NewServerMetrics(),
		Transport: &http.Transport{
			MaxIdleConnsPerHost: 32,
		},
		copier: NewCopier(),
	}
}

type ResponseError struct {
	Response *http.Response
	Error    error
}

func (s *Server) RoundTrip(req *http.Request, ch chan ResponseError) {
	defer func() {
		if r := recover(); r != nil {
			err := fmt.Sprintf("%s", r)
			ch <- ResponseError{nil, errors.New(err)}
		}
	}()

	s.Metrics.RequestStart()
	defer s.Metrics.RequestDone()

	req.URL.Scheme = "http"
	req.URL.Host = s.Address

	resp, err := s.Transport.RoundTrip(req)
	if err == nil {
		ch <- ResponseError{resp, nil}
	} else {
		ch <- ResponseError{resp, err}
	}
}

func (s *Server) logPrefix(r *http.Request, tstart time.Time) string {
	now := time.Now()

	var rtt0, rtt1 int64

	// Calculate the total round trip time if front end inserted the atlantis-arrival-time
	// header before routing the request. The header is assumed to be created by calling
	// time.Now().UnixNano() or equivalent.
	arr, err := strconv.ParseInt(r.Header.Get("atlantis-arrival-time"), 10, 64)
	if err != nil {
		rtt0 = now.UnixNano() - arr
	}

	rtt1 = now.UnixNano() - tstart.UnixNano()

	// Log prefix includes server address, request source and URI, and round trip times.
	return fmt.Sprintf("[server %s][request %s|%s][rtt %d|%d]", s.Address, r.RemoteAddr,
		r.URL, rtt0, rtt1)
}

func (s *Server) Handle(w http.ResponseWriter, r *http.Request, tout time.Duration) {
	// X-Forwarded-For; we are a proxy.
	ip := strings.Split(r.RemoteAddr, ":")[0]
	r.Header.Add("X-Forwarded-For", ip)

	resErrCh := make(chan ResponseError)
	tstart := time.Now()
	go s.RoundTrip(r, resErrCh)

	select {
	case resErr := <-resErrCh:
		if resErr.Error == nil {
			logger.Printf("%s %d", s.logPrefix(r, tstart), resErr.Response.StatusCode)
			defer resErr.Response.Body.Close()
			for hdr, vals := range resErr.Response.Header {
				for _, val := range vals {
					w.Header().Add(hdr, val)
				}
			}
			w.WriteHeader(resErr.Response.StatusCode)
			_, err := s.copier.Copy(w, resErr.Response.Body)
			if err != nil {
				logger.Errorf("%s %s", s.logPrefix(r, tstart), err)
			}
		} else {
			logger.Errorf("%s %s", s.logPrefix(r, tstart), resErr.Error)
			http.Error(w, "Bad Gateway", http.StatusBadGateway)
		}
	case <-time.After(tout):
		logger.Printf("%s timeout", s.logPrefix(r, tstart))
		http.Error(w, "Gateway Timeout", http.StatusGatewayTimeout)
	}
}

func (s *Server) CheckStatus(tout time.Duration) {
	r, _ := http.NewRequest("GET", "http://"+s.Address+"/healthz", nil)

	resErrCh := make(chan ResponseError)
	tstart := time.Now()
	go s.RoundTrip(r, resErrCh)

	select {
	case resErr := <-resErrCh:
		if resErr.Error == nil {
			logger.Errorf("%s %d", s.logPrefix(r, tstart), resErr.Response.StatusCode)
			defer resErr.Response.Body.Close()
			s.Status.ParseAndSet(resErr.Response)
		} else {
			logger.Errorf("%s %s", s.logPrefix(r, tstart), resErr.Error)
			s.Status.Set(StatusCritical)
		}
	case <-time.After(tout):
		logger.Errorf("%s timeout", s.logPrefix(r, tstart))
		s.Status.Set(StatusCritical)
	}
}

func (s *Server) Cost(accept string) uint32 {
	return s.Status.Cost(accept) + s.Metrics.Cost()
}

func (s *Server) Shutdown() {
	s.copier.Shutdown()
}
<commit_msg>Use Printf vs Errorf on successful health check.<commit_after>package backend

import (
	"atlantis/router/logger"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Server struct {
	Address   string
	Status    ServerStatus
	Metrics   ServerMetrics
	Transport *http.Transport
	copier    *Copier
}

func NewServer(address string) *Server {
	return &Server{
		Address: address,
		Status:  NewServerStatus(),
		Metrics: NewServerMetrics(),
		Transport: &http.Transport{
			MaxIdleConnsPerHost: 32,
		},
		copier: NewCopier(),
	}
}

type ResponseError struct {
	Response *http.Response
	Error    error
}

func (s *Server) RoundTrip(req *http.Request, ch chan ResponseError) {
	defer func() {
		if r := recover(); r != nil {
			err := fmt.Sprintf("%s", r)
			ch <- ResponseError{nil, errors.New(err)}
		}
	}()

	s.Metrics.RequestStart()
	defer s.Metrics.RequestDone()

	req.URL.Scheme = "http"
	req.URL.Host = s.Address

	resp, err := s.Transport.RoundTrip(req)
	if err == nil {
		ch <- ResponseError{resp, nil}
	} else {
		ch <- ResponseError{resp, err}
	}
}

func (s *Server) logPrefix(r *http.Request, tstart time.Time) string {
	now := time.Now()

	var rtt0, rtt1 int64

	// Calculate the total round trip time if front end inserted the atlantis-arrival-time
	// header before routing the request. The header is assumed to be created by calling
	// time.Now().UnixNano() or equivalent.
	arr, err := strconv.ParseInt(r.Header.Get("atlantis-arrival-time"), 10, 64)
	if err != nil {
		rtt0 = now.UnixNano() - arr
	}

	rtt1 = now.UnixNano() - tstart.UnixNano()

	// Log prefix includes server address, request source and URI, and round trip times.
	return fmt.Sprintf("[server %s][request %s|%s][rtt %d|%d]", s.Address, r.RemoteAddr,
		r.URL, rtt0, rtt1)
}

func (s *Server) Handle(w http.ResponseWriter, r *http.Request, tout time.Duration) {
	// X-Forwarded-For; we are a proxy.
	ip := strings.Split(r.RemoteAddr, ":")[0]
	r.Header.Add("X-Forwarded-For", ip)

	resErrCh := make(chan ResponseError)
	tstart := time.Now()
	go s.RoundTrip(r, resErrCh)

	select {
	case resErr := <-resErrCh:
		if resErr.Error == nil {
			logger.Printf("%s %d", s.logPrefix(r, tstart), resErr.Response.StatusCode)
			defer resErr.Response.Body.Close()
			for hdr, vals := range resErr.Response.Header {
				for _, val := range vals {
					w.Header().Add(hdr, val)
				}
			}
			w.WriteHeader(resErr.Response.StatusCode)
			_, err := s.copier.Copy(w, resErr.Response.Body)
			if err != nil {
				logger.Errorf("%s %s", s.logPrefix(r, tstart), err)
			}
		} else {
			logger.Errorf("%s %s", s.logPrefix(r, tstart), resErr.Error)
			http.Error(w, "Bad Gateway", http.StatusBadGateway)
		}
	case <-time.After(tout):
		logger.Printf("%s timeout", s.logPrefix(r, tstart))
		http.Error(w, "Gateway Timeout", http.StatusGatewayTimeout)
	}
}

func (s *Server) CheckStatus(tout time.Duration) {
	r, _ := http.NewRequest("GET", "http://"+s.Address+"/healthz", nil)

	resErrCh := make(chan ResponseError)
	tstart := time.Now()
	go s.RoundTrip(r, resErrCh)

	select {
	case resErr := <-resErrCh:
		if resErr.Error == nil {
			logger.Printf("%s %d", s.logPrefix(r, tstart), resErr.Response.StatusCode)
			defer resErr.Response.Body.Close()
			s.Status.ParseAndSet(resErr.Response)
		} else {
			logger.Errorf("%s %s", s.logPrefix(r, tstart), resErr.Error)
			s.Status.Set(StatusCritical)
		}
	case <-time.After(tout):
		logger.Errorf("%s timeout", s.logPrefix(r, tstart))
		s.Status.Set(StatusCritical)
	}
}

func (s *Server) Cost(accept string) uint32 {
	return s.Status.Cost(accept) + s.Metrics.Cost()
}

func (s *Server) Shutdown() {
	s.copier.Shutdown()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// DefaultRules are the standard validation to perform on git commits
var DefaultRules = []ValidateRule{
	func(c CommitEntry) (vr ValidateResult) {
		vr.CommitEntry = c
		hasValid := false
		for _, line := range strings.Split(c.Body, "\n") {
			if validDCO.MatchString(line) {
				hasValid = true
			}
		}
		if !hasValid {
			vr.Pass = false
			vr.Msg = "does not have a valid DCO"
		} else {
			vr.Pass = true
			vr.Msg = "has a valid DCO"
		}

		return vr
	},
	// TODO add something for the cleanliness of the c.Subject
}

var (
	flVerbose     = flag.Bool("v", false, "verbose")
	flCommitRange = flag.String("range", "", "use this commit range instead")

	validDCO = regexp.MustCompile(`^Signed-off-by: ([^<]+) <([^<>@]+@[^<>]+)>$`)
)

func main() {
	flag.Parse()

	var commitrange string
	if *flCommitRange != "" {
		commitrange = *flCommitRange
	} else {
		var err error
		commitrange, err = GitFetchHeadCommit()
		if err != nil {
			log.Fatal(err)
		}
	}

	c, err := GitCommits(commitrange)
	if err != nil {
		log.Fatal(err)
	}

	results := ValidateResults{}
	for _, commit := range c {
		fmt.Printf(" * %s %s ... ", commit.AbbreviatedCommit, commit.Subject)
		vr := ValidateCommit(commit, DefaultRules)
		results = append(results, vr...)
		if _, fail := vr.PassFail(); fail == 0 {
			fmt.Println("PASS")
			if *flVerbose {
				for _, r := range vr {
					if !r.Pass {
						fmt.Printf("  - %s\n", r.Msg)
					}
				}
			}
		} else {
			fmt.Println("FAIL")
			// default, only print out failed validations
			for _, r := range vr {
				if !r.Pass {
					fmt.Printf("  - %s\n", r.Msg)
				}
			}
		}
	}
	_, fail := results.PassFail()
	if fail > 0 {
		fmt.Printf("%d issues to fix\n", fail)
		os.Exit(1)
	}
}

// ValidateRule will operate over a provided CommitEntry, and return a result.
type ValidateRule func(CommitEntry) ValidateResult

// ValidateCommit processes the given rules on the provided commit, and returns the result set.
func ValidateCommit(c CommitEntry, rules []ValidateRule) ValidateResults {
	results := ValidateResults{}
	for _, r := range rules {
		results = append(results, r(c))
	}
	return results
}

// ValidateResult is the result for a single validation of a commit.
type ValidateResult struct {
	CommitEntry CommitEntry
	Pass        bool
	Msg         string
}

// ValidateResults is a set of results. This is type makes it easy for the following function.
type ValidateResults []ValidateResult

// PassFail gives a quick over/under of passes and failures of the results in this set
func (vr ValidateResults) PassFail() (pass int, fail int) {
	for _, res := range vr {
		if res.Pass {
			pass++
		} else {
			fail++
		}
	}
	return pass, fail
}

// CommitEntry represents a single commit's information from `git`
type CommitEntry struct {
	Commit               string
	AbbreviatedCommit    string `json:"abbreviated_commit"`
	Tree                 string
	AbbreviatedTree      string `json:"abbreviated_tree"`
	Parent               string
	AbbreviatedParent    string `json:"abbreviated_parent"`
	Refs                 string
	Encoding             string
	Subject              string
	SanitizedSubjectLine string `json:"sanitized_subject_line"`
	Body                 string
	CommitNotes          string `json:"commit_notes"`
	VerificationFlag     string `json:"verification_flag"`
	ShortMsg             string
	Signer               string
	SignerKey            string       `json:"signer_key"`
	Author               PersonAction `json:"author,omitempty"`
	Committer            PersonAction `json:"committer,omitempty"`
}

// PersonAction is a time and identity of an action on a git commit
type PersonAction struct {
	Name  string `json:"name"`
	Email string `json:"email"`
	Date  string `json:"date"` // this could maybe be an actual time.Time
}

var (
	prettyLogSubject        = `--pretty=format:%s`
	prettyLogBody           = `--pretty=format:%b`
	prettyLogCommit         = `--pretty=format:%H`
	prettyLogAuthorName     = `--pretty=format:%aN`
	prettyLogAuthorEmail    = `--pretty=format:%aE`
	prettyLogCommitterName  = `--pretty=format:%cN`
	prettyLogCommitterEmail = `--pretty=format:%cE`
	prettyLogSigner         = `--pretty=format:%GS`
	prettyLogCommitNotes    = `--pretty=format:%N`
	prettyLogFormat         = `--pretty=format:{"commit": "%H", "abbreviated_commit": "%h", "tree": "%T", "abbreviated_tree": "%t", "parent": "%P", "abbreviated_parent": "%p", "refs": "%D", "encoding": "%e", "sanitized_subject_line": "%f", "verification_flag": "%G?", "signer_key": "%GK", "author": { "date": "%aD" }, "committer": { "date": "%cD" }}`
)

// GitLogCommit assembles the full information on a commit from its commit hash
func GitLogCommit(commit string) (*CommitEntry, error) {
	buf := bytes.NewBuffer([]byte{})
	cmd := exec.Command("git", "log", "-1", prettyLogFormat, commit)
	cmd.Stdout = buf
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Println(strings.Join(cmd.Args, " "))
		return nil, err
	}
	c := CommitEntry{}
	output := buf.Bytes()
	if err := json.Unmarshal(output, &c); err != nil {
		fmt.Println(string(output))
		return nil, err
	}

	output, err := exec.Command("git", "log", "-1", prettyLogSubject, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Subject = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogBody, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Body = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogAuthorName, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Author.Name = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogAuthorEmail, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Author.Email = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogCommitterName, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Committer.Name = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogCommitterEmail, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Committer.Email = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogCommitNotes, commit).Output()
	if err != nil {
		return nil, err
	}
	c.CommitNotes = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogSigner, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Signer = strings.TrimSpace(string(output))

	return &c, nil
}

// GitCommits returns a set of commits.
// If commitrange is a git still range 12345...54321, then it will be isolated set of commits.
// If commitrange is a single commit, all ancestor commits up through the hash provided.
func GitCommits(commitrange string) ([]CommitEntry, error) {
	output, err := exec.Command("git", "log", prettyLogCommit, commitrange).Output()
	if err != nil {
		return nil, err
	}
	commitHashes := strings.Split(strings.TrimSpace(string(output)), "\n")
	commits := make([]CommitEntry, len(commitHashes))
	for i, commitHash := range commitHashes {
		c, err := GitLogCommit(commitHash)
		if err != nil {
			return commits, err
		}
		commits[i] = *c
	}
	return commits, nil
}

// GitFetchHeadCommit returns the hash of FETCH_HEAD
func GitFetchHeadCommit() (string, error) {
	output, err := exec.Command("git", "rev-parse", "--verify", "HEAD").Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}

// GitHeadCommit returns the hash of HEAD
func GitHeadCommit() (string, error) {
	output, err := exec.Command("git", "rev-parse", "--verify", "HEAD").Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}
<commit_msg>travis: fix DCO validation for merges<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

// DefaultRules are the standard validation to perform on git commits
var DefaultRules = []ValidateRule{
	func(c CommitEntry) (vr ValidateResult) {
		vr.CommitEntry = c
		if len(strings.Split(c.Parent, " ")) > 1 {
			vr.Pass = true
			vr.Msg = "merge commits do not require DCO"
			return vr
		}

		hasValid := false
		for _, line := range strings.Split(c.Body, "\n") {
			if validDCO.MatchString(line) {
				hasValid = true
			}
		}
		if !hasValid {
			vr.Pass = false
			vr.Msg = "does not have a valid DCO"
		} else {
			vr.Pass = true
			vr.Msg = "has a valid DCO"
		}

		return vr
	},
	// TODO add something for the cleanliness of the c.Subject
}

var (
	flVerbose     = flag.Bool("v", false, "verbose")
	flCommitRange = flag.String("range", "", "use this commit range instead")

	validDCO = regexp.MustCompile(`^Signed-off-by: ([^<]+) <([^<>@]+@[^<>]+)>$`)
)

func main() {
	flag.Parse()

	var commitrange string
	if *flCommitRange != "" {
		commitrange = *flCommitRange
	} else {
		var err error
		commitrange, err = GitFetchHeadCommit()
		if err != nil {
			log.Fatal(err)
		}
	}

	c, err := GitCommits(commitrange)
	if err != nil {
		log.Fatal(err)
	}

	results := ValidateResults{}
	for _, commit := range c {
		fmt.Printf(" * %s %s ... ", commit.AbbreviatedCommit, commit.Subject)
		vr := ValidateCommit(commit, DefaultRules)
		results = append(results, vr...)
		if _, fail := vr.PassFail(); fail == 0 {
			fmt.Println("PASS")
			if *flVerbose {
				for _, r := range vr {
					if r.Pass {
						fmt.Printf("  - %s\n", r.Msg)
					}
				}
			}
		} else {
			fmt.Println("FAIL")
			// default, only print out failed validations
			for _, r := range vr {
				if !r.Pass {
					fmt.Printf("  - %s\n", r.Msg)
				}
			}
		}
	}
	_, fail := results.PassFail()
	if fail > 0 {
		fmt.Printf("%d issues to fix\n", fail)
		os.Exit(1)
	}
}

// ValidateRule will operate over a provided CommitEntry, and return a result.
type ValidateRule func(CommitEntry) ValidateResult

// ValidateCommit processes the given rules on the provided commit, and returns the result set.
func ValidateCommit(c CommitEntry, rules []ValidateRule) ValidateResults {
	results := ValidateResults{}
	for _, r := range rules {
		results = append(results, r(c))
	}
	return results
}

// ValidateResult is the result for a single validation of a commit.
type ValidateResult struct {
	CommitEntry CommitEntry
	Pass        bool
	Msg         string
}

// ValidateResults is a set of results. This is type makes it easy for the following function.
type ValidateResults []ValidateResult

// PassFail gives a quick over/under of passes and failures of the results in this set
func (vr ValidateResults) PassFail() (pass int, fail int) {
	for _, res := range vr {
		if res.Pass {
			pass++
		} else {
			fail++
		}
	}
	return pass, fail
}

// CommitEntry represents a single commit's information from `git`
type CommitEntry struct {
	Commit               string
	AbbreviatedCommit    string `json:"abbreviated_commit"`
	Tree                 string
	AbbreviatedTree      string `json:"abbreviated_tree"`
	Parent               string
	AbbreviatedParent    string `json:"abbreviated_parent"`
	Refs                 string
	Encoding             string
	Subject              string
	SanitizedSubjectLine string `json:"sanitized_subject_line"`
	Body                 string
	CommitNotes          string `json:"commit_notes"`
	VerificationFlag     string `json:"verification_flag"`
	ShortMsg             string
	Signer               string
	SignerKey            string       `json:"signer_key"`
	Author               PersonAction `json:"author,omitempty"`
	Committer            PersonAction `json:"committer,omitempty"`
}

// PersonAction is a time and identity of an action on a git commit
type PersonAction struct {
	Name  string `json:"name"`
	Email string `json:"email"`
	Date  string `json:"date"` // this could maybe be an actual time.Time
}

var (
	prettyLogSubject        = `--pretty=format:%s`
	prettyLogBody           = `--pretty=format:%b`
	prettyLogCommit         = `--pretty=format:%H`
	prettyLogAuthorName     = `--pretty=format:%aN`
	prettyLogAuthorEmail    = `--pretty=format:%aE`
	prettyLogCommitterName  = `--pretty=format:%cN`
	prettyLogCommitterEmail = `--pretty=format:%cE`
	prettyLogSigner         = `--pretty=format:%GS`
	prettyLogCommitNotes    = `--pretty=format:%N`
	prettyLogFormat         = `--pretty=format:{"commit": "%H", "abbreviated_commit": "%h", "tree": "%T", "abbreviated_tree": "%t", "parent": "%P", "abbreviated_parent": "%p", "refs": "%D", "encoding": "%e", "sanitized_subject_line": "%f", "verification_flag": "%G?", "signer_key": "%GK", "author": { "date": "%aD" }, "committer": { "date": "%cD" }}`
)

// GitLogCommit assembles the full information on a commit from its commit hash
func GitLogCommit(commit string) (*CommitEntry, error) {
	buf := bytes.NewBuffer([]byte{})
	cmd := exec.Command("git", "log", "-1", prettyLogFormat, commit)
	cmd.Stdout = buf
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Println(strings.Join(cmd.Args, " "))
		return nil, err
	}
	c := CommitEntry{}
	output := buf.Bytes()
	if err := json.Unmarshal(output, &c); err != nil {
		fmt.Println(string(output))
		return nil, err
	}

	output, err := exec.Command("git", "log", "-1", prettyLogSubject, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Subject = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogBody, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Body = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogAuthorName, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Author.Name = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogAuthorEmail, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Author.Email = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogCommitterName, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Committer.Name = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogCommitterEmail, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Committer.Email = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogCommitNotes, commit).Output()
	if err != nil {
		return nil, err
	}
	c.CommitNotes = strings.TrimSpace(string(output))

	output, err = exec.Command("git", "log", "-1", prettyLogSigner, commit).Output()
	if err != nil {
		return nil, err
	}
	c.Signer = strings.TrimSpace(string(output))

	return &c, nil
}

// GitCommits returns a set of commits.
// If commitrange is a git still range 12345...54321, then it will be isolated set of commits.
// If commitrange is a single commit, all ancestor commits up through the hash provided.
func GitCommits(commitrange string) ([]CommitEntry, error) {
	output, err := exec.Command("git", "log", prettyLogCommit, commitrange).Output()
	if err != nil {
		return nil, err
	}
	commitHashes := strings.Split(strings.TrimSpace(string(output)), "\n")
	commits := make([]CommitEntry, len(commitHashes))
	for i, commitHash := range commitHashes {
		c, err := GitLogCommit(commitHash)
		if err != nil {
			return commits, err
		}
		commits[i] = *c
	}
	return commits, nil
}

// GitFetchHeadCommit returns the hash of FETCH_HEAD
func GitFetchHeadCommit() (string, error) {
	output, err := exec.Command("git", "rev-parse", "--verify", "HEAD").Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}

// GitHeadCommit returns the hash of HEAD
func GitHeadCommit() (string, error) {
	output, err := exec.Command("git", "rev-parse", "--verify", "HEAD").Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(output)), nil
}
<|endoftext|>


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

// +build arm64

package cpu

const CacheLineSize = 64

// arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
// These are linknamed in runtime/os_linux_arm64.go and are initialized by
// archauxv().
var arm64_hwcap uint
var arm64_hwcap2 uint

// HWCAP/HWCAP2 bits. These are exposed by Linux.
const (
	hwcap_FP       = (1 << 0)
	hwcap_ASIMD    = (1 << 1)
	hwcap_EVTSTRM  = (1 << 2)
	hwcap_AES      = (1 << 3)
	hwcap_PMULL    = (1 << 4)
	hwcap_SHA1     = (1 << 5)
	hwcap_SHA2     = (1 << 6)
	hwcap_CRC32    = (1 << 7)
	hwcap_ATOMICS  = (1 << 8)
	hwcap_FPHP     = (1 << 9)
	hwcap_ASIMDHP  = (1 << 10)
	hwcap_CPUID    = (1 << 11)
	hwcap_ASIMDRDM = (1 << 12)
	hwcap_JSCVT    = (1 << 13)
	hwcap_FCMA     = (1 << 14)
	hwcap_LRCPC    = (1 << 15)
	hwcap_DCPOP    = (1 << 16)
	hwcap_SHA3     = (1 << 17)
	hwcap_SM3      = (1 << 18)
	hwcap_SM4      = (1 << 19)
	hwcap_ASIMDDP  = (1 << 20)
	hwcap_SHA512   = (1 << 21)
	hwcap_SVE      = (1 << 22)
	hwcap_ASIMDFHM = (1 << 23)
)

func doinit() {
	// HWCAP feature bits
	ARM64.HasFP = isSet(arm64_hwcap, hwcap_FP)
	ARM64.HasASIMD = isSet(arm64_hwcap, hwcap_ASIMD)
	ARM64.HasEVTSTRM = isSet(arm64_hwcap, hwcap_EVTSTRM)
	ARM64.HasAES = isSet(arm64_hwcap, hwcap_AES)
	ARM64.HasPMULL = isSet(arm64_hwcap, hwcap_PMULL)
	ARM64.HasSHA1 = isSet(arm64_hwcap, hwcap_SHA1)
	ARM64.HasSHA2 = isSet(arm64_hwcap, hwcap_SHA2)
	ARM64.HasCRC32 = isSet(arm64_hwcap, hwcap_CRC32)
	ARM64.HasATOMICS = isSet(arm64_hwcap, hwcap_ATOMICS)
	ARM64.HasFPHP = isSet(arm64_hwcap, hwcap_FPHP)
	ARM64.HasASIMDHP = isSet(arm64_hwcap, hwcap_ASIMDHP)
	ARM64.HasCPUID = isSet(arm64_hwcap, hwcap_CPUID)
	ARM64.HasASIMDRDM = isSet(arm64_hwcap, hwcap_ASIMDRDM)
	ARM64.HasJSCVT = isSet(arm64_hwcap, hwcap_JSCVT)
	ARM64.HasFCMA = isSet(arm64_hwcap, hwcap_FCMA)
	ARM64.HasLRCPC = isSet(arm64_hwcap, hwcap_LRCPC)
	ARM64.HasDCPOP = isSet(arm64_hwcap, hwcap_DCPOP)
	ARM64.HasSHA3 = isSet(arm64_hwcap, hwcap_SHA3)
	ARM64.HasSM3 = isSet(arm64_hwcap, hwcap_SM3)
	ARM64.HasSM4 = isSet(arm64_hwcap, hwcap_SM4)
	ARM64.HasASIMDDP = isSet(arm64_hwcap, hwcap_ASIMDDP)
	ARM64.HasSHA512 = isSet(arm64_hwcap, hwcap_SHA512)
	ARM64.HasSVE = isSet(arm64_hwcap, hwcap_SVE)
	ARM64.HasASIMDFHM = isSet(arm64_hwcap, hwcap_ASIMDFHM)
}

func isSet(hwc uint, value uint) bool {
	return hwc&value != 0
}
<commit_msg>internal/cpu: remove redundant build tag<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cpu

const CacheLineSize = 64

// arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
// These are linknamed in runtime/os_linux_arm64.go and are initialized by
// archauxv().
var arm64_hwcap uint
var arm64_hwcap2 uint

// HWCAP/HWCAP2 bits. These are exposed by Linux.
const (
	hwcap_FP       = (1 << 0)
	hwcap_ASIMD    = (1 << 1)
	hwcap_EVTSTRM  = (1 << 2)
	hwcap_AES      = (1 << 3)
	hwcap_PMULL    = (1 << 4)
	hwcap_SHA1     = (1 << 5)
	hwcap_SHA2     = (1 << 6)
	hwcap_CRC32    = (1 << 7)
	hwcap_ATOMICS  = (1 << 8)
	hwcap_FPHP     = (1 << 9)
	hwcap_ASIMDHP  = (1 << 10)
	hwcap_CPUID    = (1 << 11)
	hwcap_ASIMDRDM = (1 << 12)
	hwcap_JSCVT    = (1 << 13)
	hwcap_FCMA     = (1 << 14)
	hwcap_LRCPC    = (1 << 15)
	hwcap_DCPOP    = (1 << 16)
	hwcap_SHA3     = (1 << 17)
	hwcap_SM3      = (1 << 18)
	hwcap_SM4      = (1 << 19)
	hwcap_ASIMDDP  = (1 << 20)
	hwcap_SHA512   = (1 << 21)
	hwcap_SVE      = (1 << 22)
	hwcap_ASIMDFHM = (1 << 23)
)

func doinit() {
	// HWCAP feature bits
	ARM64.HasFP = isSet(arm64_hwcap, hwcap_FP)
	ARM64.HasASIMD = isSet(arm64_hwcap, hwcap_ASIMD)
	ARM64.HasEVTSTRM = isSet(arm64_hwcap, hwcap_EVTSTRM)
	ARM64.HasAES = isSet(arm64_hwcap, hwcap_AES)
	ARM64.HasPMULL = isSet(arm64_hwcap, hwcap_PMULL)
	ARM64.HasSHA1 = isSet(arm64_hwcap, hwcap_SHA1)
	ARM64.HasSHA2 = isSet(arm64_hwcap, hwcap_SHA2)
	ARM64.HasCRC32 = isSet(arm64_hwcap, hwcap_CRC32)
	ARM64.HasATOMICS = isSet(arm64_hwcap, hwcap_ATOMICS)
	ARM64.HasFPHP = isSet(arm64_hwcap, hwcap_FPHP)
	ARM64.HasASIMDHP = isSet(arm64_hwcap, hwcap_ASIMDHP)
	ARM64.HasCPUID = isSet(arm64_hwcap, hwcap_CPUID)
	ARM64.HasASIMDRDM = isSet(arm64_hwcap, hwcap_ASIMDRDM)
	ARM64.HasJSCVT = isSet(arm64_hwcap, hwcap_JSCVT)
	ARM64.HasFCMA = isSet(arm64_hwcap, hwcap_FCMA)
	ARM64.HasLRCPC = isSet(arm64_hwcap, hwcap_LRCPC)
	ARM64.HasDCPOP = isSet(arm64_hwcap, hwcap_DCPOP)
	ARM64.HasSHA3 = isSet(arm64_hwcap, hwcap_SHA3)
	ARM64.HasSM3 = isSet(arm64_hwcap, hwcap_SM3)
	ARM64.HasSM4 = isSet(arm64_hwcap, hwcap_SM4)
	ARM64.HasASIMDDP = isSet(arm64_hwcap, hwcap_ASIMDDP)
	ARM64.HasSHA512 = isSet(arm64_hwcap, hwcap_SHA512)
	ARM64.HasSVE = isSet(arm64_hwcap, hwcap_SVE)
	ARM64.HasASIMDFHM = isSet(arm64_hwcap, hwcap_ASIMDFHM)
}

func isSet(hwc uint, value uint) bool {
	return hwc&value != 0
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
<<<<<<< HEAD
	"regexp"
=======
	"net/url"
>>>>>>> 9c051a5... Tests for importing various iam resources

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsIamPolicy() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsIamPolicyCreate,
		Read:   resourceAwsIamPolicyRead,
		Update: resourceAwsIamPolicyUpdate,
		Delete: resourceAwsIamPolicyDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"description": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"path": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "/",
				ForceNew: true,
			},
			"policy": &schema.Schema{
<<<<<<< HEAD
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateJsonString,
=======
				Type:             schema.TypeString,
				Required:         true,
				DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
>>>>>>> daaae09... Add DiffSuppressFunc to support heredocs for aws_iam_role.assume_role_policy and aws_iam_policy.policy
			},
			"name": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"name_prefix"},
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
					value := v.(string)
					if len(value) > 128 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 128 characters", k))
					}
					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
						errors = append(errors, fmt.Errorf(
							"%q must match [\\w+=,.@-]", k))
					}
					return
				},
			},
			"name_prefix": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
					value := v.(string)
					if len(value) > 96 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 96 characters, name is limited to 128", k))
					}
					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
						errors = append(errors, fmt.Errorf(
							"%q must match [\\w+=,.@-]", k))
					}
					return
				},
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	var name string
	if v, ok := d.GetOk("name"); ok {
		name = v.(string)
	} else if v, ok := d.GetOk("name_prefix"); ok {
		name = resource.PrefixedUniqueId(v.(string))
	} else {
		name = resource.UniqueId()
	}

	request := &iam.CreatePolicyInput{
		Description:    aws.String(d.Get("description").(string)),
		Path:           aws.String(d.Get("path").(string)),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		PolicyName:     aws.String(name),
	}

	response, err := iamconn.CreatePolicy(request)
	if err != nil {
		return fmt.Errorf("Error creating IAM policy %s: %s", name, err)
	}

	return readIamPolicy(d, response.Policy)
}

func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	getPolicyRequest := &iam.GetPolicyInput{
		PolicyArn: aws.String(d.Id()),
	}

	getPolicyResponse, err := iamconn.GetPolicy(getPolicyRequest)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %s", d.Id(), err)
	}

	getPolicyVersionRequest := &iam.GetPolicyVersionInput{
		PolicyArn: aws.String(d.Id()),
		VersionId: getPolicyResponse.Policy.DefaultVersionId,
	}

	getPolicyVersionResponse, err := iamconn.GetPolicyVersion(getPolicyVersionRequest)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading IAM policy version %s: %s", d.Id(), err)
	}

	policy, err := url.QueryUnescape(*getPolicyVersionResponse.PolicyVersion.Document)
	if err != nil {
		return err
	}
	if err := d.Set("policy", policy); err != nil {
		return err
	}

	return readIamPolicy(d, getPolicyResponse.Policy)
}

func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
		return err
	}

	if !d.HasChange("policy") {
		return nil
	}
	request := &iam.CreatePolicyVersionInput{
		PolicyArn:      aws.String(d.Id()),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		SetAsDefault:   aws.Bool(true),
	}

	if _, err := iamconn.CreatePolicyVersion(request); err != nil {
		return fmt.Errorf("Error updating IAM policy %s: %s", d.Id(), err)
	}
	return nil
}

func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
		return err
	}

	request := &iam.DeletePolicyInput{
		PolicyArn: aws.String(d.Id()),
	}

	_, err := iamconn.DeletePolicy(request)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
	}
	return nil
}

// iamPolicyPruneVersions deletes the oldest versions.
//
// Old versions are deleted until there are 4 or less remaining, which means at
// least one more can be created before hitting the maximum of 5.
//
// The default version is never deleted.

func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}
	if len(versions) < 5 {
		return nil
	}

	var oldestVersion *iam.PolicyVersion

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if oldestVersion == nil ||
			version.CreateDate.Before(*oldestVersion.CreateDate) {
			oldestVersion = version
		}
	}

	if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionId, iamconn); err != nil {
		return err
	}
	return nil
}

func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if err := iamPolicyDeleteVersion(arn, *version.VersionId, iamconn); err != nil {
			return err
		}
	}

	return nil
}

func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
	request := &iam.DeletePolicyVersionInput{
		PolicyArn: aws.String(arn),
		VersionId: aws.String(versionID),
	}

	_, err := iamconn.DeletePolicyVersion(request)
	if err != nil {
		return fmt.Errorf("Error deleting version %s from IAM policy %s: %s", versionID, arn, err)
	}
	return nil
}

func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
	request := &iam.ListPolicyVersionsInput{
		PolicyArn: aws.String(arn),
	}

	response, err := iamconn.ListPolicyVersions(request)
	if err != nil {
		return nil, fmt.Errorf("Error listing versions for IAM policy %s: %s", arn, err)
	}
	return response.Versions, nil
}

func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error {
	d.SetId(*policy.Arn)
	if policy.Description != nil {
		// the description isn't present in the response to CreatePolicy.
		if err := d.Set("description", policy.Description); err != nil {
			return err
		}
	}
	if err := d.Set("path", policy.Path); err != nil {
		return err
	}
	if err := d.Set("name", policy.PolicyName); err != nil {
		return err
	}
	if err := d.Set("arn", policy.Arn); err != nil {
		return err
	}
	return nil
}
<commit_msg>provider/aws: Fixing the broken build of the aws_iam_policy resource<commit_after>package aws

import (
	"fmt"
	"regexp"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"

	"github.com/hashicorp/go-getter/helper/url"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsIamPolicy() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsIamPolicyCreate,
		Read:   resourceAwsIamPolicyRead,
		Update: resourceAwsIamPolicyUpdate,
		Delete: resourceAwsIamPolicyDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"description": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"path": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "/",
				ForceNew: true,
			},
			"policy": &schema.Schema{
				Type:             schema.TypeString,
				Required:         true,
				ValidateFunc:     validateJsonString,
				DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
			},
			"name": &schema.Schema{
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"name_prefix"},
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
					value := v.(string)
					if len(value) > 128 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 128 characters", k))
					}
					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
						errors = append(errors, fmt.Errorf(
							"%q must match [\\w+=,.@-]", k))
					}
					return
				},
			},
			"name_prefix": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					// https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334
					value := v.(string)
					if len(value) > 96 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 96 characters, name is limited to 128", k))
					}
					if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) {
						errors = append(errors, fmt.Errorf(
							"%q must match [\\w+=,.@-]", k))
					}
					return
				},
			},
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	var name string
	if v, ok := d.GetOk("name"); ok {
		name = v.(string)
	} else if v, ok := d.GetOk("name_prefix"); ok {
		name = resource.PrefixedUniqueId(v.(string))
	} else {
		name = resource.UniqueId()
	}

	request := &iam.CreatePolicyInput{
		Description:    aws.String(d.Get("description").(string)),
		Path:           aws.String(d.Get("path").(string)),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		PolicyName:     aws.String(name),
	}

	response, err := iamconn.CreatePolicy(request)
	if err != nil {
		return fmt.Errorf("Error creating IAM policy %s: %s", name, err)
	}

	return readIamPolicy(d, response.Policy)
}

func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	getPolicyRequest := &iam.GetPolicyInput{
		PolicyArn: aws.String(d.Id()),
	}

	getPolicyResponse, err := iamconn.GetPolicy(getPolicyRequest)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %s", d.Id(), err)
	}

	getPolicyVersionRequest := &iam.GetPolicyVersionInput{
		PolicyArn: aws.String(d.Id()),
		VersionId: getPolicyResponse.Policy.DefaultVersionId,
	}

	getPolicyVersionResponse, err := iamconn.GetPolicyVersion(getPolicyVersionRequest)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error reading IAM policy version %s: %s", d.Id(), err)
	}

	policy, err := url.QueryUnescape(*getPolicyVersionResponse.PolicyVersion.Document)
	if err != nil {
		return err
	}
	if err := d.Set("policy", policy); err != nil {
		return err
	}

	return readIamPolicy(d, getPolicyResponse.Policy)
}

func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
		return err
	}

	if !d.HasChange("policy") {
		return nil
	}
	request := &iam.CreatePolicyVersionInput{
		PolicyArn:      aws.String(d.Id()),
		PolicyDocument: aws.String(d.Get("policy").(string)),
		SetAsDefault:   aws.Bool(true),
	}

	if _, err := iamconn.CreatePolicyVersion(request); err != nil {
		return fmt.Errorf("Error updating IAM policy %s: %s", d.Id(), err)
	}
	return nil
}

func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	iamconn := meta.(*AWSClient).iamconn

	if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
		return err
	}

	request := &iam.DeletePolicyInput{
		PolicyArn: aws.String(d.Id()),
	}

	_, err := iamconn.DeletePolicy(request)
	if err != nil {
		if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" {
			return nil
		}
		return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err)
	}
	return nil
}

// iamPolicyPruneVersions deletes the oldest versions.
//
// Old versions are deleted until there are 4 or less remaining, which means at
// least one more can be created before hitting the maximum of 5.
//
// The default version is never deleted.

func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}
	if len(versions) < 5 {
		return nil
	}

	var oldestVersion *iam.PolicyVersion

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if oldestVersion == nil ||
			version.CreateDate.Before(*oldestVersion.CreateDate) {
			oldestVersion = version
		}
	}

	if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionId, iamconn); err != nil {
		return err
	}
	return nil
}

func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
	versions, err := iamPolicyListVersions(arn, iamconn)
	if err != nil {
		return err
	}

	for _, version := range versions {
		if *version.IsDefaultVersion {
			continue
		}
		if err := iamPolicyDeleteVersion(arn, *version.VersionId, iamconn); err != nil {
			return err
		}
	}

	return nil
}

func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
	request := &iam.DeletePolicyVersionInput{
		PolicyArn: aws.String(arn),
		VersionId: aws.String(versionID),
	}

	_, err := iamconn.DeletePolicyVersion(request)
	if err != nil {
		return fmt.Errorf("Error deleting version %s from IAM policy %s: %s", versionID, arn, err)
	}
	return nil
}

func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
	request := &iam.ListPolicyVersionsInput{
		PolicyArn: aws.String(arn),
	}

	response, err := iamconn.ListPolicyVersions(request)
	if err != nil {
		return nil, fmt.Errorf("Error listing versions for IAM policy %s: %s", arn, err)
	}
	return response.Versions, nil
}

func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error {
	d.SetId(*policy.Arn)
	if policy.Description != nil {
		// the description isn't present in the response to CreatePolicy.
		if err := d.Set("description", policy.Description); err != nil {
			return err
		}
	}
	if err := d.Set("path", policy.Path); err != nil {
		return err
	}
	if err := d.Set("name", policy.PolicyName); err != nil {
		return err
	}
	if err := d.Set("arn", policy.Arn); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package server

import (
	"fmt"
	"io"
	"net"

	"github.com/spf13/cobra"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericoptions "k8s.io/apiserver/pkg/server/options"
	"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
	"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
	"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
	"k8s.io/sample-apiserver/pkg/apiserver"
	clientset "k8s.io/sample-apiserver/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/sample-apiserver/pkg/client/informers_generated/internalversion"
)

const defaultEtcdPathPrefix = "/registry/wardle.kubernetes.io"

type WardleServerOptions struct {
	RecommendedOptions *genericoptions.RecommendedOptions
	Admission          *genericoptions.AdmissionOptions

	StdOut io.Writer
	StdErr io.Writer
}

func NewWardleServerOptions(out, errOut io.Writer) *WardleServerOptions {
	o := &WardleServerOptions{
		RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Scheme, apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion)),
		Admission:          genericoptions.NewAdmissionOptions(),

		StdOut: out,
		StdErr: errOut,
	}

	return o
}

// NewCommandStartMaster provides a CLI handler for 'start master' command
func NewCommandStartWardleServer(out, errOut io.Writer, stopCh <-chan struct{}) *cobra.Command {
	o := NewWardleServerOptions(out, errOut)

	cmd := &cobra.Command{
		Short: "Launch a wardle API server",
		Long:  "Launch a wardle API server",
		RunE: func(c *cobra.Command, args []string) error {
			if err := o.Complete(); err != nil {
				return err
			}
			if err := o.Validate(args); err != nil {
				return err
			}
			if err := o.RunWardleServer(stopCh); err != nil {
				return err
			}
			return nil
		},
	}

	flags := cmd.Flags()
	o.RecommendedOptions.AddFlags(flags)
	o.Admission.AddFlags(flags)

	return cmd
}

func (o WardleServerOptions) Validate(args []string) error {
	errors := []error{}
	errors = append(errors, o.RecommendedOptions.Validate()...)
	errors = append(errors, o.Admission.Validate()...)
	return utilerrors.NewAggregate(errors)
}

func (o *WardleServerOptions) Complete() error {
	return nil
}

func (o WardleServerOptions) Config() (*apiserver.Config, error) {
	// register admission plugins
	banflunder.Register(o.Admission.Plugins)

	// TODO have a "real" external address
	if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil {
		return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
	}

	serverConfig := genericapiserver.NewRecommendedConfig(apiserver.Codecs)
	if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
		return nil, err
	}

	client, err := clientset.NewForConfig(serverConfig.LoopbackClientConfig)
	if err != nil {
		return nil, err
	}
	informerFactory := informers.NewSharedInformerFactory(client, serverConfig.LoopbackClientConfig.Timeout)
	admissionInitializer, err := wardleinitializer.New(informerFactory)
	if err != nil {
		return nil, err
	}

	if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, admissionInitializer); err != nil {
		return nil, err
	}

	config := &apiserver.Config{
		GenericConfig: serverConfig,
		ExtraConfig:   apiserver.ExtraConfig{},
	}
	return config, nil
}

func (o WardleServerOptions) RunWardleServer(stopCh <-chan struct{}) error {
	config, err := o.Config()
	if err != nil {
		return err
	}

	server, err := config.Complete().New()
	if err != nil {
		return err
	}

	server.GenericAPIServer.AddPostStartHook("start-sample-server-informers", func(context genericapiserver.PostStartHookContext) error {
		config.GenericConfig.SharedInformerFactory.Start(context.StopCh)
		return nil
	})

	return server.GenericAPIServer.PrepareRun().Run(stopCh)
}
<commit_msg>removes Authorizer and ExternalClientSet from kubeapiserver's admission initializer.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package server

import (
	"fmt"
	"io"
	"net"

	"github.com/spf13/cobra"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericoptions "k8s.io/apiserver/pkg/server/options"
	"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
	"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
	"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
	"k8s.io/sample-apiserver/pkg/apiserver"
	clientset "k8s.io/sample-apiserver/pkg/client/clientset_generated/internalclientset"
	informers "k8s.io/sample-apiserver/pkg/client/informers_generated/internalversion"
)

const defaultEtcdPathPrefix = "/registry/wardle.kubernetes.io"

type WardleServerOptions struct {
	RecommendedOptions *genericoptions.RecommendedOptions
	Admission          *genericoptions.AdmissionOptions

	StdOut io.Writer
	StdErr io.Writer
}

func NewWardleServerOptions(out, errOut io.Writer) *WardleServerOptions {
	o := &WardleServerOptions{
		RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Scheme, apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion)),
		Admission:          genericoptions.NewAdmissionOptions(),

		StdOut: out,
		StdErr: errOut,
	}

	return o
}

// NewCommandStartMaster provides a CLI handler for 'start master' command
func NewCommandStartWardleServer(out, errOut io.Writer, stopCh <-chan struct{}) *cobra.Command {
	o := NewWardleServerOptions(out, errOut)

	cmd := &cobra.Command{
		Short: "Launch a wardle API server",
		Long:  "Launch a wardle API server",
		RunE: func(c *cobra.Command, args []string) error {
			if err := o.Complete(); err != nil {
				return err
			}
			if err := o.Validate(args); err != nil {
				return err
			}
			if err := o.RunWardleServer(stopCh); err != nil {
				return err
			}
			return nil
		},
	}

	flags := cmd.Flags()
	o.RecommendedOptions.AddFlags(flags)
	o.Admission.AddFlags(flags)

	return cmd
}

func (o WardleServerOptions) Validate(args []string) error {
	errors := []error{}
	errors = append(errors, o.RecommendedOptions.Validate()...)
	errors = append(errors, o.Admission.Validate()...)
	return utilerrors.NewAggregate(errors)
}

func (o *WardleServerOptions) Complete() error {
	return nil
}

func (o WardleServerOptions) Config() (*apiserver.Config, error) {
	// register admission plugins
	banflunder.Register(o.Admission.Plugins)

	// TODO have a "real" external address
	if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil {
		return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
	}

	serverConfig := genericapiserver.NewRecommendedConfig(apiserver.Codecs)
	if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
		return nil, err
	}

	client, err := clientset.NewForConfig(serverConfig.LoopbackClientConfig)
	if err != nil {
		return nil, err
	}
	informerFactory := informers.NewSharedInformerFactory(client, serverConfig.LoopbackClientConfig.Timeout)
	admissionInitializer, err := wardleinitializer.New(informerFactory)
	if err != nil {
		return nil, err
	}

	if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, serverConfig.ClientConfig, admissionInitializer); err != nil {
		return nil, err
	}

	config := &apiserver.Config{
		GenericConfig: serverConfig,
		ExtraConfig:   apiserver.ExtraConfig{},
	}
	return config, nil
}

func (o WardleServerOptions) RunWardleServer(stopCh <-chan struct{}) error {
	config, err := o.Config()
	if err != nil {
		return err
	}

	server, err := config.Complete().New()
	if err != nil {
		return err
	}

	server.GenericAPIServer.AddPostStartHook("start-sample-server-informers", func(context genericapiserver.PostStartHookContext) error {
		config.GenericConfig.SharedInformerFactory.Start(context.StopCh)
		return nil
	})

	return server.GenericAPIServer.PrepareRun().Run(stopCh)
}
<|endoftext|>


<commit_before>package db

import (
	"github.com/SparrowDb/sparrowdb/db/index"
	"github.com/SparrowDb/sparrowdb/model"
	"github.com/SparrowDb/sparrowdb/util"
	"github.com/elgs/cron"
)

var (
	// keeps all active cron with dbname and id of cron
	activeCron map[string]int

	schedule *cron.Cron
)

func init() {
	activeCron = make(map[string]int)
	schedule = cron.New()
	schedule.Start()
}

func registerDbCompaction(db *Database) {
	// Cron starts compaction in another goroutine
	f, _ := schedule.AddFunc(db.Descriptor.CronExp, func() { doCompaction(db) })
	activeCron[db.Descriptor.Name] = f
}

func removeDbCompaction(dbname string) {
	if job, ok := activeCron[dbname]; ok == true {
		// removes the job from cron and delete from active cron list
		schedule.RemoveFunc(job)
		delete(activeCron, dbname)
	}
}

type tombstoneMark struct {
	path string
	index.Entry
}

func doCompaction(db *Database) {
	go db.compactionNotification()

	// get all tombstones from database
	tombstones := geTombstonesFromDb(db)

	// get all tombstones from commitlog
	tbCommitlog := getTombstonesFromCommitlog(db)
	tombstones = append(tombstones, tbCommitlog...)

	// iterate over all dataHolders
	for _, dh := range db.dhList {

		// check if dataHolder has any tombstone
		if dhContainsAnyTombstone(&dh, &tombstones) {
			// if found tombstone, get index table of dataHolder
			// and reinsert in commitlog non tombstone entry
			table := dh.summary.GetTable()

			for _, v := range table {
				if c := containsKey(v.Key, &tombstones); c == false {
					bs, _ := dh.Get(v.Offset)
					df := model.NewDataDefinitionFromByteStream(bs)
					_ = df
					//db.commitlog.Add(df.Key, df.Status, bs)
				}
			}
			util.DeleteDir(dh.path)
		}
	}

	db.compFinish <- true
}

func getTombstonesFromCommitlog(db *Database) []tombstoneMark {
	var tombstones []tombstoneMark

	summary := db.commitlog.summary.GetTable()

	for _, v := range summary {
		if v.Status == model.DataDefinitionRemoved {
			tombstones = append(tombstones, tombstoneMark{db.commitlog.filepath, *v})
		}
	}
	return tombstones
}

func geTombstonesFromDb(db *Database) []tombstoneMark {
	var tombstones []tombstoneMark
	echan := make(chan []tombstoneMark)

	// iterate over all dataHolders
	for _, dh := range db.dhList {
		// search in dataHolder index for tombstone
		go func(dh *dataHolder, results chan []tombstoneMark) {
			var result []tombstoneMark

			// get index table
			idxSummary := dh.summary.GetTable()

			for _, v := range idxSummary {
				if v.Status == model.DataDefinitionRemoved {
					result = append(result, tombstoneMark{dh.path, *v})
				}
			}
			results <- result
		}(&dh, echan)
	}

	dhListLen := len(db.dhList)
	processed := 0
	for processed < dhListLen {
		select {
		case result := <-echan:
			tombstones = append(tombstones, result...)
			processed++
		}
	}

	return tombstones
}

func containsKey(key uint32, list *[]tombstoneMark) bool {
	for _, tb := range *list {
		if tb.Key == key {
			return true
		}
	}
	return false
}

func dhContainsAnyTombstone(dh *dataHolder, list *[]tombstoneMark) bool {
	var result bool
	dhs := dh.summary.GetTable()

	for _, v := range *list {
		if _, ok := dhs[v.Key]; ok == true {
			return true
		}
	}

	return result
}
<commit_msg>update compaction<commit_after>package db

import (
	"github.com/SparrowDb/sparrowdb/db/index"
	"github.com/SparrowDb/sparrowdb/model"
	"github.com/SparrowDb/sparrowdb/util"
	"github.com/elgs/cron"
)

var (
	// keeps all active cron with dbname and id of cron
	activeCron map[string]int

	schedule *cron.Cron
)

func init() {
	activeCron = make(map[string]int)
	schedule = cron.New()
	schedule.Start()
}

func registerDbCompaction(db *Database) {
	// Cron starts compaction in another goroutine
	f, _ := schedule.AddFunc(db.Descriptor.CronExp, func() { doCompaction(db) })
	activeCron[db.Descriptor.Name] = f
}

func removeDbCompaction(dbname string) {
	if job, ok := activeCron[dbname]; ok == true {
		// removes the job from cron and delete from active cron list
		schedule.RemoveFunc(job)
		delete(activeCron, dbname)
	}
}

type tombstoneMark struct {
	path string
	index.Entry
}

func doCompaction(db *Database) {
	go db.compactionNotification()

	// get all tombstones from database
	tombstones := geTombstonesFromDb(db)

	// get all tombstones from commitlog
	tbCommitlog := getTombstonesFromCommitlog(db)
	tombstones = append(tombstones, tbCommitlog...)

	// iterate over all dataHolders
	for _, dh := range db.dhList {

		// check if dataHolder has any tombstone
		if dhContainsAnyTombstone(&dh, &tombstones) {
			// if found tombstone, get index table of dataHolder
			// and reinsert in commitlog non tombstone entry
			table := dh.summary.GetTable()

			for _, v := range table {
				if c := containsKey(v.Key, &tombstones); c == false {
					bs, _ := dh.Get(v.Offset)
					df := model.NewDataDefinitionFromByteStream(bs)
					db.commitlog.Add(df.Key, df.Status, df.Revision, bs)
				}
			}
			util.DeleteDir(dh.path)
		}
	}

	db.compFinish <- true
}

func getTombstonesFromCommitlog(db *Database) []tombstoneMark {
	var tombstones []tombstoneMark

	summary := db.commitlog.summary.GetTable()

	for _, v := range summary {
		if v.Status == model.DataDefinitionRemoved {
			tombstones = append(tombstones, tombstoneMark{db.commitlog.filepath, *v})
		}
	}
	return tombstones
}

func geTombstonesFromDb(db *Database) []tombstoneMark {
	var tombstones []tombstoneMark
	echan := make(chan []tombstoneMark)

	// iterate over all dataHolders
	for _, dh := range db.dhList {
		// search in dataHolder index for tombstone
		go func(dh *dataHolder, results chan []tombstoneMark) {
			var result []tombstoneMark

			// get index table
			idxSummary := dh.summary.GetTable()

			for _, v := range idxSummary {
				if v.Status == model.DataDefinitionRemoved {
					result = append(result, tombstoneMark{dh.path, *v})
				}
			}
			results <- result
		}(&dh, echan)
	}

	dhListLen := len(db.dhList)
	processed := 0
	for processed < dhListLen {
		select {
		case result := <-echan:
			tombstones = append(tombstones, result...)
			processed++
		}
	}

	return tombstones
}

func containsKey(key uint32, list *[]tombstoneMark) bool {
	for _, tb := range *list {
		if tb.Key == key {
			return true
		}
	}
	return false
}

func dhContainsAnyTombstone(dh *dataHolder, list *[]tombstoneMark) bool {
	var result bool
	dhs := dh.summary.GetTable()

	for _, v := range *list {
		if _, ok := dhs[v.Key]; ok == true {
			return true
		}
	}

	return result
}
<|endoftext|>