<commit_before>package baudio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"runtime"
	"strconv"
	//"time"
)

const (
	FuncValueTypeFloat    = 0
	FuncValueTypeNotFloat = 1
)

type GeneratorFunc func(t float64, i int) float64
type RuntimeOption map[string]string

type AudioChannel struct {
	funcValueType int
	funcs         []GeneratorFunc
}

func newAudioChannel(fvt int) *AudioChannel {
	bc := &AudioChannel{
		funcValueType: fvt,
		funcs:         make([]GeneratorFunc, 0),
	}
	return bc
}

func (bc *AudioChannel) push(fn GeneratorFunc) {
	bc.funcs = append(bc.funcs, fn)
}

type BOptions struct {
	Size int
	Rate int
}

func NewBOptions() *BOptions {
	return &BOptions{
		Size: 2048,
		Rate: 44000,
	}
}

type B struct {
	readable   bool
	size       int
	rate       int
	t          float64
	i          int
	paused     bool
	ended      bool
	destroyed  bool
	channels   []*AudioChannel
	chEnd      chan bool
	chEndSox   chan bool
	chResume   chan func()
	chNextTick chan bool
	pipeReader *io.PipeReader
	pipeWriter *io.PipeWriter
	sox        *Sox
}

func New(opts *BOptions, fn GeneratorFunc) *B {
	b := &B{
		readable:   true,
		size:       2048,
		rate:       44000,
		t:          0,
		i:          0,
		paused:     false,
		ended:      false,
		destroyed:  false,
		chEnd:      make(chan bool),
		chEndSox:   make(chan bool),
		chResume:   make(chan func()),
		chNextTick: make(chan bool),
	}
	b.pipeReader, b.pipeWriter = io.Pipe()
	if opts != nil {
		b.size = opts.Size
		b.rate = opts.Rate
	}
	if fn != nil {
		b.Push(fn)
	}
	go func() {
		if b.paused {
			b.chResume <- func() {
				go b.loop()
				b.main()
			}
		} else {
			go b.loop()
			b.main()
		}
	}()
	//go b.loop()
	return b
}

func (b *B) main() {
	for {
		// 2013-02-28 koyachi ここで何かしないとループまわらないのなぜ
		// => fmt.PrinfすることでnodeのnextTick的なものがつまれててそのうちPlay()のread待ちまで進めるのでは。
		//L1:
		//fmt.Println("main loop header")
		//fmt.Printf(".")
		//time.Sleep(1 * time.Millisecond)
		runtime.Gosched()
		select {
		case <-b.chEnd:
			fmt.Println("main chEnd")
			b.terminateMain()
			break
		case fn := <-b.chResume:
			//fmt.Println("main chResume")
			fn()
		case <-b.chNextTick:
			//fmt.Println("main chNextTick")
			go b.loop()
			//b.loop()
		default:
			//fmt.Println("main default")
			//go b.loop()
			//goto L1
		}
	}
}

func (b *B) terminateMain() {
	b.pipeWriter.Close()
	b.ended = true
	b.chEndSox <- true
}

// TODO: To Go Style (end,destroy,pause,resume are node.js's Stream interface.)
func (b *B) End() {
	b.ended = true
}

func (b *B) Destroy() {
	b.destroyed = true
	b.chEnd <- true
}

func (b *B) Pause() {
	b.paused = true
}

func (b *B) Resume() {
	if !b.paused {
		return
	}
	b.paused = false
	b.chResume <- func() {}
}

func (b *B) AddChannel(funcValueType int, fn GeneratorFunc) {
	bc := newAudioChannel(funcValueType)
	bc.push(fn)
	b.channels = append(b.channels, bc)
}

func (b *B) Push(fn GeneratorFunc) {
	index := len(b.channels)
	if len(b.channels) <= index {
		bc := newAudioChannel(FuncValueTypeFloat)
		b.channels = append(b.channels, bc)
	}
	b.channels[index].funcs = append(b.channels[index].funcs, fn)
}

func (b *B) loop() {
	buf := b.tick()
	if b.destroyed {
		// no more events
		//fmt.Println("loop destroyed")
	} else if b.paused {
		//fmt.Println("loop paused")
		b.chResume <- func() {
			b.pipeWriter.Write(buf.Bytes())
			b.chNextTick <- true
		}
	} else {
		//fmt.Println("loop !(destroyed || paused)")
		b.pipeWriter.Write(buf.Bytes())
		if b.ended {
			//fmt.Println("loop ended")
			b.chEnd <- true
		} else {
			//fmt.Println("loop !ended")
			b.chNextTick <- true
		}
	}
}

func (b *B) tick() *bytes.Buffer {
	bufSize := b.size * len(b.channels)
	byteBuffer := make([]byte, 0)
	buf := bytes.NewBuffer(byteBuffer)
	for i := 0; i < bufSize; i += 2 {
		lrIndex := int(i / 2)
		lenCh := len(b.channels)
		ch := b.channels[lrIndex%lenCh]
		t := float64(b.t) + math.Floor(float64(lrIndex))/float64(b.rate)/float64(lenCh)
		counter := b.i + int(math.Floor(float64(lrIndex)/float64(lenCh)))

		value := float64(0)
		n := float64(0)
		for j := 0; j < len(ch.funcs); j++ {
			x := ch.funcs[j](float64(t), counter)
			n += x
		}
		n /= float64(len(ch.funcs))

		if ch.funcValueType == FuncValueTypeFloat {
			value = signed(n)
		} else {
			b_ := math.Pow(2, float64(ch.funcValueType))
			x := math.Mod(math.Floor(n), b_) / b_ * math.Pow(2, 15)
			value = x
		}
		if err := binary.Write(buf, binary.LittleEndian, int16(clamp(value))); err != nil {
			panic(err)
		}
	}
	b.i += b.size / 2
	b.t += float64(b.size) / float64(2) / float64(b.rate)
	return buf
}

func clamp(x float64) float64 {
	return math.Max(math.Min(x, math.Pow(2, 15)-1), -math.Pow(2, 15))
}

func signed(n float64) float64 {
	b := math.Pow(2, 15)
	if n > 0 {
		return math.Min(b-1, math.Floor(b*n-1))
	}
	return math.Max(-b, math.Ceil(b*n-1))
}

func (b *B) Play(opts RuntimeOption) {
	go SoxPlay(mergeArgs(opts, RuntimeOption{
		"c": strconv.Itoa(len(b.channels)),
		"r": strconv.Itoa(b.rate),
		"t": "s16",
		"-": "DUMMY",
	}), b.waveReceiver())
	<-b.chEndSox
	b.pipeReader.Close()
}

func (b *B) Record(file string, opts RuntimeOption) {
	go SoxRecord(file, mergeArgs(opts, RuntimeOption{
		"c": strconv.Itoa(len(b.channels)),
		"r": strconv.Itoa(b.rate),
		"t": "s16",
		"-": "DUMMY",
	}), b.waveReceiver())
	<-b.chEndSox
	b.pipeReader.Close()
}

func (b *B) waveReceiver() func() []byte {
	readBuf := make([]byte, b.size*len(b.channels))
	return func() []byte {
		if _, err := b.pipeReader.Read(readBuf); err != nil {
			panic(err)
		}
		return readBuf
	}
}
<commit_msg>rename BOptions to AudioBufferOption.<commit_after>package baudio

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"runtime"
	"strconv"
	//"time"
)

const (
	FuncValueTypeFloat    = 0
	FuncValueTypeNotFloat = 1
)

type GeneratorFunc func(t float64, i int) float64
type RuntimeOption map[string]string

type AudioChannel struct {
	funcValueType int
	funcs         []GeneratorFunc
}

func newAudioChannel(fvt int) *AudioChannel {
	bc := &AudioChannel{
		funcValueType: fvt,
		funcs:         make([]GeneratorFunc, 0),
	}
	return bc
}

func (bc *AudioChannel) push(fn GeneratorFunc) {
	bc.funcs = append(bc.funcs, fn)
}

type AudioBufferOption struct {
	Size int
	Rate int
}

func NewAudioBufferOption() *AudioBufferOption {
	return &AudioBufferOption{
		Size: 2048,
		Rate: 44000,
	}
}

type B struct {
	readable   bool
	size       int
	rate       int
	t          float64
	i          int
	paused     bool
	ended      bool
	destroyed  bool
	channels   []*AudioChannel
	chEnd      chan bool
	chEndSox   chan bool
	chResume   chan func()
	chNextTick chan bool
	pipeReader *io.PipeReader
	pipeWriter *io.PipeWriter
	sox        *Sox
}

func New(opts *AudioBufferOption, fn GeneratorFunc) *B {
	b := &B{
		readable:   true,
		size:       2048,
		rate:       44000,
		t:          0,
		i:          0,
		paused:     false,
		ended:      false,
		destroyed:  false,
		chEnd:      make(chan bool),
		chEndSox:   make(chan bool),
		chResume:   make(chan func()),
		chNextTick: make(chan bool),
	}
	b.pipeReader, b.pipeWriter = io.Pipe()
	if opts != nil {
		b.size = opts.Size
		b.rate = opts.Rate
	}
	if fn != nil {
		b.Push(fn)
	}
	go func() {
		if b.paused {
			b.chResume <- func() {
				go b.loop()
				b.main()
			}
		} else {
			go b.loop()
			b.main()
		}
	}()
	//go b.loop()
	return b
}

func (b *B) main() {
	for {
		// 2013-02-28 koyachi ここで何かしないとループまわらないのなぜ
		// => fmt.PrinfすることでnodeのnextTick的なものがつまれててそのうちPlay()のread待ちまで進めるのでは。
		//L1:
		//fmt.Println("main loop header")
		//fmt.Printf(".")
		//time.Sleep(1 * time.Millisecond)
		runtime.Gosched()
		select {
		case <-b.chEnd:
			fmt.Println("main chEnd")
			b.terminateMain()
			break
		case fn := <-b.chResume:
			//fmt.Println("main chResume")
			fn()
		case <-b.chNextTick:
			//fmt.Println("main chNextTick")
			go b.loop()
			//b.loop()
		default:
			//fmt.Println("main default")
			//go b.loop()
			//goto L1
		}
	}
}

func (b *B) terminateMain() {
	b.pipeWriter.Close()
	b.ended = true
	b.chEndSox <- true
}

// TODO: To Go Style (end,destroy,pause,resume are node.js's Stream interface.)
func (b *B) End() {
	b.ended = true
}

func (b *B) Destroy() {
	b.destroyed = true
	b.chEnd <- true
}

func (b *B) Pause() {
	b.paused = true
}

func (b *B) Resume() {
	if !b.paused {
		return
	}
	b.paused = false
	b.chResume <- func() {}
}

func (b *B) AddChannel(funcValueType int, fn GeneratorFunc) {
	bc := newAudioChannel(funcValueType)
	bc.push(fn)
	b.channels = append(b.channels, bc)
}

func (b *B) Push(fn GeneratorFunc) {
	index := len(b.channels)
	if len(b.channels) <= index {
		bc := newAudioChannel(FuncValueTypeFloat)
		b.channels = append(b.channels, bc)
	}
	b.channels[index].funcs = append(b.channels[index].funcs, fn)
}

func (b *B) loop() {
	buf := b.tick()
	if b.destroyed {
		// no more events
		//fmt.Println("loop destroyed")
	} else if b.paused {
		//fmt.Println("loop paused")
		b.chResume <- func() {
			b.pipeWriter.Write(buf.Bytes())
			b.chNextTick <- true
		}
	} else {
		//fmt.Println("loop !(destroyed || paused)")
		b.pipeWriter.Write(buf.Bytes())
		if b.ended {
			//fmt.Println("loop ended")
			b.chEnd <- true
		} else {
			//fmt.Println("loop !ended")
			b.chNextTick <- true
		}
	}
}

func (b *B) tick() *bytes.Buffer {
	bufSize := b.size * len(b.channels)
	byteBuffer := make([]byte, 0)
	buf := bytes.NewBuffer(byteBuffer)
	for i := 0; i < bufSize; i += 2 {
		lrIndex := int(i / 2)
		lenCh := len(b.channels)
		ch := b.channels[lrIndex%lenCh]
		t := float64(b.t) + math.Floor(float64(lrIndex))/float64(b.rate)/float64(lenCh)
		counter := b.i + int(math.Floor(float64(lrIndex)/float64(lenCh)))

		value := float64(0)
		n := float64(0)
		for j := 0; j < len(ch.funcs); j++ {
			x := ch.funcs[j](float64(t), counter)
			n += x
		}
		n /= float64(len(ch.funcs))

		if ch.funcValueType == FuncValueTypeFloat {
			value = signed(n)
		} else {
			b_ := math.Pow(2, float64(ch.funcValueType))
			x := math.Mod(math.Floor(n), b_) / b_ * math.Pow(2, 15)
			value = x
		}
		if err := binary.Write(buf, binary.LittleEndian, int16(clamp(value))); err != nil {
			panic(err)
		}
	}
	b.i += b.size / 2
	b.t += float64(b.size) / float64(2) / float64(b.rate)
	return buf
}

func clamp(x float64) float64 {
	return math.Max(math.Min(x, math.Pow(2, 15)-1), -math.Pow(2, 15))
}

func signed(n float64) float64 {
	b := math.Pow(2, 15)
	if n > 0 {
		return math.Min(b-1, math.Floor(b*n-1))
	}
	return math.Max(-b, math.Ceil(b*n-1))
}

func (b *B) Play(opts RuntimeOption) {
	go SoxPlay(mergeArgs(opts, RuntimeOption{
		"c": strconv.Itoa(len(b.channels)),
		"r": strconv.Itoa(b.rate),
		"t": "s16",
		"-": "DUMMY",
	}), b.waveReceiver())
	<-b.chEndSox
	b.pipeReader.Close()
}

func (b *B) Record(file string, opts RuntimeOption) {
	go SoxRecord(file, mergeArgs(opts, RuntimeOption{
		"c": strconv.Itoa(len(b.channels)),
		"r": strconv.Itoa(b.rate),
		"t": "s16",
		"-": "DUMMY",
	}), b.waveReceiver())
	<-b.chEndSox
	b.pipeReader.Close()
}

func (b *B) waveReceiver() func() []byte {
	readBuf := make([]byte, b.size*len(b.channels))
	return func() []byte {
		if _, err := b.pipeReader.Read(readBuf); err != nil {
			panic(err)
		}
		return readBuf
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/labels"

	"k8s.io/test-infra/flagutil"
	"k8s.io/test-infra/prow/config"
	prowflagutil "k8s.io/test-infra/prow/flagutil"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/logrusutil"
	"k8s.io/test-infra/prow/metrics"
	"k8s.io/test-infra/prow/plank"
)

type options struct {
	totURL string

	configPath    string
	jobConfigPath string
	buildCluster  string
	selector      string
	skipReport    bool

	dryRun     bool
	kubernetes prowflagutil.KubernetesOptions
	github     prowflagutil.GitHubOptions
}

func gatherOptions() options {
	o := options{}
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	fs.StringVar(&o.totURL, "tot-url", "", "Tot URL")

	fs.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.")
	fs.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.")
	fs.StringVar(&o.buildCluster, "build-cluster", "", "Path to file containing a YAML-marshalled kube.Cluster object. If empty, uses the local cluster.")
	fs.StringVar(&o.selector, "label-selector", kube.EmptySelector, "Label selector to be applied in prowjobs. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for constructing a label selector.")
	fs.BoolVar(&o.skipReport, "--skip-report", false, "Whether or not to ignore report with githubClient")

	fs.BoolVar(&o.dryRun, "dry-run", true, "Whether or not to make mutating API calls to GitHub.")
	for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} {
		group.AddFlags(fs)
	}

	fs.Parse(os.Args[1:])
	return o
}

func (o *options) Validate() error {
	for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} {
		if err := group.Validate(o.dryRun); err != nil {
			return err
		}
	}

	if _, err := labels.Parse(o.selector); err != nil {
		return fmt.Errorf("parse label selector: %v", err)
	}

	return nil
}

func main() {
	o := gatherOptions()
	if err := o.Validate(); err != nil {
		logrus.WithError(err).Fatal("Invalid options")
	}

	logrus.SetFormatter(
		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "plank"}),
	)

	configAgent := &config.Agent{}
	if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil {
		logrus.WithError(err).Fatal("Error starting config agent.")
	}

	secretAgent := &config.SecretAgent{}
	if o.github.TokenPath != "" {
		if err := secretAgent.Start([]string{o.github.TokenPath}); err != nil {
			logrus.WithError(err).Fatal("Error starting secrets agent.")
		}
	}

	githubClient, err := o.github.GitHubClient(secretAgent, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("Error getting GitHub client.")
	}

	kubeClient, err := o.kubernetes.Client(configAgent.Config().ProwJobNamespace, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("Error getting kube client.")
	}

	var pkcs map[string]*kube.Client
	if o.dryRun {
		pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: kubeClient}
	} else {
		if o.buildCluster == "" {
			pkc, err := kube.NewClientInCluster(configAgent.Config().PodNamespace)
			if err != nil {
				logrus.WithError(err).Fatal("Error getting kube client.")
			}
			pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: pkc}
		} else {
			pkcs, err = kube.ClientMapFromFile(o.buildCluster, configAgent.Config().PodNamespace)
			if err != nil {
				logrus.WithError(err).Fatal("Error getting kube client to build cluster.")
			}
		}
	}

	c, err := plank.NewController(kubeClient, pkcs, githubClient, nil, configAgent, o.totURL, o.selector, o.skipReport)
	if err != nil {
		logrus.WithError(err).Fatal("Error creating plank controller.")
	}

	// Push metrics to the configured prometheus pushgateway endpoint.
	pushGateway := configAgent.Config().PushGateway
	if pushGateway.Endpoint != "" {
		go metrics.PushMetrics("plank", pushGateway.Endpoint, pushGateway.Interval)
	}
	// serve prometheus metrics.
	go serve()
	// gather metrics for the jobs handled by plank.
	go gather(c)

	tick := time.Tick(30 * time.Second)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	for {
		select {
		case <-tick:
			start := time.Now()
			if err := c.Sync(); err != nil {
				logrus.WithError(err).Error("Error syncing.")
			}
			logrus.WithField("duration", fmt.Sprintf("%v", time.Since(start))).Info("Synced")
		case <-sig:
			logrus.Info("Plank is shutting down...")
			return
		}
	}
}

// serve starts a http server and serves prometheus metrics.
// Meant to be called inside a goroutine.
func serve() {
	http.Handle("/metrics", promhttp.Handler())
	logrus.WithError(http.ListenAndServe(":8080", nil)).Fatal("ListenAndServe returned.")
}

// gather metrics from plank.
// Meant to be called inside a goroutine.
func gather(c *plank.Controller) {
	tick := time.Tick(30 * time.Second)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	for {
		select {
		case <-tick:
			start := time.Now()
			c.SyncMetrics()
			logrus.WithField("metrics-duration", fmt.Sprintf("%v", time.Since(start))).Debug("Metrics synced")
		case <-sig:
			logrus.Debug("Plank gatherer is shutting down...")
			return
		}
	}
}
<commit_msg>less dashes<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/labels"

	"k8s.io/test-infra/flagutil"
	"k8s.io/test-infra/prow/config"
	prowflagutil "k8s.io/test-infra/prow/flagutil"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/logrusutil"
	"k8s.io/test-infra/prow/metrics"
	"k8s.io/test-infra/prow/plank"
)

type options struct {
	totURL string

	configPath    string
	jobConfigPath string
	buildCluster  string
	selector      string
	skipReport    bool

	dryRun     bool
	kubernetes prowflagutil.KubernetesOptions
	github     prowflagutil.GitHubOptions
}

func gatherOptions() options {
	o := options{}
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)

	fs.StringVar(&o.totURL, "tot-url", "", "Tot URL")

	fs.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.")
	fs.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.")
	fs.StringVar(&o.buildCluster, "build-cluster", "", "Path to file containing a YAML-marshalled kube.Cluster object. If empty, uses the local cluster.")
	fs.StringVar(&o.selector, "label-selector", kube.EmptySelector, "Label selector to be applied in prowjobs. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for constructing a label selector.")
	fs.BoolVar(&o.skipReport, "skip-report", false, "Whether or not to ignore report with githubClient")

	fs.BoolVar(&o.dryRun, "dry-run", true, "Whether or not to make mutating API calls to GitHub.")
	for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} {
		group.AddFlags(fs)
	}

	fs.Parse(os.Args[1:])
	return o
}

func (o *options) Validate() error {
	for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} {
		if err := group.Validate(o.dryRun); err != nil {
			return err
		}
	}

	if _, err := labels.Parse(o.selector); err != nil {
		return fmt.Errorf("parse label selector: %v", err)
	}

	return nil
}

func main() {
	o := gatherOptions()
	if err := o.Validate(); err != nil {
		logrus.WithError(err).Fatal("Invalid options")
	}

	logrus.SetFormatter(
		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "plank"}),
	)

	configAgent := &config.Agent{}
	if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil {
		logrus.WithError(err).Fatal("Error starting config agent.")
	}

	secretAgent := &config.SecretAgent{}
	if o.github.TokenPath != "" {
		if err := secretAgent.Start([]string{o.github.TokenPath}); err != nil {
			logrus.WithError(err).Fatal("Error starting secrets agent.")
		}
	}

	githubClient, err := o.github.GitHubClient(secretAgent, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("Error getting GitHub client.")
	}

	kubeClient, err := o.kubernetes.Client(configAgent.Config().ProwJobNamespace, o.dryRun)
	if err != nil {
		logrus.WithError(err).Fatal("Error getting kube client.")
	}

	var pkcs map[string]*kube.Client
	if o.dryRun {
		pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: kubeClient}
	} else {
		if o.buildCluster == "" {
			pkc, err := kube.NewClientInCluster(configAgent.Config().PodNamespace)
			if err != nil {
				logrus.WithError(err).Fatal("Error getting kube client.")
			}
			pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: pkc}
		} else {
			pkcs, err = kube.ClientMapFromFile(o.buildCluster, configAgent.Config().PodNamespace)
			if err != nil {
				logrus.WithError(err).Fatal("Error getting kube client to build cluster.")
			}
		}
	}

	c, err := plank.NewController(kubeClient, pkcs, githubClient, nil, configAgent, o.totURL, o.selector, o.skipReport)
	if err != nil {
		logrus.WithError(err).Fatal("Error creating plank controller.")
	}

	// Push metrics to the configured prometheus pushgateway endpoint.
	pushGateway := configAgent.Config().PushGateway
	if pushGateway.Endpoint != "" {
		go metrics.PushMetrics("plank", pushGateway.Endpoint, pushGateway.Interval)
	}
	// serve prometheus metrics.
	go serve()
	// gather metrics for the jobs handled by plank.
	go gather(c)

	tick := time.Tick(30 * time.Second)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	for {
		select {
		case <-tick:
			start := time.Now()
			if err := c.Sync(); err != nil {
				logrus.WithError(err).Error("Error syncing.")
			}
			logrus.WithField("duration", fmt.Sprintf("%v", time.Since(start))).Info("Synced")
		case <-sig:
			logrus.Info("Plank is shutting down...")
			return
		}
	}
}

// serve starts a http server and serves prometheus metrics.
// Meant to be called inside a goroutine.
func serve() {
	http.Handle("/metrics", promhttp.Handler())
	logrus.WithError(http.ListenAndServe(":8080", nil)).Fatal("ListenAndServe returned.")
}

// gather metrics from plank.
// Meant to be called inside a goroutine.
func gather(c *plank.Controller) {
	tick := time.Tick(30 * time.Second)
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	for {
		select {
		case <-tick:
			start := time.Now()
			c.SyncMetrics()
			logrus.WithField("metrics-duration", fmt.Sprintf("%v", time.Since(start))).Debug("Metrics synced")
		case <-sig:
			logrus.Debug("Plank gatherer is shutting down...")
			return
		}
	}
}
<|endoftext|>


<commit_before>package mocks

import "github.com/control-center/serviced/volume"
import "github.com/stretchr/testify/mock"

var DriverName volume.DriverType = "mock"

type Driver struct {
	mock.Mock
}

func (m *Driver) Root() string {
	ret := m.Called()

	r0 := ret.Get(0).(string)

	return r0
}
func (m *Driver) DriverType() volume.DriverType {
	return DriverName
}

func (m *Driver) Create(volumeName string) (volume.Volume, error) {
	ret := m.Called(volumeName)

	r0 := ret.Get(0).(volume.Volume)
	r1 := ret.Error(1)

	return r0, r1
}
func (m *Driver) Remove(volumeName string) error {
	ret := m.Called(volumeName)

	r0 := ret.Error(0)

	return r0
}
func (m *Driver) Get(volumeName string) (volume.Volume, error) {
	ret := m.Called(volumeName)

	r0 := ret.Get(0).(volume.Volume)
	r1 := ret.Error(1)

	return r0, r1
}
func (m *Driver) Release(volumeName string) error {
	ret := m.Called(volumeName)

	r0 := ret.Error(0)

	return r0
}
func (m *Driver) List() []string {
	ret := m.Called()

	var r0 []string
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]string)
	}

	return r0
}
func (m *Driver) Exists(volumeName string) bool {
	ret := m.Called(volumeName)

	r0 := ret.Get(0).(bool)

	return r0
}
func (m *Driver) Cleanup() error {
	ret := m.Called()

	r0 := ret.Error(0)

	return r0
}
func (m* Driver) Status() (*volume.Status, error) {
	ret := m.Called()

	r0 := ret.Get(0).(volume.Status)
	r1 := ret.Error(0)

	return &r0, r1
}

<commit_msg>Restore boilerplate<commit_after>// Copyright 2015 The Serviced 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 mocks

import "github.com/control-center/serviced/volume"
import "github.com/stretchr/testify/mock"

var DriverName volume.DriverType = "mock"

type Driver struct {
	mock.Mock
}

func (m *Driver) Root() string {
	ret := m.Called()

	r0 := ret.Get(0).(string)

	return r0
}
func (m *Driver) DriverType() volume.DriverType {
	return DriverName
}

func (m *Driver) Create(volumeName string) (volume.Volume, error) {
	ret := m.Called(volumeName)

	r0 := ret.Get(0).(volume.Volume)
	r1 := ret.Error(1)

	return r0, r1
}
func (m *Driver) Remove(volumeName string) error {
	ret := m.Called(volumeName)

	r0 := ret.Error(0)

	return r0
}
func (m *Driver) Get(volumeName string) (volume.Volume, error) {
	ret := m.Called(volumeName)

	r0 := ret.Get(0).(volume.Volume)
	r1 := ret.Error(1)

	return r0, r1
}
func (m *Driver) Release(volumeName string) error {
	ret := m.Called(volumeName)

	r0 := ret.Error(0)

	return r0
}
func (m *Driver) List() []string {
	ret := m.Called()

	var r0 []string
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]string)
	}

	return r0
}
func (m *Driver) Exists(volumeName string) bool {
	ret := m.Called(volumeName)

	r0 := ret.Get(0).(bool)

	return r0
}
func (m *Driver) Cleanup() error {
	ret := m.Called()

	r0 := ret.Error(0)

	return r0
}
func (m *Driver) Status() (*volume.Status, error) {
	ret := m.Called()

	r0 := ret.Get(0).(volume.Status)
	r1 := ret.Error(0)

	return &r0, r1
}
<|endoftext|>


<commit_before>package solver

import (
	"cryptics/utils"
	"strings"
)

const (
	ANA = iota
	SUB
	REV
	INS
	CAT
	ANA_
	SUB_
	INS_
	REV_
	NULL
	LIT
	DEF
	FIRST
	SYN
)

type clue_function func([]string, utils.Phrasing) map[string]bool

var FUNCTIONS = map[int]clue_function{
	ANA: utils.Anagrams,
	SUB: utils.AllLegalSubstrings,
	REV: utils.Reverse,
	INS: utils.AllInsertions}

var HEADS = map[int]bool{ANA_: true, SUB_: true, INS_: true, REV_: true, DEF: true}

type StructuredClue struct {
	Type int
	Head string
	Args []*StructuredClue
	Ans  map[string][]string // each answer to this clue is a key in the map and each value is the slice of sub-answers to each clue in Args that gave that particular answer
}

func (clue *StructuredClue) Solve(phrasing *utils.Phrasing, solved_parts map[string]map[string][]string, map_c chan bool) (err bool) {
	length := utils.Sum((*phrasing).Lengths)
	var sub_answers map[string][]string
	// fmt.Println("Trying to solve:", clue.HashString())
	<-map_c
	ans, ok := solved_parts[clue.HashString()]
	map_c <- true
	if ok {
		clue.Ans = ans
	} else {
		clue.Ans = map[string][]string{}
		var sub_clue *StructuredClue
		var new_args []string
		trans, trans_ok := TRANSFORMS[clue.Type]
		if HEADS[clue.Type] {
			trans = TRANSFORMS[NULL]
			trans_ok = true
		}
		clue_func, func_ok := FUNCTIONS[clue.Type]
		if trans_ok {
			clue.Ans = trans(clue.Head, length)
		} else if func_ok {
			args_set := [][]string{{}}
			new_args_set := [][]string{}
			for _, sub_clue := range clue.Args {
				err = sub_clue.Solve(phrasing, solved_parts, map_c)
				if err {
					<-map_c
					solved_parts[clue.HashString()] = clue.Ans
					map_c <- true
					return true
				}
				new_args_set = [][]string{}
				for _, args := range args_set {
					sub_answers = sub_clue.Ans
					for sub_ans := range sub_answers {
						new_args = append(args, sub_ans)
						new_args_set = append(new_args_set, new_args)
					}
				}
				args_set = new_args_set
			}
			for _, args := range args_set {
				for sub_ans := range clue_func(filter_empty_strings(args), *phrasing) {
					clue.Ans[sub_ans] = args
				}
			}
		} else if clue.Type == CAT {
			active_set := [][]string{{}}
			new_active_set := [][]string{}
			var candidate []string
			for _, sub_clue = range clue.Args {
				new_active_set = [][]string{}
				err = sub_clue.Solve(phrasing, solved_parts, map_c)
				if err {
					<-map_c
					(solved_parts)[clue.HashString()] = clue.Ans
					map_c <- true
					return true
				}
				for _, s := range active_set {
					for w := range sub_clue.Ans {
						candidate = append(s, strings.Replace(w, "_", "", -1))
						if utils.PartialAnswerTest(strings.Join(candidate, ""), phrasing) {
							new_active_set = append(new_active_set, make([]string, len(candidate)))
							copy(new_active_set[len(new_active_set)-1], candidate)
						}
					}
				}
				if len(new_active_set) > 0 {
					active_set = new_active_set
				} else {
					<-map_c
					(solved_parts)[clue.HashString()] = clue.Ans
					map_c <- true
					return true
				}
			}
			for _, s := range active_set {
				clue.Ans[strings.Join(s, "")] = s
			}
		} else {
			panic("Unrecognized clue type")
		}
	}
	<-map_c
	solved_parts[clue.HashString()] = clue.Ans
	map_c <- true
	_, blank_ans := clue.Ans[""]
	// fmt.Println("returning", clue.Ans, "for clue", clue.HashString())
	if (len(clue.Ans) == 0 || (len(clue.Ans) == 1 && blank_ans)) && (clue.Type != NULL && !HEADS[clue.Type]) {
		// fmt.Println("err true")
		return true
	} else {
		// fmt.Println("err false")
		return false
	}
	return false
}

func (c *StructuredClue) HashString() string {
	result := "(" + type_to_str[c.Type] + ", " + c.Head + ", "
	for _, s := range c.Args {
		result += (s).HashString() + ", "
	}
	result += ")"
	return result
}

func (c *StructuredClue) FormatAnswers() []string {
	var results []string
	var result string
	for ans := range c.Ans {
		result = c.print_with_answer(ans)
		results = append(results, result)
	}
	return results
}

func (c *StructuredClue) print_with_answer(answer string) string {
	var result string
	result = "('" + type_to_str[c.Type] + "', "
	if c.Head != "" {
		result += "'" + c.Head + "', "
	}
	parents := c.Ans[answer]
	if len(parents) > 0 {
		for i, sub_clue := range c.Args {
			result += sub_clue.print_with_answer(parents[i]) + ", "
		}
	}
	result += "'" + strings.ToUpper(answer) + "')"
	return result
}

func filter_empty_strings(input []string) []string {
	result := []string{}
	for _, s := range input {
		if s != "" {
			result = append(result, s)
		}
	}
	return result
}
<commit_msg>Combine CAT type handline with other functions<commit_after>package solver

import (
	"cryptics/utils"
	"strings"
)

const (
	ANA = iota
	SUB
	REV
	INS
	CAT
	ANA_
	SUB_
	INS_
	REV_
	NULL
	LIT
	DEF
	FIRST
	SYN
)

type clue_function func([]string, utils.Phrasing) map[string]bool

var FUNCTIONS = map[int]clue_function{
	ANA: utils.Anagrams,
	SUB: utils.AllLegalSubstrings,
	REV: utils.Reverse,
	INS: utils.AllInsertions,
	CAT: func(s []string, p utils.Phrasing) map[string]bool {
		return map[string]bool{strings.Join(s, ""): true}
	}}

var HEADS = map[int]bool{ANA_: true, SUB_: true, INS_: true, REV_: true, DEF: true}

type StructuredClue struct {
	Type int
	Head string
	Args []*StructuredClue
	Ans  map[string][]string // each answer to this clue is a key in the map and each value is the slice of sub-answers to each clue in Args that gave that particular answer
}

func (clue *StructuredClue) Solve(phrasing *utils.Phrasing, solved_parts map[string]map[string][]string, map_c chan bool) (err bool) {
	length := utils.Sum((*phrasing).Lengths)
	// fmt.Println("Trying to solve:", clue.HashString())
	<-map_c
	ans, ok := solved_parts[clue.HashString()]
	map_c <- true
	if ok {
		clue.Ans = ans
	} else {
		clue.Ans = map[string][]string{}
		var sub_clue *StructuredClue
		var new_args []string
		trans, trans_ok := TRANSFORMS[clue.Type]
		if HEADS[clue.Type] {
			trans = TRANSFORMS[NULL]
			trans_ok = true
		}
		clue_func, func_ok := FUNCTIONS[clue.Type]
		if trans_ok {
			clue.Ans = trans(clue.Head, length)
		} else if func_ok {
			args_set := [][]string{{}}
			new_args_set := [][]string{}
			var candidate []string
			for _, sub_clue = range clue.Args {
				err = sub_clue.Solve(phrasing, solved_parts, map_c)
				if err {
					<-map_c
					solved_parts[clue.HashString()] = clue.Ans
					map_c <- true
					return true
				}
				new_args_set = [][]string{}
				if clue.Type == CAT {
					for _, s := range args_set {
						for w := range sub_clue.Ans {
							candidate = append(s, strings.Replace(w, "_", "", -1))
							if utils.PartialAnswerTest(strings.Join(candidate, ""), phrasing) {
								new_args_set = append(new_args_set, make([]string, len(candidate)))
								copy(new_args_set[len(new_args_set)-1], candidate)
							}
						}
					}
				} else {
					for _, args := range args_set {
						for sub_ans := range sub_clue.Ans {
							new_args = append(args, sub_ans)
							new_args_set = append(new_args_set, new_args)
						}
					}
				}
				if len(new_args_set) > 0 {
					args_set = new_args_set
				} else {
					<-map_c
					(solved_parts)[clue.HashString()] = clue.Ans
					map_c <- true
					return true
				}

			}
			for _, args := range args_set {
				for sub_ans := range clue_func(filter_empty_strings(args), *phrasing) {
					clue.Ans[sub_ans] = args
				}
			}
		} else {
			panic("Unrecognized clue type")
		}
	}
	<-map_c
	solved_parts[clue.HashString()] = clue.Ans
	map_c <- true
	_, blank_ans := clue.Ans[""]
	// fmt.Println("returning", clue.Ans, "for clue", clue.HashString())
	if (len(clue.Ans) == 0 || (len(clue.Ans) == 1 && blank_ans)) && (clue.Type != NULL && !HEADS[clue.Type]) {
		// fmt.Println("err true")
		return true
	} else {
		// fmt.Println("err false")
		return false
	}
	return false
}

func (c *StructuredClue) HashString() string {
	result := "(" + type_to_str[c.Type] + ", " + c.Head + ", "
	for _, s := range c.Args {
		result += (s).HashString() + ", "
	}
	result += ")"
	return result
}

func (c *StructuredClue) FormatAnswers() []string {
	var results []string
	var result string
	for ans := range c.Ans {
		result = c.print_with_answer(ans)
		results = append(results, result)
	}
	return results
}

func (c *StructuredClue) print_with_answer(answer string) string {
	var result string
	result = "('" + type_to_str[c.Type] + "', "
	if c.Head != "" {
		result += "'" + c.Head + "', "
	}
	parents := c.Ans[answer]
	if len(parents) > 0 {
		for i, sub_clue := range c.Args {
			result += sub_clue.print_with_answer(parents[i]) + ", "
		}
	}
	result += "'" + strings.ToUpper(answer) + "')"
	return result
}

func filter_empty_strings(input []string) []string {
	result := []string{}
	for _, s := range input {
		if s != "" {
			result = append(result, s)
		}
	}
	return result
}
<|endoftext|>


<commit_before>package golangNeo4jBoltDriver

import (
	"testing"
	"github.com/johnnadratowski/golang-neo4j-bolt-driver/structures/graph"
)

func TestBoltTx_Commit(t *testing.T) {
	driver := NewDriver()
	conn, err := driver.OpenNeo(neo4jConnStr)
	if err != nil {
		t.Fatalf("An error occurred opening conn: %s", err)
	}

	tx, err := conn.Begin()
	if err != nil {
		t.Fatalf("An error occurred beginning transaction: %s", err)
	}

	stmt, err := conn.PrepareNeo(`CREATE (f:FOO {a: "1"})-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) RETURN f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing statement: %s", err)
	}

	result, err := stmt.ExecNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred querying Neo: %s", err)
	}

	if num, err := result.RowsAffected(); num != 5 {
		t.Fatalf("Expected 5 rows affected: %#v err: %#v", result.Metadata(), err)
	}

	err = tx.Commit()
	if err != nil {
		t.Fatalf("An error occurred committing transaction: %s", err)
	}

	err = stmt.Close()
	if err != nil {
		t.Fatalf("An error occurred closing statement")
	}

	stmt, err = conn.PrepareNeo(`MATCH (f:FOO {a: "1"})-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) RETURN f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing statement: %s", err)
	}

	rows, err := stmt.QueryNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred querying Neo: %s", err)
	}

	output, _, err := rows.NextNeo()
	if err != nil {
		t.Fatalf("An error occurred getting next row: %s", err)
	}

	if output[0].(graph.Node).Labels[0] != "FOO" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[1].(graph.Relationship).Type != "TO" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[2].(graph.Node).Labels[0] != "BAR" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[3].(graph.Relationship).Type != "FROM" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[4].(graph.Node).Labels[0] != "BAZ" {
		t.Fatalf("Unexpected return data: %s", err)
	}

	// Closing in middle of record stream
	stmt.Close()

	stmt, err = conn.PrepareNeo(`MATCH (f:FOO)-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) DELETE f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing delete statement: %s", err)
	}

	_, err = stmt.ExecNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred on delete query to Neo: %s", err)
	}

	err = conn.Close()
	if err != nil {
		t.Fatalf("Error closing connection: %s", err)
	}
}
<commit_msg>Added rollback test<commit_after>package golangNeo4jBoltDriver

import (
	"testing"
	"github.com/johnnadratowski/golang-neo4j-bolt-driver/structures/graph"
	"io"
)

func TestBoltTx_Commit(t *testing.T) {
	driver := NewDriver()
	conn, err := driver.OpenNeo(neo4jConnStr)
	if err != nil {
		t.Fatalf("An error occurred opening conn: %s", err)
	}

	tx, err := conn.Begin()
	if err != nil {
		t.Fatalf("An error occurred beginning transaction: %s", err)
	}

	stmt, err := conn.PrepareNeo(`CREATE (f:FOO {a: "1"})-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) RETURN f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing statement: %s", err)
	}

	result, err := stmt.ExecNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred querying Neo: %s", err)
	}

	if num, err := result.RowsAffected(); num != 5 {
		t.Fatalf("Expected 5 rows affected: %#v err: %#v", result.Metadata(), err)
	}

	err = tx.Commit()
	if err != nil {
		t.Fatalf("An error occurred committing transaction: %s", err)
	}

	err = stmt.Close()
	if err != nil {
		t.Fatalf("An error occurred closing statement")
	}

	stmt, err = conn.PrepareNeo(`MATCH (f:FOO {a: "1"})-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) RETURN f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing statement: %s", err)
	}

	rows, err := stmt.QueryNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred querying Neo: %s", err)
	}

	output, _, err := rows.NextNeo()
	if err != nil {
		t.Fatalf("An error occurred getting next row: %s", err)
	}

	if output[0].(graph.Node).Labels[0] != "FOO" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[1].(graph.Relationship).Type != "TO" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[2].(graph.Node).Labels[0] != "BAR" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[3].(graph.Relationship).Type != "FROM" {
		t.Fatalf("Unexpected return data: %s", err)
	}
	if output[4].(graph.Node).Labels[0] != "BAZ" {
		t.Fatalf("Unexpected return data: %s", err)
	}

	// Closing in middle of record stream
	stmt.Close()

	stmt, err = conn.PrepareNeo(`MATCH (f:FOO)-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) DELETE f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing delete statement: %s", err)
	}

	_, err = stmt.ExecNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred on delete query to Neo: %s", err)
	}

	err = conn.Close()
	if err != nil {
		t.Fatalf("Error closing connection: %s", err)
	}
}

func TestBoltTx_Rollback(t *testing.T) {
	driver := NewDriver()
	conn, err := driver.OpenNeo(neo4jConnStr)
	if err != nil {
		t.Fatalf("An error occurred opening conn: %s", err)
	}

	tx, err := conn.Begin()
	if err != nil {
		t.Fatalf("An error occurred beginning transaction: %s", err)
	}

	stmt, err := conn.PrepareNeo(`CREATE (f:FOO {a: "1"})-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) RETURN f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing statement: %s", err)
	}

	result, err := stmt.ExecNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred querying Neo: %s", err)
	}

	if num, err := result.RowsAffected(); num != 5 {
		t.Fatalf("Expected 5 rows affected: %#v err: %#v", result.Metadata(), err)
	}

	err = tx.Rollback()
	if err != nil {
		t.Fatalf("An error occurred committing transaction: %s", err)
	}

	err = stmt.Close()
	if err != nil {
		t.Fatalf("An error occurred closing statement")
	}

	stmt, err = conn.PrepareNeo(`MATCH (f:FOO {a: "1"})-[b:TO]->(c:BAR)<-[d:FROM]-(e:BAZ) RETURN f, b, c, d, e`)
	if err != nil {
		t.Fatalf("An error occurred preparing statement: %s", err)
	}

	rows, err := stmt.QueryNeo(nil)
	if err != nil {
		t.Fatalf("An error occurred querying Neo: %s", err)
	}

	output, _, err := rows.NextNeo()
	if err != io.EOF {
		t.Fatalf("Unexpected error returned from getting next rows: %s", err)
	}

	if len(output) != 0 {
		t.Fatalf("Unexpected return data: %s", err)
	}

	err = stmt.Close()
	if err != nil {
		t.Fatalf("An error occurred closing statement")
	}

	err = conn.Close()
	if err != nil {
		t.Fatalf("Error closing connection: %s", err)
	}
}
<|endoftext|>


<commit_before>// The code in this file is a heavily modified version of
// https://github.com/tmc/grpc-websocket-proxy/

package lnrpc

import (
	"bufio"
	"io"
	"net/http"
	"net/textproto"
	"strings"

	"github.com/btcsuite/btclog"
	"github.com/gorilla/websocket"
	"golang.org/x/net/context"
)

const (
	// MethodOverrideParam is the GET query parameter that specifies what
	// HTTP request method should be used for the forwarded REST request.
	// This is necessary because the WebSocket API specifies that a
	// handshake request must always be done through a GET request.
	MethodOverrideParam = "method"
)

var (
	// defaultHeadersToForward is a map of all HTTP header fields that are
	// forwarded by default. The keys must be in the canonical MIME header
	// format.
	defaultHeadersToForward = map[string]bool{
		"Origin":                 true,
		"Referer":                true,
		"Grpc-Metadata-Macaroon": true,
	}
)

// NewWebSocketProxy attempts to expose the underlying handler as a response-
// streaming WebSocket stream with newline-delimited JSON as the content
// encoding.
func NewWebSocketProxy(h http.Handler, logger btclog.Logger) http.Handler {
	p := &WebsocketProxy{
		backend: h,
		logger:  logger,
		upgrader: &websocket.Upgrader{
			ReadBufferSize:  1024,
			WriteBufferSize: 1024,
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
		},
	}
	return p
}

// WebsocketProxy provides websocket transport upgrade to compatible endpoints.
type WebsocketProxy struct {
	backend  http.Handler
	logger   btclog.Logger
	upgrader *websocket.Upgrader
}

// ServeHTTP handles the incoming HTTP request. If the request is an
// "upgradeable" WebSocket request (identified by header fields), then the
// WS proxy handles the request. Otherwise the request is passed directly to the
// underlying REST proxy.
func (p *WebsocketProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !websocket.IsWebSocketUpgrade(r) {
		p.backend.ServeHTTP(w, r)
		return
	}
	p.upgradeToWebSocketProxy(w, r)
}

// upgradeToWebSocketProxy upgrades the incoming request to a WebSocket, reads
// one incoming message then streams all responses until either the client or
// server quit the connection.
func (p *WebsocketProxy) upgradeToWebSocketProxy(w http.ResponseWriter,
	r *http.Request) {

	conn, err := p.upgrader.Upgrade(w, r, nil)
	if err != nil {
		p.logger.Errorf("error upgrading websocket:", err)
		return
	}
	defer func() {
		err := conn.Close()
		if err != nil && !IsClosedConnError(err) {
			p.logger.Errorf("WS: error closing upgraded conn: %v",
				err)
		}
	}()

	ctx, cancelFn := context.WithCancel(context.Background())
	defer cancelFn()

	requestForwarder := newRequestForwardingReader()
	request, err := http.NewRequestWithContext(
		r.Context(), r.Method, r.URL.String(), requestForwarder,
	)
	if err != nil {
		p.logger.Errorf("WS: error preparing request:", err)
		return
	}
	for header := range r.Header {
		headerName := textproto.CanonicalMIMEHeaderKey(header)
		forward, ok := defaultHeadersToForward[headerName]
		if ok && forward {
			request.Header.Set(headerName, r.Header.Get(header))
		}
	}
	if m := r.URL.Query().Get(MethodOverrideParam); m != "" {
		request.Method = m
	}

	responseForwarder := newResponseForwardingWriter()
	go func() {
		<-ctx.Done()
		responseForwarder.Close()
	}()

	go func() {
		defer cancelFn()
		p.backend.ServeHTTP(responseForwarder, request)
	}()

	// Read loop: Take messages from websocket and write to http request.
	go func() {
		defer cancelFn()
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}

			_, payload, err := conn.ReadMessage()
			if err != nil {
				if IsClosedConnError(err) {
					p.logger.Tracef("WS: socket "+
						"closed: %v", err)
					return
				}
				p.logger.Errorf("error reading message: %v",
					err)
				return
			}
			_, err = requestForwarder.Write(payload)
			if err != nil {
				p.logger.Errorf("WS: error writing message "+
					"to upstream http server: %v", err)
				return
			}
			_, _ = requestForwarder.Write([]byte{'\n'})

			// We currently only support server-streaming messages.
			// Therefore we close the request body after the first
			// incoming message to trigger a response.
			requestForwarder.CloseWriter()
		}
	}()

	// Write loop: Take messages from the response forwarder and write them
	// to the WebSocket.
	for responseForwarder.Scan() {
		if len(responseForwarder.Bytes()) == 0 {
			p.logger.Errorf("WS: empty scan: %v",
				responseForwarder.Err())

			continue
		}

		err = conn.WriteMessage(
			websocket.TextMessage, responseForwarder.Bytes(),
		)
		if err != nil {
			p.logger.Errorf("WS: error writing message: %v", err)
			return
		}
	}
	if err := responseForwarder.Err(); err != nil && !IsClosedConnError(err) {
		p.logger.Errorf("WS: scanner err: %v", err)
	}
}

// newRequestForwardingReader creates a new request forwarding pipe.
func newRequestForwardingReader() *requestForwardingReader {
	r, w := io.Pipe()
	return &requestForwardingReader{
		Reader: r,
		Writer: w,
		pipeR:  r,
		pipeW:  w,
	}
}

// requestForwardingReader is a wrapper around io.Pipe that embeds both the
// io.Reader and io.Writer interface and can be closed.
type requestForwardingReader struct {
	io.Reader
	io.Writer

	pipeR *io.PipeReader
	pipeW *io.PipeWriter
}

// CloseWriter closes the underlying pipe writer.
func (r *requestForwardingReader) CloseWriter() {
	_ = r.pipeW.CloseWithError(io.EOF)
}

// newResponseForwardingWriter creates a new http.ResponseWriter that intercepts
// what's written to it and presents it through a bufio.Scanner interface.
func newResponseForwardingWriter() *responseForwardingWriter {
	r, w := io.Pipe()
	return &responseForwardingWriter{
		Writer:  w,
		Scanner: bufio.NewScanner(r),
		pipeR:   r,
		pipeW:   w,
		header:  http.Header{},
		closed:  make(chan bool, 1),
	}
}

// responseForwardingWriter is a type that implements the http.ResponseWriter
// interface but internally forwards what's written to the writer through a pipe
// so it can easily be read again through the bufio.Scanner interface.
type responseForwardingWriter struct {
	io.Writer
	*bufio.Scanner

	pipeR *io.PipeReader
	pipeW *io.PipeWriter

	header http.Header
	code   int
	closed chan bool
}

// Write writes the given bytes to the internal pipe.
//
// NOTE: This is part of the http.ResponseWriter interface.
func (w *responseForwardingWriter) Write(b []byte) (int, error) {
	return w.Writer.Write(b)
}

// Header returns the HTTP header fields intercepted so far.
//
// NOTE: This is part of the http.ResponseWriter interface.
func (w *responseForwardingWriter) Header() http.Header {
	return w.header
}

// WriteHeader indicates that the header part of the response is now finished
// and sets the response code.
//
// NOTE: This is part of the http.ResponseWriter interface.
func (w *responseForwardingWriter) WriteHeader(code int) {
	w.code = code
}

// CloseNotify returns a channel that indicates if a connection was closed.
//
// NOTE: This is part of the http.CloseNotifier interface.
func (w *responseForwardingWriter) CloseNotify() <-chan bool {
	return w.closed
}

// Flush empties all buffers. We implement this to indicate to our backend that
// we support flushing our content. There is no actual implementation because
// all writes happen immediately, there is no internal buffering.
//
// NOTE: This is part of the http.Flusher interface.
func (w *responseForwardingWriter) Flush() {}

func (w *responseForwardingWriter) Close() {
	_ = w.pipeR.CloseWithError(io.EOF)
	_ = w.pipeW.CloseWithError(io.EOF)
	w.closed <- true
}

// IsClosedConnError is a helper function that returns true if the given error
// is an error indicating we are using a closed connection.
func IsClosedConnError(err error) bool {
	if err == nil {
		return false
	}
	if err == http.ErrServerClosed {
		return true
	}

	str := err.Error()
	if strings.Contains(str, "use of closed network connection") {
		return true
	}
	if strings.Contains(str, "closed pipe") {
		return true
	}
	if strings.Contains(str, "broken pipe") {
		return true
	}
	return websocket.IsCloseError(
		err, websocket.CloseNormalClosure, websocket.CloseGoingAway,
	)
}
<commit_msg>lnrpc: add macaroon workaround for WebSockets in browsers<commit_after>// The code in this file is a heavily modified version of
// https://github.com/tmc/grpc-websocket-proxy/

package lnrpc

import (
	"bufio"
	"io"
	"net/http"
	"net/textproto"
	"strings"

	"github.com/btcsuite/btclog"
	"github.com/gorilla/websocket"
	"golang.org/x/net/context"
)

const (
	// MethodOverrideParam is the GET query parameter that specifies what
	// HTTP request method should be used for the forwarded REST request.
	// This is necessary because the WebSocket API specifies that a
	// handshake request must always be done through a GET request.
	MethodOverrideParam = "method"

	// HeaderWebSocketProtocol is the name of the WebSocket protocol
	// exchange header field that we use to transport additional header
	// fields.
	HeaderWebSocketProtocol = "Sec-Websocket-Protocol"

	// WebSocketProtocolDelimiter is the delimiter we use between the
	// additional header field and its value. We use the plus symbol because
	// the default delimiters aren't allowed in the protocol names.
	WebSocketProtocolDelimiter = "+"
)

var (
	// defaultHeadersToForward is a map of all HTTP header fields that are
	// forwarded by default. The keys must be in the canonical MIME header
	// format.
	defaultHeadersToForward = map[string]bool{
		"Origin":                 true,
		"Referer":                true,
		"Grpc-Metadata-Macaroon": true,
	}

	// defaultProtocolsToAllow are additional header fields that we allow
	// to be transported inside of the Sec-Websocket-Protocol field to be
	// forwarded to the backend.
	defaultProtocolsToAllow = map[string]bool{
		"Grpc-Metadata-Macaroon": true,
	}
)

// NewWebSocketProxy attempts to expose the underlying handler as a response-
// streaming WebSocket stream with newline-delimited JSON as the content
// encoding.
func NewWebSocketProxy(h http.Handler, logger btclog.Logger) http.Handler {
	p := &WebsocketProxy{
		backend: h,
		logger:  logger,
		upgrader: &websocket.Upgrader{
			ReadBufferSize:  1024,
			WriteBufferSize: 1024,
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
		},
	}
	return p
}

// WebsocketProxy provides websocket transport upgrade to compatible endpoints.
type WebsocketProxy struct {
	backend  http.Handler
	logger   btclog.Logger
	upgrader *websocket.Upgrader
}

// ServeHTTP handles the incoming HTTP request. If the request is an
// "upgradeable" WebSocket request (identified by header fields), then the
// WS proxy handles the request. Otherwise the request is passed directly to the
// underlying REST proxy.
func (p *WebsocketProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !websocket.IsWebSocketUpgrade(r) {
		p.backend.ServeHTTP(w, r)
		return
	}
	p.upgradeToWebSocketProxy(w, r)
}

// upgradeToWebSocketProxy upgrades the incoming request to a WebSocket, reads
// one incoming message then streams all responses until either the client or
// server quit the connection.
func (p *WebsocketProxy) upgradeToWebSocketProxy(w http.ResponseWriter,
	r *http.Request) {

	conn, err := p.upgrader.Upgrade(w, r, nil)
	if err != nil {
		p.logger.Errorf("error upgrading websocket:", err)
		return
	}
	defer func() {
		err := conn.Close()
		if err != nil && !IsClosedConnError(err) {
			p.logger.Errorf("WS: error closing upgraded conn: %v",
				err)
		}
	}()

	ctx, cancelFn := context.WithCancel(context.Background())
	defer cancelFn()

	requestForwarder := newRequestForwardingReader()
	request, err := http.NewRequestWithContext(
		r.Context(), r.Method, r.URL.String(), requestForwarder,
	)
	if err != nil {
		p.logger.Errorf("WS: error preparing request:", err)
		return
	}

	// Allow certain headers to be forwarded, either from source headers
	// or the special Sec-Websocket-Protocol header field.
	forwardHeaders(r.Header, request.Header)

	// Also allow the target request method to be overwritten, as all
	// WebSocket establishment calls MUST be GET requests.
	if m := r.URL.Query().Get(MethodOverrideParam); m != "" {
		request.Method = m
	}

	responseForwarder := newResponseForwardingWriter()
	go func() {
		<-ctx.Done()
		responseForwarder.Close()
	}()

	go func() {
		defer cancelFn()
		p.backend.ServeHTTP(responseForwarder, request)
	}()

	// Read loop: Take messages from websocket and write to http request.
	go func() {
		defer cancelFn()
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}

			_, payload, err := conn.ReadMessage()
			if err != nil {
				if IsClosedConnError(err) {
					p.logger.Tracef("WS: socket "+
						"closed: %v", err)
					return
				}
				p.logger.Errorf("error reading message: %v",
					err)
				return
			}
			_, err = requestForwarder.Write(payload)
			if err != nil {
				p.logger.Errorf("WS: error writing message "+
					"to upstream http server: %v", err)
				return
			}
			_, _ = requestForwarder.Write([]byte{'\n'})

			// We currently only support server-streaming messages.
			// Therefore we close the request body after the first
			// incoming message to trigger a response.
			requestForwarder.CloseWriter()
		}
	}()

	// Write loop: Take messages from the response forwarder and write them
	// to the WebSocket.
	for responseForwarder.Scan() {
		if len(responseForwarder.Bytes()) == 0 {
			p.logger.Errorf("WS: empty scan: %v",
				responseForwarder.Err())

			continue
		}

		err = conn.WriteMessage(
			websocket.TextMessage, responseForwarder.Bytes(),
		)
		if err != nil {
			p.logger.Errorf("WS: error writing message: %v", err)
			return
		}
	}
	if err := responseForwarder.Err(); err != nil && !IsClosedConnError(err) {
		p.logger.Errorf("WS: scanner err: %v", err)
	}
}

// forwardHeaders forwards certain allowed header fields from the source request
// to the target request. Because browsers are limited in what header fields
// they can send on the WebSocket setup call, we also allow additional fields to
// be transported in the special Sec-Websocket-Protocol field.
func forwardHeaders(source, target http.Header) {
	// Forward allowed header fields directly.
	for header := range source {
		headerName := textproto.CanonicalMIMEHeaderKey(header)
		forward, ok := defaultHeadersToForward[headerName]
		if ok && forward {
			target.Set(headerName, source.Get(header))
		}
	}

	// Browser aren't allowed to set custom header fields on WebSocket
	// requests. We need to allow them to submit the macaroon as a WS
	// protocol, which is the only allowed header. Set any "protocols" we
	// declare valid as header fields on the forwarded request.
	protocol := source.Get(HeaderWebSocketProtocol)
	for key := range defaultProtocolsToAllow {
		if strings.HasPrefix(protocol, key) {
			// The format is "<protocol name>+<value>". We know the
			// protocol string starts with the name so we only need
			// to set the value.
			values := strings.Split(
				protocol, WebSocketProtocolDelimiter,
			)
			target.Set(key, values[1])
		}
	}
}

// newRequestForwardingReader creates a new request forwarding pipe.
func newRequestForwardingReader() *requestForwardingReader {
	r, w := io.Pipe()
	return &requestForwardingReader{
		Reader: r,
		Writer: w,
		pipeR:  r,
		pipeW:  w,
	}
}

// requestForwardingReader is a wrapper around io.Pipe that embeds both the
// io.Reader and io.Writer interface and can be closed.
type requestForwardingReader struct {
	io.Reader
	io.Writer

	pipeR *io.PipeReader
	pipeW *io.PipeWriter
}

// CloseWriter closes the underlying pipe writer.
func (r *requestForwardingReader) CloseWriter() {
	_ = r.pipeW.CloseWithError(io.EOF)
}

// newResponseForwardingWriter creates a new http.ResponseWriter that intercepts
// what's written to it and presents it through a bufio.Scanner interface.
func newResponseForwardingWriter() *responseForwardingWriter {
	r, w := io.Pipe()
	return &responseForwardingWriter{
		Writer:  w,
		Scanner: bufio.NewScanner(r),
		pipeR:   r,
		pipeW:   w,
		header:  http.Header{},
		closed:  make(chan bool, 1),
	}
}

// responseForwardingWriter is a type that implements the http.ResponseWriter
// interface but internally forwards what's written to the writer through a pipe
// so it can easily be read again through the bufio.Scanner interface.
type responseForwardingWriter struct {
	io.Writer
	*bufio.Scanner

	pipeR *io.PipeReader
	pipeW *io.PipeWriter

	header http.Header
	code   int
	closed chan bool
}

// Write writes the given bytes to the internal pipe.
//
// NOTE: This is part of the http.ResponseWriter interface.
func (w *responseForwardingWriter) Write(b []byte) (int, error) {
	return w.Writer.Write(b)
}

// Header returns the HTTP header fields intercepted so far.
//
// NOTE: This is part of the http.ResponseWriter interface.
func (w *responseForwardingWriter) Header() http.Header {
	return w.header
}

// WriteHeader indicates that the header part of the response is now finished
// and sets the response code.
//
// NOTE: This is part of the http.ResponseWriter interface.
func (w *responseForwardingWriter) WriteHeader(code int) {
	w.code = code
}

// CloseNotify returns a channel that indicates if a connection was closed.
//
// NOTE: This is part of the http.CloseNotifier interface.
func (w *responseForwardingWriter) CloseNotify() <-chan bool {
	return w.closed
}

// Flush empties all buffers. We implement this to indicate to our backend that
// we support flushing our content. There is no actual implementation because
// all writes happen immediately, there is no internal buffering.
//
// NOTE: This is part of the http.Flusher interface.
func (w *responseForwardingWriter) Flush() {}

func (w *responseForwardingWriter) Close() {
	_ = w.pipeR.CloseWithError(io.EOF)
	_ = w.pipeW.CloseWithError(io.EOF)
	w.closed <- true
}

// IsClosedConnError is a helper function that returns true if the given error
// is an error indicating we are using a closed connection.
func IsClosedConnError(err error) bool {
	if err == nil {
		return false
	}
	if err == http.ErrServerClosed {
		return true
	}

	str := err.Error()
	if strings.Contains(str, "use of closed network connection") {
		return true
	}
	if strings.Contains(str, "closed pipe") {
		return true
	}
	if strings.Contains(str, "broken pipe") {
		return true
	}
	return websocket.IsCloseError(
		err, websocket.CloseNormalClosure, websocket.CloseGoingAway,
	)
}
<|endoftext|>


<commit_before>package mastodon

import (
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// Account hold information for mastodon account.
type Account struct {
	ID             int64     `json:"id"`
	Username       string    `json:"username"`
	Acct           string    `json:"acct"`
	DisplayName    string    `json:"display_name"`
	Locked         bool      `json:"locked"`
	CreatedAt      time.Time `json:"created_at"`
	FollowersCount int64     `json:"followers_count"`
	FollowingCount int64     `json:"following_count"`
	StatusesCount  int64     `json:"statuses_count"`
	Note           string    `json:"note"`
	URL            string    `json:"url"`
	Avatar         string    `json:"avatar"`
	AvatarStatic   string    `json:"avatar_static"`
	Header         string    `json:"header"`
	HeaderStatic   string    `json:"header_static"`
}

// GetAccount return Account.
func (c *Client) GetAccount(id int) (*Account, error) {
	var account Account
	err := c.doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d", id), nil, &account)
	if err != nil {
		return nil, err
	}
	return &account, nil
}

// GetAccountCurrentUser return Account of current user.
func (c *Client) GetAccountCurrentUser() (*Account, error) {
	var account Account
	err := c.doAPI(http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account)
	if err != nil {
		return nil, err
	}
	return &account, nil
}

// GetAccountFollowers return followers list.
func (c *Client) GetAccountFollowers(id int64) ([]*Account, error) {
	var accounts []*Account
	err := c.doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), nil, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// GetAccountFollowing return following list.
func (c *Client) GetAccountFollowing(id int64) ([]*Account, error) {
	var accounts []*Account
	err := c.doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// Relationship hold information for relation-ship to the account.
type Relationship struct {
	ID         int64 `json:"id"`
	Following  bool  `json:"following"`
	FollowedBy bool  `json:"followed_by"`
	Blocking   bool  `json:"blocking"`
	Muting     bool  `json:"muting"`
	Requested  bool  `json:"requested"`
}

// AccountFollow follow the account.
func (c *Client) AccountFollow(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/follow", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountUnfollow unfollow the account.
func (c *Client) AccountUnfollow(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unfollow", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountBlock block the account.
func (c *Client) AccountBlock(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/block", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountUnblock unblock the account.
func (c *Client) AccountUnblock(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unblock", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountMute mute the account.
func (c *Client) AccountMute(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/mute", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountUnmute unmute the account.
func (c *Client) AccountUnmute(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unmute", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// GetAccountRelationship return relationship for the account.
func (c *Client) GetAccountRelationship(id int64) ([]*Relationship, error) {
	params := url.Values{}
	params.Set("id", fmt.Sprint(id))

	var relationships []*Relationship
	err := c.doAPI(http.MethodGet, "/api/v1/accounts/relationship", params, &relationships)
	if err != nil {
		return nil, err
	}
	return relationships, nil
}

// AccountsSearch search accounts by query.
func (c *Client) AccountsSearch(q string, limit int64) ([]*Account, error) {
	params := url.Values{}
	params.Set("q", q)
	params.Set("limit", fmt.Sprint(limit))

	var accounts []*Account
	err := c.doAPI(http.MethodGet, "/api/v1/accounts/search", params, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// Follow send follow-request.
func (c *Client) FollowRemoteUser(uri string) (*Account, error) {
	params := url.Values{}
	params.Set("uri", uri)

	var account Account
	err := c.doAPI(http.MethodPost, "/api/v1/follows", params, &account)
	if err != nil {
		return nil, err
	}
	return &account, nil
}

// GetFollowRequests return follow-requests.
func (c *Client) GetFollowRequests(uri string) ([]*Account, error) {
	params := url.Values{}
	params.Set("uri", uri)

	var accounts []*Account
	err := c.doAPI(http.MethodGet, "/api/v1/follow_requests", params, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}
<commit_msg>Add GetBlocks<commit_after>package mastodon

import (
	"fmt"
	"net/http"
	"net/url"
	"time"
)

// Account hold information for mastodon account.
type Account struct {
	ID             int64     `json:"id"`
	Username       string    `json:"username"`
	Acct           string    `json:"acct"`
	DisplayName    string    `json:"display_name"`
	Locked         bool      `json:"locked"`
	CreatedAt      time.Time `json:"created_at"`
	FollowersCount int64     `json:"followers_count"`
	FollowingCount int64     `json:"following_count"`
	StatusesCount  int64     `json:"statuses_count"`
	Note           string    `json:"note"`
	URL            string    `json:"url"`
	Avatar         string    `json:"avatar"`
	AvatarStatic   string    `json:"avatar_static"`
	Header         string    `json:"header"`
	HeaderStatic   string    `json:"header_static"`
}

// GetAccount return Account.
func (c *Client) GetAccount(id int) (*Account, error) {
	var account Account
	err := c.doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d", id), nil, &account)
	if err != nil {
		return nil, err
	}
	return &account, nil
}

// GetAccountCurrentUser return Account of current user.
func (c *Client) GetAccountCurrentUser() (*Account, error) {
	var account Account
	err := c.doAPI(http.MethodGet, "/api/v1/accounts/verify_credentials", nil, &account)
	if err != nil {
		return nil, err
	}
	return &account, nil
}

// GetAccountFollowers return followers list.
func (c *Client) GetAccountFollowers(id int64) ([]*Account, error) {
	var accounts []*Account
	err := c.doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/followers", id), nil, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// GetAccountFollowing return following list.
func (c *Client) GetAccountFollowing(id int64) ([]*Account, error) {
	var accounts []*Account
	err := c.doAPI(http.MethodGet, fmt.Sprintf("/api/v1/accounts/%d/following", id), nil, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// GetBlocks return block list.
func (c *Client) GetBlocks() ([]*Account, error) {
	var accounts []*Account
	err := c.doAPI(http.MethodGet, "/api/v1/blocks", nil, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// Relationship hold information for relation-ship to the account.
type Relationship struct {
	ID         int64 `json:"id"`
	Following  bool  `json:"following"`
	FollowedBy bool  `json:"followed_by"`
	Blocking   bool  `json:"blocking"`
	Muting     bool  `json:"muting"`
	Requested  bool  `json:"requested"`
}

// AccountFollow follow the account.
func (c *Client) AccountFollow(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/follow", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountUnfollow unfollow the account.
func (c *Client) AccountUnfollow(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unfollow", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountBlock block the account.
func (c *Client) AccountBlock(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/block", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountUnblock unblock the account.
func (c *Client) AccountUnblock(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unblock", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountMute mute the account.
func (c *Client) AccountMute(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/mute", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// AccountUnmute unmute the account.
func (c *Client) AccountUnmute(id int64) (*Relationship, error) {
	var relationship Relationship
	err := c.doAPI(http.MethodPost, fmt.Sprintf("/api/v1/accounts/%d/unmute", id), nil, &relationship)
	if err != nil {
		return nil, err
	}
	return &relationship, nil
}

// GetAccountRelationship return relationship for the account.
func (c *Client) GetAccountRelationship(id int64) ([]*Relationship, error) {
	params := url.Values{}
	params.Set("id", fmt.Sprint(id))

	var relationships []*Relationship
	err := c.doAPI(http.MethodGet, "/api/v1/accounts/relationship", params, &relationships)
	if err != nil {
		return nil, err
	}
	return relationships, nil
}

// AccountsSearch search accounts by query.
func (c *Client) AccountsSearch(q string, limit int64) ([]*Account, error) {
	params := url.Values{}
	params.Set("q", q)
	params.Set("limit", fmt.Sprint(limit))

	var accounts []*Account
	err := c.doAPI(http.MethodGet, "/api/v1/accounts/search", params, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}

// Follow send follow-request.
func (c *Client) FollowRemoteUser(uri string) (*Account, error) {
	params := url.Values{}
	params.Set("uri", uri)

	var account Account
	err := c.doAPI(http.MethodPost, "/api/v1/follows", params, &account)
	if err != nil {
		return nil, err
	}
	return &account, nil
}

// GetFollowRequests return follow-requests.
func (c *Client) GetFollowRequests(uri string) ([]*Account, error) {
	params := url.Values{}
	params.Set("uri", uri)

	var accounts []*Account
	err := c.doAPI(http.MethodGet, "/api/v1/follow_requests", params, &accounts)
	if err != nil {
		return nil, err
	}
	return accounts, nil
}
<|endoftext|>


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

package orchestrator

import (
	"allmark.io/modules/common/route"
	"allmark.io/modules/web/view/viewmodel"
)

type UpdateOrchestrator struct {
	*Orchestrator

	viewModelOrchestrator *ViewModelOrchestrator
}

func (orchestrator *UpdateOrchestrator) StartWatching(route route.Route) {
	orchestrator.repository.StartWatching(route)
}

func (orchestrator *UpdateOrchestrator) StopWatching(route route.Route) {
	orchestrator.repository.StopWatching(route)
}

func (orchestrator *UpdateOrchestrator) GetUpdatedModel(itemRoute route.Route) (viewModel viewmodel.Model, found bool) {
	model, found := orchestrator.viewModelOrchestrator.GetViewModel(itemRoute)
	if !found {
		return viewmodel.Model{}, false
	}

	return model, true
}
<commit_msg>Fixed the snippet update issue<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package orchestrator

import (
	"allmark.io/modules/common/route"
	"allmark.io/modules/web/view/viewmodel"
)

type UpdateOrchestrator struct {
	*Orchestrator

	viewModelOrchestrator *ViewModelOrchestrator
}

func (orchestrator *UpdateOrchestrator) StartWatching(route route.Route) {
	orchestrator.repository.StartWatching(route)
}

func (orchestrator *UpdateOrchestrator) StopWatching(route route.Route) {
	orchestrator.repository.StopWatching(route)
}

func (orchestrator *UpdateOrchestrator) GetUpdatedModel(itemRoute route.Route) (viewModel viewmodel.Model, found bool) {
	model, found := orchestrator.viewModelOrchestrator.GetFullViewModel(itemRoute)
	if !found {
		return viewmodel.Model{}, false
	}

	return model, true
}
<|endoftext|>


<commit_before>package model_test

import (
	"github.com/Lunchr/luncher-api/db/model"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("RegistrationAccessToken", func() {
	Describe("Token", func() {
		Describe("NewToken", func() {
			It("doesn't return duplicate items", func() {
				t1, err := model.NewToken()
				Expect(err).NotTo(HaveOccurred())
				t2, err := model.NewToken()
				Expect(err).NotTo(HaveOccurred())
				Expect(t1).NotTo(Equal(t2))
			})
		})

		Describe("TokenFromString", func() {
			It("creates a correct token from a known string and back", func() {
				t, err := model.TokenFromString("EF4120DA-0302-BCEE-712B-1C258D2FB6D4")
				Expect(err).NotTo(HaveOccurred())
				Expect(t.String()).To(Equal("EF4120DA-0302-BCEE-712B-1C258D2FB6D4"))
			})
		})
	})
})
<commit_msg>Improve token generation tests<commit_after>package model_test

import (
	"github.com/Lunchr/luncher-api/db/model"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("RegistrationAccessToken", func() {
	Describe("Token", func() {
		Describe("NewToken", func() {
			It("doesn't return duplicate items", func() {
				t1, err := model.NewToken()
				Expect(err).NotTo(HaveOccurred())
				t2, err := model.NewToken()
				Expect(err).NotTo(HaveOccurred())
				Expect(t1).NotTo(Equal(t2))
			})
		})

		Describe("TokenFromString", func() {
			It("creates a correct token from a known string", func() {
				t, err := model.TokenFromString("EF4120DA-0302-BCEE-712B-1C258D2FB6D4")
				Expect(err).NotTo(HaveOccurred())
				Expect(t).To(Equal(model.Token{0xef, 0x41, 0x20, 0xda, 0x3, 0x2, 0xbc, 0xee, 0x71, 0x2b,
					0x1c, 0x25, 0x8d, 0x2f, 0xb6, 0xd4}))
			})
		})

		Describe("String", func() {
			It("creates a correct string from known token", func() {
				t := model.Token{0xef, 0x41, 0x20, 0xda, 0x3, 0x2, 0xbc, 0xee, 0x71, 0x2b, 0x1c, 0x25, 0x8d,
					0x2f, 0xb6, 0xd4}
				Expect(t.String()).To(Equal("EF4120DA-0302-BCEE-712B-1C258D2FB6D4"))
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"github.com/akyoto/color"
	"github.com/animenotifier/notify.moe/arn"
)

func main() {
	color.Yellow("Fixing non-existing anime relations")
	defer color.Green("Finished.")
	defer arn.Node.Close()

	for anime := range arn.StreamAnime() {
		relations := anime.Relations()

		if relations == nil {
			relations = &arn.AnimeRelations{
				AnimeID: anime.ID,
			}

			relations.Save()
		}
	}
}
<commit_msg>Improved patch<commit_after>package main

import (
	"github.com/akyoto/color"
	"github.com/animenotifier/notify.moe/arn"
)

func main() {
	color.Yellow("Fixing non-existing anime relations")
	defer arn.Node.Close()

	count := 0

	for anime := range arn.StreamAnime() {
		relations := anime.Relations()

		if relations == nil {
			relations = &arn.AnimeRelations{
				AnimeID: anime.ID,
			}

			relations.Save()
			count++
		}
	}

	color.Green("Finished adding %d anime relations objects.", count)
}
<|endoftext|>


<commit_before>package interleave

func isInterLeaving(s1, s2, s3 string) bool {
	return false
}
<commit_msg>solve 97 by recursion<commit_after>package interleave

func isInterLeaving(s1, s2, s3 string) bool {
	return useRecursion(s1, s2, s3, 0, 0, 0)
}

// useRecursion time complexity O(2^(M+N)) space complexity O(M+N)
func useRecursion(s1, s2, s3 string, p1, p2, p3 int) bool {
	n1, n2, n3 := len(s1), len(s2), len(s3)
	if n1 + n2 != n3 {
		return false
	}
	if p1 == n1 && p2 == n2 && p3 == n3 {
		return true
	}
	// if p3 == n3 {
	// 	return false
	// }
	var ret bool
	if p1 < n1 && s1[p1] == s3[p3] {
		ret = useRecursion(s1, s2, s3, p1+1, p2, p3+1) || ret
	}
	if p2 < n2 && s2[p2] == s3[p3] {
		ret = useRecursion(s1, s2, s3, p1, p2+1, p3+1) || ret
	}
	return ret
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

func main() {
	//An array type definition specifies a length and an element type.
	var a [5]int
	a[0] = 1
	i := a[0]
	fmt.Println("i value: ", i)
	//Arrays do not need to be initialized explicitly;
	fmt.Println("a[2] value: ", a[2] == 0)
	//Go's arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element.

	//An array literal
	literalArr := [3]int{1, 2, 3}
	fmt.Println("literal array: ", literalArr)
	//compiler count the array elements
	lang := [...]string{"golang", "Dart"}
	fmt.Println("lang: ", lang)

	/*The slice type is an abstraction built on top of Go's array type.
	A slice literal is declared just like an array literal, except you leave out the element count*/
	letters := []string{"a", "b", "c", "d"}
	fmt.Println("letters: ", letters)
	//A slice can be created with the built-in function called make: func make([]T, len, cap) []T
	var s []byte
	s = make([]byte, 5, 5)
	fmt.Println("slice: ", s)
	//When the capacity argument is omitted, it defaults to the specified length
	s2 := make([]byte, 5)
	fmt.Println("slice2: ", s2)
	//The length and capacity of a slice can be inspected using the built-in len and cap functions.
	fmt.Println("Length: ", len(s) == 5)
	fmt.Println("Capacity: ", cap(s) == 5)
	fmt.Println(cap(s) == cap(s2))
	//The zero value of a slice is nil. The len and cap functions will both return 0 for a nil slice

	//A slice can also be formed by "slicing" an existing slice or array
	b := []string{"g", "o", "l", "a", "n", "g"}
	fmt.Println(b)
	fmt.Println("b[:2] == []byte{'g', 'o'} ")
	fmt.Println("b[2:] == []byte{'l', 'a', 'n', 'g'}")
	fmt.Println("b[:] == b")
	/*A slice is a descriptor of an array segment. It consists of a pointer to the array,
	the length of the segment, and its capacity (the maximum length of the segment).
	Slicing does not copy the slice's data. It creates a new slice value that points to the original array.
	A slice cannot be grown beyond its capacity.
	To append one slice to another, use ... to expand the second argument to a list of arguments.*/
	x := []string{"John", "Paul"}
	y := []string{"George", "Ringo", "Pete"}
	x = append(x, y...) // equivalent to "append(a, b[0], b[1], b[2])"
	fmt.Println("Append...:", x)

}
<commit_msg>slices 2<commit_after>package main

import (
	"fmt"
)

func main() {
	//An array type definition specifies a length and an element type.
	var a [5]int
	a[0] = 1
	i := a[0]
	fmt.Println("i value: ", i)
	//Arrays do not need to be initialized explicitly;
	fmt.Println("a[2] value: ", a[2] == 0)
	//Go's arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element.

	//An array literal
	literalArr := [3]int{1, 2, 3}
	fmt.Println("literal array: ", literalArr)
	//compiler count the array elements
	lang := [...]string{"golang", "Dart"}
	fmt.Println("lang: ", lang)

	/*The slice type is an abstraction built on top of Go's array type.
	A slice literal is declared just like an array literal, except you leave out the element count*/
	letters := []string{"a", "b", "c", "d"}
	fmt.Println("letters: ", letters)
	//A slice can be created with the built-in function called make: func make([]T, len, cap) []T
	var s []byte
	s = make([]byte, 5, 5)
	fmt.Println("slice: ", s)
	//When the capacity argument is omitted, it defaults to the specified length
	s2 := make([]byte, 5)
	fmt.Println("slice2: ", s2)
	//The length and capacity of a slice can be inspected using the built-in len and cap functions.
	fmt.Println("Length: ", len(s) == 5)
	fmt.Println("Capacity: ", cap(s) == 5)
	fmt.Println(cap(s) == cap(s2))
	//The zero value of a slice is nil. The len and cap functions will both return 0 for a nil slice

	//A slice can also be formed by "slicing" an existing slice or array
	b := []string{"g", "o", "l", "a", "n", "g"}
	fmt.Println(b)
	fmt.Println("b[:2] == []byte{'g', 'o'} ")
	fmt.Println("b[2:] == []byte{'l', 'a', 'n', 'g'}")
	fmt.Println("b[:] == b")
	/*A slice is a descriptor of an array segment. It consists of a pointer to the array,
	the length of the segment, and its capacity (the maximum length of the segment).
	Slicing does not copy the slice's data. It creates a new slice value that points to the original array.
	A slice cannot be grown beyond its capacity.
	To append one slice to another, use ... to expand the second argument to a list of arguments.*/
	x := []string{"John", "Paul"}
	y := []string{"George", "Ringo", "Pete"}
	x = append(x, y...) // equivalent to "append(a, b[0], b[1], b[2])"
	fmt.Println("Append...:", x)
}
<|endoftext|>


<commit_before>package dns

import "strings"

// Record represents a single DNS Record and it's settings
type Record struct {
	RecordType          string `json:"-"`
	Active              bool   `json:"active,omitempty"`
	Algorithm           int    `json:"algorithm,omitempty"`
	Contact             string `json:"contact,omitempty"`
	Digest              string `json:"digest,omitempty"`
	DigestType          int    `json:"digest_type,omitempty"`
	Expiration          string `json:"expiration,omitempty"`
	Expire              int    `json:"expire,omitempty"`
	Fingerprint         string `json:"fingerprint,omitempty"`
	FingerprintType     int    `json:"fingerprint_type,omitempty"`
	Flags               int    `json:"flags,omitempty"`
	Hardware            string `json:"hardware,omitempty"`
	Inception           string `json:"inception,omitempty"`
	Iterations          int    `json:"iterations,omitempty"`
	Key                 string `json:"key,omitempty"`
	Keytag              int    `json:"keytag,omitempty"`
	Labels              int    `json:"labels,omitempty"`
	Mailbox             string `json:"mailbox,omitempty"`
	Minimum             int    `json:"minimum,omitempty"`
	Name                string `json:"name,omitempty"`
	NextHashedOwnerName string `json:"next_hashed_owner_name,omitempty"`
	Order               int    `json:"order,omitempty"`
	OriginalTTL         int    `json:"original_ttl,omitempty"`
	Originserver        string `json:"originserver,omitempty"`
	Port                int    `json:"port,omitempty"`
	Preference          int    `json:"preference,omitempty"`
	Priority            int    `json:"priority,omitempty"`
	Protocol            int    `json:"protocol,omitempty"`
	Refresh             int    `json:"refresh,omitempty"`
	Regexp              string `json:"regexp,omitempty"`
	Replacement         string `json:"replacement,omitempty"`
	Retry               int    `json:"retry,omitempty"`
	Salt                string `json:"salt,omitempty"`
	Serial              int    `json:"serial,omitempty"`
	Service             string `json:"service,omitempty"`
	Signature           string `json:"signature,omitempty"`
	Signer              string `json:"signer,omitempty"`
	Software            string `json:"software,omitempty"`
	Subtype             int    `json:"subtype,omitempty"`
	Target              string `json:"target,omitempty"`
	TTL                 int    `json:"ttl,omitempty"`
	Txt                 string `json:"txt,omitempty"`
	TypeBitmaps         string `json:"type_bitmaps,omitempty"`
	TypeCovered         string `json:"type_covered,omitempty"`
	Weight              uint   `json:"weight,omitempty"`
}

type NaptrRecord struct {
	Record
	Active      bool   `json:"active,omitempty"`
	Flags       string `json:"flags,omitempty""`
	Name        string `json:"name,omitempty"`
	Order       int    `json:"order"`
	Preference  int    `json:"preference,omitempty"`
	Regexp      string `json:"regexp,omitempty"`
	Replacement string `json:"replacement,omitempty"`
	Service     string `json:"service,omitempty"`
	TTL         int    `json:"ttl,omitempty"`
}

type NsRecord struct {
	Active bool    `json:"active,omitempty"`
	Name   *string `json:"name"`
	Target string  `json:"target,omitempty"`
	TTL    int     `json:"ttl,omitempty"`
}

type Nsec3Record struct {
	Active              bool   `json:"active,omitempty"`
	Algorithm           int    `json:"algorithm,omitempty"`
	Flags               int    `json:"flags"`
	Iterations          int    `json:"iterations,omitempty"`
	Name                string `json:"name,omitempty"`
	NextHashedOwnerName string `json:"next_hashed_owner_name,omitempty"`
	Salt                string `json:"salt,omitempty"`
	TTL                 int    `json:"ttl,omitempty"`
	TypeBitmaps         string `json:"type_bitmaps,omitempty"`
}

type Nsec3paramRecord struct {
	Active     bool   `json:"active,omitempty"`
	Algorithm  int    `json:"algorithm,omitempty"`
	Flags      int    `json:"flags"`
	Iterations int    `json:"iterations,omitempty"`
	Name       string `json:"name,omitempty"`
	Salt       string `json:"salt,omitempty"`
	TTL        int    `json:"ttl,omitempty"`
}

type SrvRecord struct {
	Active   bool   `json:"active,omitempty"`
	Name     string `json:"name,omitempty"`
	Port     int    `json:"port,omitempty"`
	Priority int    `json:"priority,omitempty"`
	Target   string `json:"target,omitempty"`
	TTL      int    `json:"ttl,omitempty"`
	Weight   uint   `json:"weight"`
}

// Allows will validates if a the current record type allows a given field
func (record *Record) Allows(field string) bool {
	field = strings.ToLower(field)

	fieldMap := map[string]map[string]struct{}{
		"active": {
			"A":          {},
			"AAAA":       {},
			"AFSDB":      {},
			"CNAME":      {},
			"DNSKEY":     {},
			"DS":         {},
			"HINFO":      {},
			"LOC":        {},
			"MX":         {},
			"NAPTR":      {},
			"NS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"PTR":        {},
			"RP":         {},
			"RRSIG":      {},
			"SPF":        {},
			"SRV":        {},
			"SSHFP":      {},
			"TXT":        {},
		},
		"algorithm": {
			"DNSKEY":     {},
			"DS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"RRSIG":      {},
			"SSHFP":      {},
		},
		"contact":         {"SOA": {}},
		"digest":          {"DS": {}},
		"digesttype":      {"DS": {}},
		"expiration":      {"RRSIG": {}},
		"expire":          {"SOA": {}},
		"fingerprint":     {"SSHFP": {}},
		"fingerprinttype": {"SSHFP": {}},
		"flags": {
			"DNSKEY":     {},
			"NAPTR":      {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
		},
		"hardware":  {"HINFO": {}},
		"inception": {"RRSIG": {}},
		"iterations": {
			"NSEC3":       {},
			"NSEC3PARAMS": {},
		},
		"key": {
			"DNSKEY": {},
			"DS":     {},
		},
		"keytag":  {"RRSIG": {}},
		"labels":  {"RRSIG": {}},
		"mailbox": {"RP": {}},
		"minimum": {"SOA": {}},
		"name": {
			"A":          {},
			"AAAA":       {},
			"AFSDB":      {},
			"CNAME":      {},
			"DNSKEY":     {},
			"DS":         {},
			"HINFO":      {},
			"LOC":        {},
			"MX":         {},
			"NAPTR":      {},
			"NS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"PTR":        {},
			"RP":         {},
			"RRSIG":      {},
			"SPF":        {},
			"SRV":        {},
			"SSHFP":      {},
			"TXT":        {},
		},
		"nexthashedownername": {"NSEC3": {}},
		"order":               {"NAPTR": {}},
		"originalttl":         {"RRSIG": {}},
		"originserver":        {"SOA": {}},
		"port":                {"SRV": {}},
		"preference":          {"NAPTR": {}},
		"priority": {
			"SRV": {},
			"MX":  {},
		},
		"protocol":    {"DNSKEY": {}},
		"refresh":     {"SOA": {}},
		"regexp":      {"NAPTR": {}},
		"replacement": {"NAPTR": {}},
		"retry":       {"SOA": {}},
		"salt": {
			"NSEC3":      {},
			"NSEC3PARAM": {},
		},
		"serial":    {"SOA": {}},
		"service":   {"NAPTR": {}},
		"signature": {"RRSIG": {}},
		"signer":    {"RRSIG": {}},
		"software":  {"HINFO": {}},
		"subtype":   {"AFSDB": {}},
		"targets": {
			"A":          {},
			"AAAA":       {},
			"AFSDB":      {},
			"CNAME":      {},
			"DNSKEY":     {},
			"DS":         {},
			"HINFO":      {},
			"LOC":        {},
			"MX":         {},
			"NAPTR":      {},
			"NS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"PTR":        {},
			"RP":         {},
			"RRSIG":      {},
			"SOA":        {},
			"SPF":        {},
			"SRV":        {},
			"SSHFP":      {},
			"TXT":        {},
		},
		"ttl": {
			"A":     {},
			"AAAA":  {},
			"AFSDB": {},
			"CNAME": {},
			"LOC":   {},
			"MX":    {},
			"NS":    {},
			"PTR":   {},
			"SPF":   {},
			"SRV":   {},
			"TXT":   {},
		},
		"txt":         {"RP": {}},
		"typebitmaps": {"NSEC3": {}},
		"typecovered": {"RRSIG": {}},
		"weight":      {"SRV": {}},
	}

	_, ok := fieldMap[field][strings.ToUpper(record.RecordType)]

	return ok
}
<commit_msg>Add in `Record` to each sub-record type<commit_after>package dns

import "strings"

// Record represents a single DNS Record and it's settings
type Record struct {
	RecordType          string `json:"-"`
	Active              bool   `json:"active,omitempty"`
	Algorithm           int    `json:"algorithm,omitempty"`
	Contact             string `json:"contact,omitempty"`
	Digest              string `json:"digest,omitempty"`
	DigestType          int    `json:"digest_type,omitempty"`
	Expiration          string `json:"expiration,omitempty"`
	Expire              int    `json:"expire,omitempty"`
	Fingerprint         string `json:"fingerprint,omitempty"`
	FingerprintType     int    `json:"fingerprint_type,omitempty"`
	Flags               int    `json:"flags,omitempty"`
	Hardware            string `json:"hardware,omitempty"`
	Inception           string `json:"inception,omitempty"`
	Iterations          int    `json:"iterations,omitempty"`
	Key                 string `json:"key,omitempty"`
	Keytag              int    `json:"keytag,omitempty"`
	Labels              int    `json:"labels,omitempty"`
	Mailbox             string `json:"mailbox,omitempty"`
	Minimum             int    `json:"minimum,omitempty"`
	Name                string `json:"name,omitempty"`
	NextHashedOwnerName string `json:"next_hashed_owner_name,omitempty"`
	Order               int    `json:"order,omitempty"`
	OriginalTTL         int    `json:"original_ttl,omitempty"`
	Originserver        string `json:"originserver,omitempty"`
	Port                int    `json:"port,omitempty"`
	Preference          int    `json:"preference,omitempty"`
	Priority            int    `json:"priority,omitempty"`
	Protocol            int    `json:"protocol,omitempty"`
	Refresh             int    `json:"refresh,omitempty"`
	Regexp              string `json:"regexp,omitempty"`
	Replacement         string `json:"replacement,omitempty"`
	Retry               int    `json:"retry,omitempty"`
	Salt                string `json:"salt,omitempty"`
	Serial              int    `json:"serial,omitempty"`
	Service             string `json:"service,omitempty"`
	Signature           string `json:"signature,omitempty"`
	Signer              string `json:"signer,omitempty"`
	Software            string `json:"software,omitempty"`
	Subtype             int    `json:"subtype,omitempty"`
	Target              string `json:"target,omitempty"`
	TTL                 int    `json:"ttl,omitempty"`
	Txt                 string `json:"txt,omitempty"`
	TypeBitmaps         string `json:"type_bitmaps,omitempty"`
	TypeCovered         string `json:"type_covered,omitempty"`
	Weight              uint   `json:"weight,omitempty"`
}

type NaptrRecord struct {
	Record
	Active      bool   `json:"active,omitempty"`
	Flags       string `json:"flags,omitempty""`
	Name        string `json:"name,omitempty"`
	Order       int    `json:"order"`
	Preference  int    `json:"preference,omitempty"`
	Regexp      string `json:"regexp,omitempty"`
	Replacement string `json:"replacement,omitempty"`
	Service     string `json:"service,omitempty"`
	TTL         int    `json:"ttl,omitempty"`
}

type NsRecord struct {
	Record
	Active bool    `json:"active,omitempty"`
	Name   *string `json:"name"`
	Target string  `json:"target,omitempty"`
	TTL    int     `json:"ttl,omitempty"`
}

type Nsec3Record struct {
	Record
	Active              bool   `json:"active,omitempty"`
	Algorithm           int    `json:"algorithm,omitempty"`
	Flags               int    `json:"flags"`
	Iterations          int    `json:"iterations,omitempty"`
	Name                string `json:"name,omitempty"`
	NextHashedOwnerName string `json:"next_hashed_owner_name,omitempty"`
	Salt                string `json:"salt,omitempty"`
	TTL                 int    `json:"ttl,omitempty"`
	TypeBitmaps         string `json:"type_bitmaps,omitempty"`
}

type Nsec3paramRecord struct {
	Record
	Active     bool   `json:"active,omitempty"`
	Algorithm  int    `json:"algorithm,omitempty"`
	Flags      int    `json:"flags"`
	Iterations int    `json:"iterations,omitempty"`
	Name       string `json:"name,omitempty"`
	Salt       string `json:"salt,omitempty"`
	TTL        int    `json:"ttl,omitempty"`
}

type SrvRecord struct {
	Record
	Active   bool   `json:"active,omitempty"`
	Name     string `json:"name,omitempty"`
	Port     int    `json:"port,omitempty"`
	Priority int    `json:"priority,omitempty"`
	Target   string `json:"target,omitempty"`
	TTL      int    `json:"ttl,omitempty"`
	Weight   uint   `json:"weight"`
}

// Allows will validates if a the current record type allows a given field
func (record *Record) Allows(field string) bool {
	field = strings.ToLower(field)

	fieldMap := map[string]map[string]struct{}{
		"active": {
			"A":          {},
			"AAAA":       {},
			"AFSDB":      {},
			"CNAME":      {},
			"DNSKEY":     {},
			"DS":         {},
			"HINFO":      {},
			"LOC":        {},
			"MX":         {},
			"NAPTR":      {},
			"NS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"PTR":        {},
			"RP":         {},
			"RRSIG":      {},
			"SPF":        {},
			"SRV":        {},
			"SSHFP":      {},
			"TXT":        {},
		},
		"algorithm": {
			"DNSKEY":     {},
			"DS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"RRSIG":      {},
			"SSHFP":      {},
		},
		"contact":         {"SOA": {}},
		"digest":          {"DS": {}},
		"digesttype":      {"DS": {}},
		"expiration":      {"RRSIG": {}},
		"expire":          {"SOA": {}},
		"fingerprint":     {"SSHFP": {}},
		"fingerprinttype": {"SSHFP": {}},
		"flags": {
			"DNSKEY":     {},
			"NAPTR":      {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
		},
		"hardware":  {"HINFO": {}},
		"inception": {"RRSIG": {}},
		"iterations": {
			"NSEC3":       {},
			"NSEC3PARAMS": {},
		},
		"key": {
			"DNSKEY": {},
			"DS":     {},
		},
		"keytag":  {"RRSIG": {}},
		"labels":  {"RRSIG": {}},
		"mailbox": {"RP": {}},
		"minimum": {"SOA": {}},
		"name": {
			"A":          {},
			"AAAA":       {},
			"AFSDB":      {},
			"CNAME":      {},
			"DNSKEY":     {},
			"DS":         {},
			"HINFO":      {},
			"LOC":        {},
			"MX":         {},
			"NAPTR":      {},
			"NS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"PTR":        {},
			"RP":         {},
			"RRSIG":      {},
			"SPF":        {},
			"SRV":        {},
			"SSHFP":      {},
			"TXT":        {},
		},
		"nexthashedownername": {"NSEC3": {}},
		"order":               {"NAPTR": {}},
		"originalttl":         {"RRSIG": {}},
		"originserver":        {"SOA": {}},
		"port":                {"SRV": {}},
		"preference":          {"NAPTR": {}},
		"priority": {
			"SRV": {},
			"MX":  {},
		},
		"protocol":    {"DNSKEY": {}},
		"refresh":     {"SOA": {}},
		"regexp":      {"NAPTR": {}},
		"replacement": {"NAPTR": {}},
		"retry":       {"SOA": {}},
		"salt": {
			"NSEC3":      {},
			"NSEC3PARAM": {},
		},
		"serial":    {"SOA": {}},
		"service":   {"NAPTR": {}},
		"signature": {"RRSIG": {}},
		"signer":    {"RRSIG": {}},
		"software":  {"HINFO": {}},
		"subtype":   {"AFSDB": {}},
		"targets": {
			"A":          {},
			"AAAA":       {},
			"AFSDB":      {},
			"CNAME":      {},
			"DNSKEY":     {},
			"DS":         {},
			"HINFO":      {},
			"LOC":        {},
			"MX":         {},
			"NAPTR":      {},
			"NS":         {},
			"NSEC3":      {},
			"NSEC3PARAM": {},
			"PTR":        {},
			"RP":         {},
			"RRSIG":      {},
			"SOA":        {},
			"SPF":        {},
			"SRV":        {},
			"SSHFP":      {},
			"TXT":        {},
		},
		"ttl": {
			"A":     {},
			"AAAA":  {},
			"AFSDB": {},
			"CNAME": {},
			"LOC":   {},
			"MX":    {},
			"NS":    {},
			"PTR":   {},
			"SPF":   {},
			"SRV":   {},
			"TXT":   {},
		},
		"txt":         {"RP": {}},
		"typebitmaps": {"NSEC3": {}},
		"typecovered": {"RRSIG": {}},
		"weight":      {"SRV": {}},
	}

	_, ok := fieldMap[field][strings.ToUpper(record.RecordType)]

	return ok
}
<|endoftext|>