<commit_before>// Copyright 2021 Prometheus Team
// 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 sns

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"unicode/utf8"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/alertmanager/types"
	commoncfg "github.com/prometheus/common/config"
)

// Notifier implements a Notifier for SNS notifications.
type Notifier struct {
	conf    *config.SNSConfig
	tmpl    *template.Template
	logger  log.Logger
	client  *http.Client
	retrier *notify.Retrier
	isFifo  *bool
}

// New returns a new SNS notification handler.
func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
	client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", append(httpOpts, commoncfg.WithHTTP2Disabled())...)
	if err != nil {
		return nil, err
	}
	return &Notifier{
		conf:    c,
		tmpl:    t,
		logger:  l,
		client:  client,
		retrier: &notify.Retrier{},
	}, nil
}

func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
	var (
		err   error
		data                           = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
		tmpl                           = notify.TmplText(n.tmpl, data, &err)
		creds *credentials.Credentials = nil
	)
	if n.conf.Sigv4.AccessKey != "" && n.conf.Sigv4.SecretKey != "" {
		creds = credentials.NewStaticCredentials(n.conf.Sigv4.AccessKey, string(n.conf.Sigv4.SecretKey), "")
	}

	attributes := make(map[string]*sns.MessageAttributeValue, len(n.conf.Attributes))
	if len(n.conf.Attributes) > 0 {
		for k, v := range n.conf.Attributes {
			attributes[tmpl(k)] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(tmpl(v))}
		}

	}

	sess, err := session.NewSessionWithOptions(session.Options{
		Config: aws.Config{
			Region:      aws.String(n.conf.Sigv4.Region),
			Credentials: creds,
			Endpoint:    aws.String(tmpl(n.conf.APIUrl)),
		},
		Profile: n.conf.Sigv4.Profile,
	})

	if _, err := sess.Config.Credentials.Get(); err != nil {
		return false, fmt.Errorf("could not get SigV4 credentials: %w", err)
	}

	if n.conf.Sigv4.RoleARN != "" {
		sess.Config.Credentials = stscreds.NewCredentials(sess, n.conf.Sigv4.RoleARN)
	}

	// Max message size for a message in a SNS publish request is 256KB, except for SMS messages where the limit is 1600 characters/runes.
	messageSize := 256 * 1024
	client := sns.New(sess, &aws.Config{Credentials: creds})
	publishInput := &sns.PublishInput{}

	if n.conf.TopicARN != "" {
		publishInput.SetTopicArn(tmpl(n.conf.TopicARN))

		if n.isFifo == nil {
			checkFifo, err := checkTopicFifoAttribute(client, n.conf.TopicARN)
			if err != nil {
				if e, ok := err.(awserr.RequestFailure); ok {
					return n.retrier.Check(e.StatusCode(), strings.NewReader(e.Message()))
				} else {
					return true, err
				}
			}
			n.isFifo = &checkFifo
		}
		if *n.isFifo {
			// Deduplication key and Message Group ID are only added if it's a FIFO SNS Topic.
			key, err := notify.ExtractGroupKey(ctx)
			if err != nil {
				return false, err
			}
			publishInput.SetMessageDeduplicationId(key.Hash())
			publishInput.SetMessageGroupId(key.Hash())
		}
	}
	if n.conf.PhoneNumber != "" {
		publishInput.SetPhoneNumber(tmpl(n.conf.PhoneNumber))
		// If we have an SMS message, we need to truncate to 1600 characters/runes.
		messageSize = 1600
	}
	if n.conf.TargetARN != "" {
		publishInput.SetTargetArn(tmpl(n.conf.TargetARN))

	}

	messageToSend, isTrunc, err := validateAndTruncateMessage(tmpl(n.conf.Message), messageSize)
	if err != nil {
		return false, err
	}
	if isTrunc {
		attributes[tmpl("truncated")] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(tmpl("true"))}
	}
	publishInput.SetMessage(messageToSend)

	if n.conf.Subject != "" {
		publishInput.SetSubject(tmpl(n.conf.Subject))
	}

	publishInput.SetMessageAttributes(attributes)

	publishOutput, err := client.Publish(publishInput)
	if err != nil {
		if e, ok := err.(awserr.RequestFailure); ok {
			return n.retrier.Check(e.StatusCode(), strings.NewReader(e.Message()))
		} else {
			return true, err
		}
	}

	level.Debug(n.logger).Log("msg", "SNS publish successfully sent", "message_id", publishOutput.MessageId, "sequence number", publishOutput.SequenceNumber)

	return false, nil
}

func checkTopicFifoAttribute(client *sns.SNS, topicARN string) (bool, error) {
	topicAttributes, err := client.GetTopicAttributes(&sns.GetTopicAttributesInput{TopicArn: aws.String(topicARN)})
	if err != nil {
		return false, err
	}
	ta := topicAttributes.Attributes["FifoTopic"]
	if aws.StringValue(ta) == "true" {
		return true, nil
	}
	return false, nil
}

func validateAndTruncateMessage(message string, sizeInBytes int) (string, bool, error) {
	if !utf8.ValidString(message) {
		return "", false, fmt.Errorf("non utf8 encoded message string")
	}
	if len(message) <= sizeInBytes {
		return message, false, nil
	}
	// if the message is larger than our specified size we have to truncate.
	truncated := make([]byte, sizeInBytes)
	copy(truncated, message)
	return string(truncated), true, nil
}
<commit_msg>Add missing template for aws region<commit_after>// Copyright 2021 Prometheus Team
// 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 sns

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"unicode/utf8"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/alertmanager/types"
	commoncfg "github.com/prometheus/common/config"
)

// Notifier implements a Notifier for SNS notifications.
type Notifier struct {
	conf    *config.SNSConfig
	tmpl    *template.Template
	logger  log.Logger
	client  *http.Client
	retrier *notify.Retrier
	isFifo  *bool
}

// New returns a new SNS notification handler.
func New(c *config.SNSConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
	client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "sns", append(httpOpts, commoncfg.WithHTTP2Disabled())...)
	if err != nil {
		return nil, err
	}
	return &Notifier{
		conf:    c,
		tmpl:    t,
		logger:  l,
		client:  client,
		retrier: &notify.Retrier{},
	}, nil
}

func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
	var (
		err   error
		data                           = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
		tmpl                           = notify.TmplText(n.tmpl, data, &err)
		creds *credentials.Credentials = nil
	)
	if n.conf.Sigv4.AccessKey != "" && n.conf.Sigv4.SecretKey != "" {
		creds = credentials.NewStaticCredentials(n.conf.Sigv4.AccessKey, string(n.conf.Sigv4.SecretKey), "")
	}

	attributes := make(map[string]*sns.MessageAttributeValue, len(n.conf.Attributes))
	if len(n.conf.Attributes) > 0 {
		for k, v := range n.conf.Attributes {
			attributes[tmpl(k)] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(tmpl(v))}
		}

	}

	sess, err := session.NewSessionWithOptions(session.Options{
		Config: aws.Config{
			Region:      aws.String(tmpl(n.conf.Sigv4.Region)),
			Credentials: creds,
			Endpoint:    aws.String(tmpl(n.conf.APIUrl)),
		},
		Profile: n.conf.Sigv4.Profile,
	})

	if _, err := sess.Config.Credentials.Get(); err != nil {
		return false, fmt.Errorf("could not get SigV4 credentials: %w", err)
	}

	if n.conf.Sigv4.RoleARN != "" {
		sess.Config.Credentials = stscreds.NewCredentials(sess, n.conf.Sigv4.RoleARN)
	}

	// Max message size for a message in a SNS publish request is 256KB, except for SMS messages where the limit is 1600 characters/runes.
	messageSize := 256 * 1024
	client := sns.New(sess, &aws.Config{Credentials: creds})
	publishInput := &sns.PublishInput{}

	if n.conf.TopicARN != "" {
		publishInput.SetTopicArn(tmpl(n.conf.TopicARN))

		if n.isFifo == nil {
			checkFifo, err := checkTopicFifoAttribute(client, n.conf.TopicARN)
			if err != nil {
				if e, ok := err.(awserr.RequestFailure); ok {
					return n.retrier.Check(e.StatusCode(), strings.NewReader(e.Message()))
				} else {
					return true, err
				}
			}
			n.isFifo = &checkFifo
		}
		if *n.isFifo {
			// Deduplication key and Message Group ID are only added if it's a FIFO SNS Topic.
			key, err := notify.ExtractGroupKey(ctx)
			if err != nil {
				return false, err
			}
			publishInput.SetMessageDeduplicationId(key.Hash())
			publishInput.SetMessageGroupId(key.Hash())
		}
	}
	if n.conf.PhoneNumber != "" {
		publishInput.SetPhoneNumber(tmpl(n.conf.PhoneNumber))
		// If we have an SMS message, we need to truncate to 1600 characters/runes.
		messageSize = 1600
	}
	if n.conf.TargetARN != "" {
		publishInput.SetTargetArn(tmpl(n.conf.TargetARN))

	}

	messageToSend, isTrunc, err := validateAndTruncateMessage(tmpl(n.conf.Message), messageSize)
	if err != nil {
		return false, err
	}
	if isTrunc {
		attributes[tmpl("truncated")] = &sns.MessageAttributeValue{DataType: aws.String("String"), StringValue: aws.String(tmpl("true"))}
	}
	publishInput.SetMessage(messageToSend)

	if n.conf.Subject != "" {
		publishInput.SetSubject(tmpl(n.conf.Subject))
	}

	publishInput.SetMessageAttributes(attributes)

	publishOutput, err := client.Publish(publishInput)
	if err != nil {
		if e, ok := err.(awserr.RequestFailure); ok {
			return n.retrier.Check(e.StatusCode(), strings.NewReader(e.Message()))
		} else {
			return true, err
		}
	}

	level.Debug(n.logger).Log("msg", "SNS publish successfully sent", "message_id", publishOutput.MessageId, "sequence number", publishOutput.SequenceNumber)

	return false, nil
}

func checkTopicFifoAttribute(client *sns.SNS, topicARN string) (bool, error) {
	topicAttributes, err := client.GetTopicAttributes(&sns.GetTopicAttributesInput{TopicArn: aws.String(topicARN)})
	if err != nil {
		return false, err
	}
	ta := topicAttributes.Attributes["FifoTopic"]
	if aws.StringValue(ta) == "true" {
		return true, nil
	}
	return false, nil
}

func validateAndTruncateMessage(message string, sizeInBytes int) (string, bool, error) {
	if !utf8.ValidString(message) {
		return "", false, fmt.Errorf("non utf8 encoded message string")
	}
	if len(message) <= sizeInBytes {
		return message, false, nil
	}
	// if the message is larger than our specified size we have to truncate.
	truncated := make([]byte, sizeInBytes)
	copy(truncated, message)
	return string(truncated), true, nil
}
<|endoftext|>


<commit_before>package slashquery

import (
	"fmt"
	"net/url"
)

// Setup configures the upstream
func (sq *Slashquery) Setup() error {
	for _, route := range sq.Routes {
		if route.URL != "" {
			u, err := url.Parse(route.URL)
			if err != nil {
				return err
			}
			route.Scheme = u.Scheme
			route.Host = u.Host
			route.Path = u.Path
			route.rawQuery = u.RawQuery
			if route.Upstream == "" {
				route.Upstream = u.Host
				if _, ok := sq.Upstreams[u.Host]; !ok {
					sq.Upstreams[u.Host] = &Upstream{
						Servers: []string{u.Host},
					}
				}
			}
			continue
		}

		// set http or https
		if route.Scheme == "" {
			route.Scheme = "http"
		} else if route.Scheme != "https" {
			return fmt.Errorf("Unsuported scheme: %q", route.Scheme)
		}
		u, err := url.Parse(fmt.Sprintf("%s://%s%s", route.Scheme, route.Host, route.Path))
		if err != nil {
			return err
		}
		route.Scheme = u.Scheme
		route.Host = u.Host
		route.Path = u.Path
		route.rawQuery = u.RawQuery
	}
	return nil
}
<commit_msg>allow port when using domain in url foo.com:8080 creates foo.com as upstream<commit_after>package slashquery

import (
	"fmt"
	"net/url"
	"strings"
)

// Setup configures the upstream
func (sq *Slashquery) Setup() error {
	for _, route := range sq.Routes {
		if route.URL != "" {
			u, err := url.Parse(route.URL)
			if err != nil {
				return err
			}
			route.Scheme = u.Scheme
			route.Host = u.Host
			route.Path = u.Path
			route.rawQuery = u.RawQuery
			if route.Upstream == "" {
				h := strings.Split(u.Host, ":")[0]
				route.Upstream = h
				if _, ok := sq.Upstreams[h]; !ok {
					sq.Upstreams[h] = &Upstream{
						Servers: []string{h},
					}
				}
			}
			continue
		}

		// set http or https
		if route.Scheme == "" {
			route.Scheme = "http"
		} else if route.Scheme != "https" {
			return fmt.Errorf("Unsuported scheme: %q", route.Scheme)
		}
		u, err := url.Parse(fmt.Sprintf("%s://%s%s", route.Scheme, route.Host, route.Path))
		if err != nil {
			return err
		}
		route.Scheme = u.Scheme
		route.Host = u.Host
		route.Path = u.Path
		route.rawQuery = u.RawQuery
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"os"
	"os/exec"
	"strconv"
	"syscall"
)

type FactorioServer struct {
	Cmd              *exec.Cmd
	Savefile         string
	Latency          int  `json:"latency"`
	AutosaveInterval int  `json:"autosave_interval"`
	AutosaveSlots    int  `json:"autosave_slots"`
	Port             int  `json:"port"`
	DisallowCmd      bool `json:"disallow_cmd"`
	Running          bool `json:"running"`
	PeerToPeer       bool `json:"peer2peer"`
	AutoPause        bool `json:"auto_pause"`
	StdOut           io.ReadCloser
	StdErr           io.ReadCloser
	StdIn            io.WriteCloser
}

func createSave(saveName string) (string, error) {
	args := []string{"--create", saveName}

	cmdOutput, err := exec.Command(config.FactorioBinary, args...).Output()
	if err != nil {
		log.Printf("Error in creating Factorio save: %s", err)
		return "", err
	}

	result := string(cmdOutput)

	return result, nil
}

func initFactorio() *FactorioServer {
	// TODO move values to config struct
	f := FactorioServer{
		Latency:          100,
		AutosaveInterval: 5,
		AutosaveSlots:    10,
	}

	return &f
}

func (f *FactorioServer) Run() error {
	var err error

	args := []string{"--start-server", config.FactorioSavesDir + "/" + f.Savefile,
		"--latency-ms", strconv.Itoa(f.Latency),
		"--autosave-interval", strconv.Itoa(f.AutosaveInterval),
		"--autosave-slots", strconv.Itoa(f.AutosaveSlots),
		"--port", strconv.Itoa(f.Port)}
	if f.DisallowCmd {
		args = append(args, "--disallow-commands")
	}
	if f.PeerToPeer {
		args = append(args, "--peer-to-peer")
	}
	if f.AutoPause {
		args = append(args, "--no-auto-pause")
	}

	log.Println("Starting server with command: ", config.FactorioBinary, args)

	f.Cmd = exec.Command(config.FactorioBinary, args...)

	f.StdOut, err = f.Cmd.StdoutPipe()
	if err != nil {
		log.Printf("Error opening stdout pipe: %s", err)
		return err
	}

	f.StdIn, err = f.Cmd.StdinPipe()
	if err != nil {
		log.Printf("Error opening stdin pipe: %s", err)
		return err
	}

	f.StdErr, err = f.Cmd.StderrPipe()
	if err != nil {
		log.Printf("Error opening stderr pipe: %s", err)
		return err
	}

	go io.Copy(os.Stdout, f.StdOut)
	go io.Copy(os.Stderr, f.StdErr)

	err = f.Cmd.Start()
	if err != nil {
		log.Printf("Error starting server process: %s", err)
		return err
	}

	f.Running = true

	err = f.Cmd.Wait()
	if err != nil {
		log.Printf("Command exited with error: %s", err)
		return err
	}

	return nil
}

func (f *FactorioServer) Stop() error {
	err := f.Cmd.Process.Signal(syscall.SIGINT)
	if err != nil {
		log.Printf("Error sending SIGINT to Factorio process: %s", err)
		return err
	}

	f.Running = false

	log.Printf("Sent SIGINT to Factorio process, Factorio server shutting down...")

	return nil
}
<commit_msg>Modified server-start save path variable for compatibility<commit_after>package main

import (
	"io"
	"log"
	"os"
	"os/exec"
	"strconv"
	"syscall"
)

type FactorioServer struct {
	Cmd              *exec.Cmd
	Savefile         string
	Latency          int  `json:"latency"`
	AutosaveInterval int  `json:"autosave_interval"`
	AutosaveSlots    int  `json:"autosave_slots"`
	Port             int  `json:"port"`
	DisallowCmd      bool `json:"disallow_cmd"`
	Running          bool `json:"running"`
	PeerToPeer       bool `json:"peer2peer"`
	AutoPause        bool `json:"auto_pause"`
	StdOut           io.ReadCloser
	StdErr           io.ReadCloser
	StdIn            io.WriteCloser
}

func createSave(saveName string) (string, error) {
	args := []string{"--create", saveName}

	cmdOutput, err := exec.Command(config.FactorioBinary, args...).Output()
	if err != nil {
		log.Printf("Error in creating Factorio save: %s", err)
		return "", err
	}

	result := string(cmdOutput)

	return result, nil
}

func initFactorio() *FactorioServer {
	// TODO move values to config struct
	f := FactorioServer{
		Latency:          100,
		AutosaveInterval: 5,
		AutosaveSlots:    10,
	}

	return &f
}

func (f *FactorioServer) Run() error {
	var err error

	args := []string{"--start-server", filepath.Join(config.FactorioSavesDir, f.Savefile),
		"--latency-ms", strconv.Itoa(f.Latency),
		"--autosave-interval", strconv.Itoa(f.AutosaveInterval),
		"--autosave-slots", strconv.Itoa(f.AutosaveSlots),
		"--port", strconv.Itoa(f.Port)}
	if f.DisallowCmd {
		args = append(args, "--disallow-commands")
	}
	if f.PeerToPeer {
		args = append(args, "--peer-to-peer")
	}
	if f.AutoPause {
		args = append(args, "--no-auto-pause")
	}

	log.Println("Starting server with command: ", config.FactorioBinary, args)

	f.Cmd = exec.Command(config.FactorioBinary, args...)

	f.StdOut, err = f.Cmd.StdoutPipe()
	if err != nil {
		log.Printf("Error opening stdout pipe: %s", err)
		return err
	}

	f.StdIn, err = f.Cmd.StdinPipe()
	if err != nil {
		log.Printf("Error opening stdin pipe: %s", err)
		return err
	}

	f.StdErr, err = f.Cmd.StderrPipe()
	if err != nil {
		log.Printf("Error opening stderr pipe: %s", err)
		return err
	}

	go io.Copy(os.Stdout, f.StdOut)
	go io.Copy(os.Stderr, f.StdErr)

	err = f.Cmd.Start()
	if err != nil {
		log.Printf("Error starting server process: %s", err)
		return err
	}

	f.Running = true

	err = f.Cmd.Wait()
	if err != nil {
		log.Printf("Command exited with error: %s", err)
		return err
	}

	return nil
}

func (f *FactorioServer) Stop() error {
	err := f.Cmd.Process.Signal(syscall.SIGINT)
	if err != nil {
		log.Printf("Error sending SIGINT to Factorio process: %s", err)
		return err
	}

	f.Running = false

	log.Printf("Sent SIGINT to Factorio process, Factorio server shutting down...")

	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"sync"
	"time"

	"github.com/coreos/fleet/job"
)

type testJob func(j *job.Job) bool

func testJobStateLoaded(j *job.Job) bool {
	if j == nil || j.State == nil {
		return false
	}
	return *(j.State) == job.JobStateLoaded
}

func testJobStateLaunched(j *job.Job) bool {
	if j == nil || j.State == nil {
		return false
	}
	return *(j.State) == job.JobStateLaunched
}

func testJobStateInactive(j *job.Job) bool {
	if j == nil || j.State == nil {
		return false
	}
	return *(j.State) == job.JobStateInactive
}

func testUnitStateActive(j *job.Job) bool {
	if j == nil || j.UnitState == nil {
		return false
	}
	return j.UnitState.ActiveState == "active"

}

// stateCheck defines how to monitor a job state
type stateCheck struct {
	test      testJob
	statechan chan *jobState
	errchan   chan error
}

// newStateCheck returns a StateCheck struct with new channels for monitoring
func newStateCheck(test testJob) *stateCheck {
	statechan := make(chan *jobState)
	errchan := make(chan error)
	return &stateCheck{test, statechan, errchan}
}

// waitForJobStates polls each of the indicated jobs until each of their
// states is equal to that which the caller indicates via stateCheck test
func waitForJobStates(jobs []string, check *stateCheck) error {
	var wg sync.WaitGroup

	// check each job with the stateCheck
	for _, name := range jobs {
		wg.Add(1)
		go checkJobState(name, check, &wg)
	}

	// wait for all jobs to complete
	go func() {
		wg.Wait()
		close(check.errchan)
	}()

	// print output while jobs are transitioning
	defer fmt.Printf("\n")
	for {
		select {
		// read from state channel
		case state := <-check.statechan:
			// return on closed channel
			if state == nil {
				return nil
			}
			// otherwise print output
			if state.sub == "" || state.sub == "dead" {
				fmt.Printf("\033[0;33m%v:\033[0m %v, %v                              \r",
					state.name, state.loaded, state.active)
			} else {
				fmt.Printf("\033[0;33m%v:\033[0m %v, %v (%v)                        \r",
					state.name, state.loaded, state.active, state.sub)
			}
		// read from error channel
		case err := <-check.errchan:
			return err
		}
		time.Sleep(200 * time.Millisecond)
	}
}

func checkJobState(jobName string, check *stateCheck, wg *sync.WaitGroup) {
	defer wg.Done()
	sleep := 100 * time.Millisecond
	for {
		if assertJobState(jobName, check) {
			return
		}
		time.Sleep(sleep)
	}
}

type jobState struct {
	name   string
	loaded string
	active string
	sub    string
}

func newJobState(name string, j *job.Job) *jobState {
	var (
		loaded string
		active string
		sub    string
	)
	if j.State != nil {
		loaded = fmt.Sprintf("%v", *(j.State))
	}
	if j.UnitState != nil {
		active = j.UnitState.ActiveState
		sub = j.UnitState.SubState
	}
	return &jobState{name, loaded, active, sub}
}

func assertJobState(name string, check *stateCheck) bool {
	j, err := cAPI.Job(name)
	if err != nil {
		check.errchan <- fmt.Errorf("Error retrieving Job(%s) from Registry: %v", name, err)
		return false
	}

	// send current state to the output channel
	check.statechan <- newJobState(name, j)

	// if test function
	if check.test(j) {
		close(check.statechan)
		return true
	}
	return false
}
<commit_msg>fix(state): print inactive states without substates<commit_after>package client

import (
	"fmt"
	"sync"
	"time"

	"github.com/coreos/fleet/job"
)

type testJob func(j *job.Job) bool

func testJobStateLoaded(j *job.Job) bool {
	if j == nil || j.State == nil {
		return false
	}
	return *(j.State) == job.JobStateLoaded
}

func testJobStateLaunched(j *job.Job) bool {
	if j == nil || j.State == nil {
		return false
	}
	return *(j.State) == job.JobStateLaunched
}

func testJobStateInactive(j *job.Job) bool {
	if j == nil || j.State == nil {
		return false
	}
	return *(j.State) == job.JobStateInactive
}

func testUnitStateActive(j *job.Job) bool {
	if j == nil || j.UnitState == nil {
		return false
	}
	return j.UnitState.ActiveState == "active"

}

// stateCheck defines how to monitor a job state
type stateCheck struct {
	test      testJob
	statechan chan *jobState
	errchan   chan error
}

// newStateCheck returns a StateCheck struct with new channels for monitoring
func newStateCheck(test testJob) *stateCheck {
	statechan := make(chan *jobState)
	errchan := make(chan error)
	return &stateCheck{test, statechan, errchan}
}

// waitForJobStates polls each of the indicated jobs until each of their
// states is equal to that which the caller indicates via stateCheck test
func waitForJobStates(jobs []string, check *stateCheck) error {
	var wg sync.WaitGroup

	// check each job with the stateCheck
	for _, name := range jobs {
		wg.Add(1)
		go checkJobState(name, check, &wg)
	}

	// wait for all jobs to complete
	go func() {
		wg.Wait()
		close(check.errchan)
	}()

	// print output while jobs are transitioning
	defer fmt.Printf("\n")
	for {
		select {
		// read from state channel
		case state := <-check.statechan:
			// return on closed channel
			if state == nil {
				return nil
			}
			// otherwise print output
			if state.loaded == "inactive" {
				fmt.Printf("\033[0;33m%v:\033[0m %v                                 \r",
					state.name, state.loaded)
			} else {
				fmt.Printf("\033[0;33m%v:\033[0m %v, %v (%v)                        \r",
					state.name, state.loaded, state.active, state.sub)
			}
		// read from error channel
		case err := <-check.errchan:
			return err
		}
		time.Sleep(200 * time.Millisecond)
	}
}

func checkJobState(jobName string, check *stateCheck, wg *sync.WaitGroup) {
	defer wg.Done()
	sleep := 100 * time.Millisecond
	for {
		if assertJobState(jobName, check) {
			return
		}
		time.Sleep(sleep)
	}
}

type jobState struct {
	name   string
	loaded string
	active string
	sub    string
}

func newJobState(name string, j *job.Job) *jobState {
	var (
		loaded string
		active string
		sub    string
	)
	if j.State != nil {
		loaded = fmt.Sprintf("%v", *(j.State))
	}
	if j.UnitState != nil {
		active = j.UnitState.ActiveState
		sub = j.UnitState.SubState
	}
	return &jobState{name, loaded, active, sub}
}

func assertJobState(name string, check *stateCheck) bool {
	j, err := cAPI.Job(name)
	if err != nil {
		check.errchan <- fmt.Errorf("Error retrieving Job(%s) from Registry: %v", name, err)
		return false
	}

	// send current state to the output channel
	check.statechan <- newJobState(name, j)

	// if test function
	if check.test(j) {
		close(check.statechan)
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"time"

	. "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-check"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/random"
)

func (s *S) TestFormationStreaming(c *C) {
	before := time.Now()
	release := s.createTestRelease(c, &ct.Release{})
	app := s.createTestApp(c, &ct.App{Name: "streamtest-existing"})
	s.createTestFormation(c, &ct.Formation{ReleaseID: release.ID, AppID: app.ID})

	updates := make(chan *ct.ExpandedFormation)
	streamCtrl, connectErr := s.c.StreamFormations(&before, updates)
	c.Assert(connectErr, IsNil)
	defer streamCtrl.Close()

	var existingFound bool
	for f := range updates {
		if f.App == nil {
			break
		}
		if f.Release.ID == release.ID {
			existingFound = true
		}
	}
	c.Assert(streamCtrl.Err(), IsNil)
	c.Assert(existingFound, Equals, true)

	release = s.createTestRelease(c, &ct.Release{
		Processes: map[string]ct.ProcessType{"foo": {}},
	})
	app = s.createTestApp(c, &ct.App{Name: "streamtest"})
	formation := s.createTestFormation(c, &ct.Formation{
		ReleaseID: release.ID,
		AppID:     app.ID,
		Processes: map[string]int{"foo": 1},
	})
	defer s.deleteTestFormation(formation)

	var out *ct.ExpandedFormation
	select {
	case out = <-updates:
	case <-time.After(time.Second):
		c.Fatal("timed out waiting for create")
	}
	c.Assert(streamCtrl.Err(), IsNil)
	c.Assert(out.Release, DeepEquals, release)
	c.Assert(out.App, DeepEquals, app)
	c.Assert(out.Processes, DeepEquals, formation.Processes)
	c.Assert(out.Artifact.CreatedAt, Not(IsNil))
	c.Assert(out.Artifact.ID, Equals, release.ArtifactID)

	c.Assert(s.c.DeleteFormation(app.ID, release.ID), IsNil)

	select {
	case out = <-updates:
	case <-time.After(time.Second):
		c.Fatal("timed out waiting for delete")
	}
	c.Assert(streamCtrl.Err(), IsNil)
	c.Assert(out.Release, DeepEquals, release)
	c.Assert(out.App, DeepEquals, app)
	c.Assert(out.Processes, IsNil)
}

func (s *S) TestFormationListActive(c *C) {
	app1 := s.createTestApp(c, &ct.App{})
	app2 := s.createTestApp(c, &ct.App{})
	artifact := s.createTestArtifact(c, &ct.Artifact{})

	createFormation := func(app *ct.App, procs map[string]int) *ct.ExpandedFormation {
		release := &ct.Release{
			ArtifactID: artifact.ID,
			Processes:  make(map[string]ct.ProcessType, len(procs)),
		}
		for typ := range procs {
			release.Processes[typ] = ct.ProcessType{}
		}
		s.createTestRelease(c, release)
		s.createTestFormation(c, &ct.Formation{
			AppID:     app.ID,
			ReleaseID: release.ID,
			Processes: procs,
		})
		return &ct.ExpandedFormation{
			App:       app,
			Release:   release,
			Artifact:  artifact,
			Processes: procs,
		}
	}

	formations := []*ct.ExpandedFormation{
		createFormation(app1, map[string]int{"web": 0}),
		createFormation(app1, map[string]int{"web": 1}),
		createFormation(app2, map[string]int{"web": 0, "worker": 0}),
		createFormation(app2, map[string]int{"web": 0, "worker": 1}),
		createFormation(app2, map[string]int{"web": 1, "worker": 2}),
	}

	list, err := s.c.FormationListActive()
	c.Assert(err, IsNil)
	c.Assert(list, HasLen, 3)

	// check that we only get the formations with a non-zero process count,
	// most recently updated first
	expected := []*ct.ExpandedFormation{formations[4], formations[3], formations[1]}
	for i, f := range expected {
		actual := list[i]
		c.Assert(actual.App.ID, Equals, f.App.ID)
		c.Assert(actual.Release.ID, Equals, f.Release.ID)
		c.Assert(actual.Artifact.ID, Equals, f.Artifact.ID)
		c.Assert(actual.Processes, DeepEquals, f.Processes)
	}
}

func (s *S) TestFormationStreamingInterrupted(c *C) {
	before := time.Now()
	appRepo := NewAppRepo(s.hc.db, os.Getenv("DEFAULT_ROUTE_DOMAIN"), s.hc.rc)
	releaseRepo := NewReleaseRepo(s.hc.db)
	artifactRepo := NewArtifactRepo(s.hc.db)
	formationRepo := NewFormationRepo(s.hc.db, appRepo, releaseRepo, artifactRepo)

	artifact := &ct.Artifact{Type: "docker", URI: fmt.Sprintf("https://example.com/%s", random.String(8))}
	c.Assert(artifactRepo.Add(artifact), IsNil)

	release := &ct.Release{ArtifactID: artifact.ID}
	c.Assert(releaseRepo.Add(release), IsNil)

	app := &ct.App{Name: "streamtest-interrupted"}
	c.Assert(appRepo.Add(app), IsNil)

	formation := &ct.Formation{ReleaseID: release.ID, AppID: app.ID}
	c.Assert(formationRepo.Add(formation), IsNil)

	ch := make(chan *ct.ExpandedFormation)
	updated := make(chan struct{})

	_, err := formationRepo.Subscribe(ch, before, updated)
	c.Assert(err, IsNil)

	// simulate scenario where we have not completed `sendUpdatedSince` but the channel for a subscription
	// is closed, by example due to an error listening on table `formations` that triggered `unsubscribeAll`.
	formationRepo.unsubscribeAll()

	// wait until `sendUpdateSince` finishes at which point it will not longer send to the (now closed) channel.
	<-updated
}
<commit_msg>controller: Don't allow TestFormationStreamingInterrupted to block<commit_after>package main

import (
	"fmt"
	"os"
	"time"

	. "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-check"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/random"
)

func (s *S) TestFormationStreaming(c *C) {
	before := time.Now()
	release := s.createTestRelease(c, &ct.Release{})
	app := s.createTestApp(c, &ct.App{Name: "streamtest-existing"})
	s.createTestFormation(c, &ct.Formation{ReleaseID: release.ID, AppID: app.ID})

	updates := make(chan *ct.ExpandedFormation)
	streamCtrl, connectErr := s.c.StreamFormations(&before, updates)
	c.Assert(connectErr, IsNil)
	defer streamCtrl.Close()

	var existingFound bool
	for f := range updates {
		if f.App == nil {
			break
		}
		if f.Release.ID == release.ID {
			existingFound = true
		}
	}
	c.Assert(streamCtrl.Err(), IsNil)
	c.Assert(existingFound, Equals, true)

	release = s.createTestRelease(c, &ct.Release{
		Processes: map[string]ct.ProcessType{"foo": {}},
	})
	app = s.createTestApp(c, &ct.App{Name: "streamtest"})
	formation := s.createTestFormation(c, &ct.Formation{
		ReleaseID: release.ID,
		AppID:     app.ID,
		Processes: map[string]int{"foo": 1},
	})
	defer s.deleteTestFormation(formation)

	var out *ct.ExpandedFormation
	select {
	case out = <-updates:
	case <-time.After(time.Second):
		c.Fatal("timed out waiting for create")
	}
	c.Assert(streamCtrl.Err(), IsNil)
	c.Assert(out.Release, DeepEquals, release)
	c.Assert(out.App, DeepEquals, app)
	c.Assert(out.Processes, DeepEquals, formation.Processes)
	c.Assert(out.Artifact.CreatedAt, Not(IsNil))
	c.Assert(out.Artifact.ID, Equals, release.ArtifactID)

	c.Assert(s.c.DeleteFormation(app.ID, release.ID), IsNil)

	select {
	case out = <-updates:
	case <-time.After(time.Second):
		c.Fatal("timed out waiting for delete")
	}
	c.Assert(streamCtrl.Err(), IsNil)
	c.Assert(out.Release, DeepEquals, release)
	c.Assert(out.App, DeepEquals, app)
	c.Assert(out.Processes, IsNil)
}

func (s *S) TestFormationListActive(c *C) {
	app1 := s.createTestApp(c, &ct.App{})
	app2 := s.createTestApp(c, &ct.App{})
	artifact := s.createTestArtifact(c, &ct.Artifact{})

	createFormation := func(app *ct.App, procs map[string]int) *ct.ExpandedFormation {
		release := &ct.Release{
			ArtifactID: artifact.ID,
			Processes:  make(map[string]ct.ProcessType, len(procs)),
		}
		for typ := range procs {
			release.Processes[typ] = ct.ProcessType{}
		}
		s.createTestRelease(c, release)
		s.createTestFormation(c, &ct.Formation{
			AppID:     app.ID,
			ReleaseID: release.ID,
			Processes: procs,
		})
		return &ct.ExpandedFormation{
			App:       app,
			Release:   release,
			Artifact:  artifact,
			Processes: procs,
		}
	}

	formations := []*ct.ExpandedFormation{
		createFormation(app1, map[string]int{"web": 0}),
		createFormation(app1, map[string]int{"web": 1}),
		createFormation(app2, map[string]int{"web": 0, "worker": 0}),
		createFormation(app2, map[string]int{"web": 0, "worker": 1}),
		createFormation(app2, map[string]int{"web": 1, "worker": 2}),
	}

	list, err := s.c.FormationListActive()
	c.Assert(err, IsNil)
	c.Assert(list, HasLen, 3)

	// check that we only get the formations with a non-zero process count,
	// most recently updated first
	expected := []*ct.ExpandedFormation{formations[4], formations[3], formations[1]}
	for i, f := range expected {
		actual := list[i]
		c.Assert(actual.App.ID, Equals, f.App.ID)
		c.Assert(actual.Release.ID, Equals, f.Release.ID)
		c.Assert(actual.Artifact.ID, Equals, f.Artifact.ID)
		c.Assert(actual.Processes, DeepEquals, f.Processes)
	}
}

func (s *S) TestFormationStreamingInterrupted(c *C) {
	before := time.Now()
	appRepo := NewAppRepo(s.hc.db, os.Getenv("DEFAULT_ROUTE_DOMAIN"), s.hc.rc)
	releaseRepo := NewReleaseRepo(s.hc.db)
	artifactRepo := NewArtifactRepo(s.hc.db)
	formationRepo := NewFormationRepo(s.hc.db, appRepo, releaseRepo, artifactRepo)

	artifact := &ct.Artifact{Type: "docker", URI: fmt.Sprintf("https://example.com/%s", random.String(8))}
	c.Assert(artifactRepo.Add(artifact), IsNil)

	release := &ct.Release{ArtifactID: artifact.ID}
	c.Assert(releaseRepo.Add(release), IsNil)

	app := &ct.App{Name: "streamtest-interrupted"}
	c.Assert(appRepo.Add(app), IsNil)

	formation := &ct.Formation{ReleaseID: release.ID, AppID: app.ID}
	c.Assert(formationRepo.Add(formation), IsNil)

	ch := make(chan *ct.ExpandedFormation)
	updated := make(chan struct{})

	_, err := formationRepo.Subscribe(ch, before, updated)
	c.Assert(err, IsNil)

	// simulate scenario where we have not completed `sendUpdatedSince` but the channel for a subscription
	// is closed, by example due to an error listening on table `formations` that triggered `unsubscribeAll`.
	formationRepo.unsubscribeAll()

	// wait until `sendUpdateSince` finishes at which point it will not longer send to the (now closed) channel.
	select {
	case <-updated:
	case <-time.After(5 * time.Second):
		c.Fatal("timed out waiting for sendUpdatedSince to finish")
	}
}
<|endoftext|>


<commit_before>package shared

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"log"
	"io"
	"github.com/aws/aws-sdk-go/private/waiter"
)

type AllocatedVolume struct {
	VolumeId   string
	DeviceName string
	InstanceId string
	EC2        *ec2.EC2
}

func NewAllocatedVolume(volumeId string, deviceName string, instanceId string, EC2 *ec2.EC2) *AllocatedVolume {

	return &AllocatedVolume{VolumeId: volumeId, DeviceName: deviceName, InstanceId: instanceId, EC2: EC2}
}

func (volume AllocatedVolume) Attach() error {

	opts := &ec2.AttachVolumeInput{
		Device:     aws.String(volume.DeviceName),
		InstanceId: aws.String(volume.InstanceId),
		VolumeId:   aws.String(volume.VolumeId),
	}

	log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", volume.VolumeId, volume.InstanceId)

	if _, err := volume.EC2.AttachVolume(opts); err != nil {

		if awsErr, ok := err.(awserr.Error); ok {
			return fmt.Errorf("Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
				volume.VolumeId, volume.InstanceId, awsErr.Message(), awsErr.Code())
		}

	} else {

		err := volume.waitUntilVolumeAttached()

		if err != nil {
			return fmt.Errorf("Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
				volume.VolumeId, volume.InstanceId, err)
		}
	}

	return nil

}

func (volume AllocatedVolume) Info(w io.Writer) {
	fmt.Fprintf(w,"Instance ID %s, Device Name %s, Volume ID %s\n",volume.InstanceId,volume.DeviceName, volume.VolumeId)
}

func (volume AllocatedVolume) describeVolumesInput() *ec2.DescribeVolumesInput {
	return &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(volume.VolumeId)},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(volume.InstanceId)},
			},
		},
	}
}

// waitUntilVolumeAttached uses the Amazon EC2 API operation
// DescribeVolumes to wait for a condition to be met before returning.
// If the condition is not meet within the max attempt window an error will
// be returned.
func (volume AllocatedVolume) waitUntilVolumeAttached() error {

	input := volume.describeVolumesInput()

	waiterCfg := waiter.Config{
		Operation:   "DescribeVolumes",
		Delay:       15,
		MaxAttempts: 40,
		Acceptors: []waiter.WaitAcceptor{
			{
				State:    "success",
				Matcher:  "pathAll",
				Argument: "Volumes[].Attachments[].State",
				Expected: "attached",
			},
			{
				State:    "failure",
				Matcher:  "pathAny",
				Argument: "Volumes[].Attachments[].State",
				Expected: "detached",
			},
		},
	}

	w := waiter.Waiter{
		Client: volume.EC2,
		Input:  input,
		Config: waiterCfg,
	}
	return w.Wait()
}<commit_msg>Use of AWS constants<commit_after>package shared

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"log"
	"io"
	"github.com/aws/aws-sdk-go/private/waiter"
)

type AllocatedVolume struct {
	VolumeId   string
	DeviceName string
	InstanceId string
	EC2        *ec2.EC2
}

func NewAllocatedVolume(volumeId string, deviceName string, instanceId string, EC2 *ec2.EC2) *AllocatedVolume {

	return &AllocatedVolume{VolumeId: volumeId, DeviceName: deviceName, InstanceId: instanceId, EC2: EC2}
}

func (volume AllocatedVolume) Attach() error {

	opts := &ec2.AttachVolumeInput{
		Device:     aws.String(volume.DeviceName),
		InstanceId: aws.String(volume.InstanceId),
		VolumeId:   aws.String(volume.VolumeId),
	}

	log.Printf("[DEBUG] Attaching Volume (%s) to Instance (%s)", volume.VolumeId, volume.InstanceId)

	if _, err := volume.EC2.AttachVolume(opts); err != nil {

		if awsErr, ok := err.(awserr.Error); ok {
			return fmt.Errorf("Error attaching volume (%s) to instance (%s), message: \"%s\", code: \"%s\"",
				volume.VolumeId, volume.InstanceId, awsErr.Message(), awsErr.Code())
		}

	} else {

		err := volume.waitUntilVolumeAttached()

		if err != nil {
			return fmt.Errorf("Error waiting for Volume (%s) to attach to Instance: %s, error: %s",
				volume.VolumeId, volume.InstanceId, err)
		}
	}

	return nil

}

func (volume AllocatedVolume) Info(w io.Writer) {
	fmt.Fprintf(w,"Instance ID %s, Device Name %s, Volume ID %s\n",volume.InstanceId,volume.DeviceName, volume.VolumeId)
}

func (volume AllocatedVolume) describeVolumesInput() *ec2.DescribeVolumesInput {
	return &ec2.DescribeVolumesInput{
		VolumeIds: []*string{aws.String(volume.VolumeId)},
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("attachment.instance-id"),
				Values: []*string{aws.String(volume.InstanceId)},
			},
		},
	}
}

// waitUntilVolumeAttached uses the Amazon EC2 API operation
// DescribeVolumes to wait for a condition to be met before returning.
// If the condition is not meet within the max attempt window an error will
// be returned.
func (volume AllocatedVolume) waitUntilVolumeAttached() error {

	input := volume.describeVolumesInput()

	waiterCfg := waiter.Config{
		Operation:   "DescribeVolumes",
		Delay:       15,
		MaxAttempts: 40,
		Acceptors: []waiter.WaitAcceptor{
			{
				State:    "success",
				Matcher:  "pathAll",
				Argument: "Volumes[].Attachments[].State",
				Expected: ec2.AttachmentStatusAttached,
			},
			{
				State:    "failure",
				Matcher:  "pathAny",
				Argument: "Volumes[].Attachments[].State",
				Expected: ec2.AttachmentStatusDetached,
			},
		},
	}

	w := waiter.Waiter{
		Client: volume.EC2,
		Input:  input,
		Config: waiterCfg,
	}
	return w.Wait()
}<|endoftext|>


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

// Binary shipshape is a command line interface to shipshape.
// It (optionally) pulls a docker container, runs it,
// and runs the analysis service with the specified local
// files and configuration.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"shipshape/util/docker"
	glog "third_party/go-glog"
	"third_party/kythe/go/rpc/client"

	"github.com/golang/protobuf/proto"

	notepb "shipshape/proto/note_proto"
	ctxpb "shipshape/proto/shipshape_context_proto"
	rpcpb "shipshape/proto/shipshape_rpc_proto"
)

var (
	tag     = flag.String("tag", "prod", "Tag to use for the analysis service image")
	local   = flag.Bool("try_local", false, "True if we should use the local copy of this image, and pull only if it doesn't exist. False will always pull.")
	streams = flag.Bool("streams", false, "True if we should run in streams mode, false if we should run as a service.")

	event      = flag.String("event", "manual", "The name of the event to use")
	categories = flag.String("categories", "", "Categories to trigger (comma-separated). If none are specified, will use the .shipshape configuration file to decide which categories to run.")
	stayUp     = flag.Bool("stay_up", false, "True if we should keep the container running for debugging purposes, false if we should stop and remove it.")
	repo       = flag.String("repo", "gcr.io/_b_shipshape_registry", "The name of the docker repo to use")
	kytheRepo  = flag.String("kytheRepo", "gcr.io/kythe_repo", "The name of the docker repo to use")
	// TODO(ciera): use the analyzer images
	//analyzerImages  = flag.String("analyzer_images", "", "Full docker path to images of external analyzers to use (comma-separated)")
	jsonOutput = flag.String("json_output", "", "When specified, log shipshape results to provided .json file")
	build      = flag.String("build", "", "The name of the build system to use to generate compilation units. If empty, will not run the compilation step. Options are maven and go.")
)

const (
	workspace = "/shipshape-workspace"
	logsDir   = "/shipshape-output"
	localLogs = "/tmp"
	image     = "service"
)

func logMessage(msg *rpcpb.ShipshapeResponse) error {
	if *jsonOutput == "" {
		fileNotes := make(map[string][]*notepb.Note)
		for _, analysis := range msg.AnalyzeResponse {
			for _, failure := range analysis.Failure {
				fmt.Printf("WARNING: Analyzer %s failed to run: %s\n", *failure.Category, *failure.FailureMessage)
			}
			for _, note := range analysis.Note {
				path := ""
				if note.Location != nil {
					path = note.Location.GetPath()
				}
				fileNotes[path] = append(fileNotes[path], note)
			}
		}

		// TODO(ciera): these results aren't sorted. They should be sorted by path and start line
		for path, notes := range fileNotes {
			if path != "" {
				fmt.Println(path)
			} else {
				fmt.Println("Global")
			}
			for _, note := range notes {
				loc := ""
				subCat := ""
				if note.Subcategory != nil {
					subCat = ":" + *note.Subcategory
				}
				if note.GetLocation().Range != nil && note.GetLocation().GetRange().StartLine != nil {
					if note.GetLocation().GetRange().StartColumn != nil {
						loc = fmt.Sprintf("Line %d, Col %d ", *note.Location.Range.StartLine, *note.Location.Range.StartColumn)
					} else {
						loc = fmt.Sprintf("Line %d ", *note.Location.Range.StartLine)
					}
				}

				fmt.Printf("%s[%s%s]\n", loc, *note.Category, subCat)
				fmt.Printf("\t%s\n", *note.Description)
			}
			fmt.Println()
		}
		return nil
	}

	b, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(*jsonOutput, b, 0644)
}

func main() {
	flag.Parse()

	// Get the directory to analyze.
	if len(flag.Args()) != 1 {
		glog.Fatal("Usage: shipshape [OPTIONS] <directory>")
	}

	dir := flag.Arg(0)
	if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
		glog.Fatalf("%s is not a valid directory", dir)
	}

	absRoot, err := filepath.Abs(dir)
	if err != nil {
		glog.Fatalf("Could not get absolute path for %s: %v", dir, err)
	}

	res := docker.Authenticate()
	if res.Err != nil {
		glog.Infoln(strings.TrimSpace(res.Stderr))
		glog.Fatalf("Could not authenticate: %v", res.Err)
	}
	glog.Infoln(strings.TrimSpace(res.Stdout))

	image := docker.FullImageName(*repo, image, *tag)
	glog.Infof("Starting shipshape using %s on %s", image, absRoot)

	// Create the request

	var trigger []string
	if *categories != "" {
		trigger = strings.Split(*categories, ",")
	} else {
		glog.Infof("No categories provided. Will be using categories specified by the config file for the event %s", *event)
	}

	req := &rpcpb.ShipshapeRequest{
		TriggeredCategory: trigger,
		ShipshapeContext: &ctxpb.ShipshapeContext{
			RepoRoot: proto.String(workspace),
		},
		Event: proto.String(*event),
		Stage: ctxpb.Stage_PRE_BUILD.Enum(),
	}

	// If necessary, pull it
	// If local is true it doesn't meant that docker won't pull it, it will just
	// look locally first.
	if !*local {
		pull(image)
	}

	// Put in this defer before calling run. Even if run fails, it can
	// still create the container.
	if !*stayUp {
		defer stop("shipping_container")
	}

	var c *client.Client

	// Run it on files
	if *streams {
		err = streamsAnalyze(image, absRoot, req)
		if err != nil {
			glog.Errorf("Error making stream call: %v", err)
			return
		}
	} else {
		c, err = startShipshapeService(image, absRoot)
		if err != nil {
			glog.Errorf("HTTP client did not become healthy: %v", err)
			return
		}
		err = serviceAnalyze(c, req)
		if err != nil {
			glog.Errorf("Error making service call: %v", err)
			return
		}
	}

	// If desired, generate compilation units with a kythe image
	if *build != "" {
		// TODO(ciera): handle campfire as an option
		kytheImage := docker.FullImageName(*kytheRepo, "kythe", "latest")
		if !*local {
			pull(kytheImage)
		}

		defer stop("kythe")
		glog.Infof("Retrieving compilation units with %s", *build)
		volumeMap := map[string]string{
			filepath.Join(absRoot, "compilations"): "/compilations",
			absRoot: "/repo",
		}
		home := os.Getenv("HOME")
		if len(home) > 0 {
			volumeMap[filepath.Join(home, ".m2")] = "/root/.m2"
		} else {
			glog.Infof("$HOME is not set. Not using .m2 mapping")
		}

		// TODO(ciera): Can I pass in more than one extractor?
		// TODO(ciera): Can we exclude files in the .shipshape ignore path?
		// TODO(ciera): Can we use the same command for campfire extraction?
		result := docker.RunAttached(kytheImage, "kythe", nil, volumeMap, nil, nil, nil, []string{"--extract", *build})
		if result.Err != nil {
			// kythe spews output, so only capture it if something went wrong.
			glog.Infoln(strings.TrimSpace(result.Stdout))
			glog.Infoln(strings.TrimSpace(result.Stderr))
			glog.Errorf("Error from run: %v", result.Err)
			return
		}
		glog.Infoln("CompilationUnits prepared")

		req.Stage = ctxpb.Stage_POST_BUILD.Enum()
		if !*streams {
			err = serviceAnalyze(c, req)
			if err != nil {
				glog.Errorf("Error making service call: %v", err)
				return
			}
		} else {
			err = streamsAnalyze(absRoot, req)
			if err != nil {
				glog.Errorf("Error making stream call: %v", err)
				return
			}
		}
	}

	glog.Infoln("End of Results.")
}

func startShipshapeService(image, absRoot string) (*client.Client, error) {
	volumeMap := map[string]string{absRoot: workspace, localLogs: logsDir}
	glog.Infof("Running image %s in service mode", image)
	environment := map[string]string{"START_SERVICE": "true"}
	result := docker.Run(image, "shipping_container", map[int]int{10007: 10007}, volumeMap, nil, environment, nil)
	glog.Infoln(strings.TrimSpace(result.Stdout))
	glog.Infoln(strings.TrimSpace(result.Stderr))
	if result.Err != nil {
		return nil, result.Err
	}
	c := client.NewHTTPClient("localhost:10007")
	return c, c.WaitUntilReady(10 * time.Second)
}

func serviceAnalyze(c *client.Client, req *rpcpb.ShipshapeRequest) error {
	glog.Infof("Calling to the shipshape service with %v", req)
	rd := c.Stream("/ShipshapeService/Run", req)
	defer rd.Close()
	for {
		var msg rpcpb.ShipshapeResponse
		if err := rd.NextResult(&msg); err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("received an error from calling run: %v", err.Error())
		}

		if err := logMessage(&msg); err != nil {
			return fmt.Errorf("could not parse results: %v", err.Error())
		}
	}
	return nil
}

func streamsAnalyze(image, absRoot string, req *rpcpb.ShipshapeRequest) error {
	volumeMap := map[string]string{absRoot: workspace, localLogs: logsDir}
	glog.Infof("Running image %s in stream mode", image)
	reqBytes, err := proto.Marshal(req)
	if err != nil {
		return fmt.Errorf("error marshalling %v: %v", req, err)
	}

	result := docker.RunAttached(image, "shipping_container", map[int]int{10007: 10007}, volumeMap, nil, nil, reqBytes, nil)
	glog.Infoln(strings.TrimSpace(result.Stderr))

	if result.Err != nil {
		return fmt.Errorf("error from run: %v", result.Err)
	}
	var msg rpcpb.ShipshapeResponse
	if err := proto.Unmarshal([]byte(result.Stdout), &msg); err != nil {
		return fmt.Errorf("unexpected ShipshapeResponse %v", err)
	}
	return logMessage(&msg)
}

func pull(image string) {
	glog.Infof("Pulling image %s", image)
	result := docker.Pull(image)
	glog.Infoln(strings.TrimSpace(result.Stdout))
	glog.Infoln(strings.TrimSpace(result.Stderr))
	if result.Err != nil {
		glog.Errorf("Error from pull: %v", result.Err)
		return
	}
	glog.Infoln("Pulling complete")
}

func stop(container string) {
	glog.Infof("Stopping and removing %s", container)
	result := docker.Stop(container, true)
	glog.Infoln(strings.TrimSpace(result.Stdout))
	glog.Infoln(strings.TrimSpace(result.Stderr))
	if result.Err != nil {
		glog.Infof("Could not stop %s: %v", container, result.Err)
	} else {
		glog.Infoln("Removed.")
	}
}
<commit_msg>Fix build error, forgot the second call<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Binary shipshape is a command line interface to shipshape.
// It (optionally) pulls a docker container, runs it,
// and runs the analysis service with the specified local
// files and configuration.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"shipshape/util/docker"
	glog "third_party/go-glog"
	"third_party/kythe/go/rpc/client"

	"github.com/golang/protobuf/proto"

	notepb "shipshape/proto/note_proto"
	ctxpb "shipshape/proto/shipshape_context_proto"
	rpcpb "shipshape/proto/shipshape_rpc_proto"
)

var (
	tag     = flag.String("tag", "prod", "Tag to use for the analysis service image")
	local   = flag.Bool("try_local", false, "True if we should use the local copy of this image, and pull only if it doesn't exist. False will always pull.")
	streams = flag.Bool("streams", false, "True if we should run in streams mode, false if we should run as a service.")

	event      = flag.String("event", "manual", "The name of the event to use")
	categories = flag.String("categories", "", "Categories to trigger (comma-separated). If none are specified, will use the .shipshape configuration file to decide which categories to run.")
	stayUp     = flag.Bool("stay_up", false, "True if we should keep the container running for debugging purposes, false if we should stop and remove it.")
	repo       = flag.String("repo", "gcr.io/_b_shipshape_registry", "The name of the docker repo to use")
	kytheRepo  = flag.String("kytheRepo", "gcr.io/kythe_repo", "The name of the docker repo to use")
	// TODO(ciera): use the analyzer images
	//analyzerImages  = flag.String("analyzer_images", "", "Full docker path to images of external analyzers to use (comma-separated)")
	jsonOutput = flag.String("json_output", "", "When specified, log shipshape results to provided .json file")
	build      = flag.String("build", "", "The name of the build system to use to generate compilation units. If empty, will not run the compilation step. Options are maven and go.")
)

const (
	workspace = "/shipshape-workspace"
	logsDir   = "/shipshape-output"
	localLogs = "/tmp"
	image     = "service"
)

func logMessage(msg *rpcpb.ShipshapeResponse) error {
	if *jsonOutput == "" {
		fileNotes := make(map[string][]*notepb.Note)
		for _, analysis := range msg.AnalyzeResponse {
			for _, failure := range analysis.Failure {
				fmt.Printf("WARNING: Analyzer %s failed to run: %s\n", *failure.Category, *failure.FailureMessage)
			}
			for _, note := range analysis.Note {
				path := ""
				if note.Location != nil {
					path = note.Location.GetPath()
				}
				fileNotes[path] = append(fileNotes[path], note)
			}
		}

		// TODO(ciera): these results aren't sorted. They should be sorted by path and start line
		for path, notes := range fileNotes {
			if path != "" {
				fmt.Println(path)
			} else {
				fmt.Println("Global")
			}
			for _, note := range notes {
				loc := ""
				subCat := ""
				if note.Subcategory != nil {
					subCat = ":" + *note.Subcategory
				}
				if note.GetLocation().Range != nil && note.GetLocation().GetRange().StartLine != nil {
					if note.GetLocation().GetRange().StartColumn != nil {
						loc = fmt.Sprintf("Line %d, Col %d ", *note.Location.Range.StartLine, *note.Location.Range.StartColumn)
					} else {
						loc = fmt.Sprintf("Line %d ", *note.Location.Range.StartLine)
					}
				}

				fmt.Printf("%s[%s%s]\n", loc, *note.Category, subCat)
				fmt.Printf("\t%s\n", *note.Description)
			}
			fmt.Println()
		}
		return nil
	}

	b, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(*jsonOutput, b, 0644)
}

func main() {
	flag.Parse()

	// Get the directory to analyze.
	if len(flag.Args()) != 1 {
		glog.Fatal("Usage: shipshape [OPTIONS] <directory>")
	}

	dir := flag.Arg(0)
	if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
		glog.Fatalf("%s is not a valid directory", dir)
	}

	absRoot, err := filepath.Abs(dir)
	if err != nil {
		glog.Fatalf("Could not get absolute path for %s: %v", dir, err)
	}

	res := docker.Authenticate()
	if res.Err != nil {
		glog.Infoln(strings.TrimSpace(res.Stderr))
		glog.Fatalf("Could not authenticate: %v", res.Err)
	}
	glog.Infoln(strings.TrimSpace(res.Stdout))

	image := docker.FullImageName(*repo, image, *tag)
	glog.Infof("Starting shipshape using %s on %s", image, absRoot)

	// Create the request

	var trigger []string
	if *categories != "" {
		trigger = strings.Split(*categories, ",")
	} else {
		glog.Infof("No categories provided. Will be using categories specified by the config file for the event %s", *event)
	}

	req := &rpcpb.ShipshapeRequest{
		TriggeredCategory: trigger,
		ShipshapeContext: &ctxpb.ShipshapeContext{
			RepoRoot: proto.String(workspace),
		},
		Event: proto.String(*event),
		Stage: ctxpb.Stage_PRE_BUILD.Enum(),
	}

	// If necessary, pull it
	// If local is true it doesn't meant that docker won't pull it, it will just
	// look locally first.
	if !*local {
		pull(image)
	}

	// Put in this defer before calling run. Even if run fails, it can
	// still create the container.
	if !*stayUp {
		defer stop("shipping_container")
	}

	var c *client.Client

	// Run it on files
	if *streams {
		err = streamsAnalyze(image, absRoot, req)
		if err != nil {
			glog.Errorf("Error making stream call: %v", err)
			return
		}
	} else {
		c, err = startShipshapeService(image, absRoot)
		if err != nil {
			glog.Errorf("HTTP client did not become healthy: %v", err)
			return
		}
		err = serviceAnalyze(c, req)
		if err != nil {
			glog.Errorf("Error making service call: %v", err)
			return
		}
	}

	// If desired, generate compilation units with a kythe image
	if *build != "" {
		// TODO(ciera): handle campfire as an option
		kytheImage := docker.FullImageName(*kytheRepo, "kythe", "latest")
		if !*local {
			pull(kytheImage)
		}

		defer stop("kythe")
		glog.Infof("Retrieving compilation units with %s", *build)
		volumeMap := map[string]string{
			filepath.Join(absRoot, "compilations"): "/compilations",
			absRoot: "/repo",
		}
		home := os.Getenv("HOME")
		if len(home) > 0 {
			volumeMap[filepath.Join(home, ".m2")] = "/root/.m2"
		} else {
			glog.Infof("$HOME is not set. Not using .m2 mapping")
		}

		// TODO(ciera): Can I pass in more than one extractor?
		// TODO(ciera): Can we exclude files in the .shipshape ignore path?
		// TODO(ciera): Can we use the same command for campfire extraction?
		result := docker.RunAttached(kytheImage, "kythe", nil, volumeMap, nil, nil, nil, []string{"--extract", *build})
		if result.Err != nil {
			// kythe spews output, so only capture it if something went wrong.
			glog.Infoln(strings.TrimSpace(result.Stdout))
			glog.Infoln(strings.TrimSpace(result.Stderr))
			glog.Errorf("Error from run: %v", result.Err)
			return
		}
		glog.Infoln("CompilationUnits prepared")

		req.Stage = ctxpb.Stage_POST_BUILD.Enum()
		if !*streams {
			err = serviceAnalyze(c, req)
			if err != nil {
				glog.Errorf("Error making service call: %v", err)
				return
			}
		} else {
			err = streamsAnalyze(image, absRoot, req)
			if err != nil {
				glog.Errorf("Error making stream call: %v", err)
				return
			}
		}
	}

	glog.Infoln("End of Results.")
}

func startShipshapeService(image, absRoot string) (*client.Client, error) {
	volumeMap := map[string]string{absRoot: workspace, localLogs: logsDir}
	glog.Infof("Running image %s in service mode", image)
	environment := map[string]string{"START_SERVICE": "true"}
	result := docker.Run(image, "shipping_container", map[int]int{10007: 10007}, volumeMap, nil, environment, nil)
	glog.Infoln(strings.TrimSpace(result.Stdout))
	glog.Infoln(strings.TrimSpace(result.Stderr))
	if result.Err != nil {
		return nil, result.Err
	}
	c := client.NewHTTPClient("localhost:10007")
	return c, c.WaitUntilReady(10 * time.Second)
}

func serviceAnalyze(c *client.Client, req *rpcpb.ShipshapeRequest) error {
	glog.Infof("Calling to the shipshape service with %v", req)
	rd := c.Stream("/ShipshapeService/Run", req)
	defer rd.Close()
	for {
		var msg rpcpb.ShipshapeResponse
		if err := rd.NextResult(&msg); err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("received an error from calling run: %v", err.Error())
		}

		if err := logMessage(&msg); err != nil {
			return fmt.Errorf("could not parse results: %v", err.Error())
		}
	}
	return nil
}

func streamsAnalyze(image, absRoot string, req *rpcpb.ShipshapeRequest) error {
	volumeMap := map[string]string{absRoot: workspace, localLogs: logsDir}
	glog.Infof("Running image %s in stream mode", image)
	reqBytes, err := proto.Marshal(req)
	if err != nil {
		return fmt.Errorf("error marshalling %v: %v", req, err)
	}

	result := docker.RunAttached(image, "shipping_container", map[int]int{10007: 10007}, volumeMap, nil, nil, reqBytes, nil)
	glog.Infoln(strings.TrimSpace(result.Stderr))

	if result.Err != nil {
		return fmt.Errorf("error from run: %v", result.Err)
	}
	var msg rpcpb.ShipshapeResponse
	if err := proto.Unmarshal([]byte(result.Stdout), &msg); err != nil {
		return fmt.Errorf("unexpected ShipshapeResponse %v", err)
	}
	return logMessage(&msg)
}

func pull(image string) {
	glog.Infof("Pulling image %s", image)
	result := docker.Pull(image)
	glog.Infoln(strings.TrimSpace(result.Stdout))
	glog.Infoln(strings.TrimSpace(result.Stderr))
	if result.Err != nil {
		glog.Errorf("Error from pull: %v", result.Err)
		return
	}
	glog.Infoln("Pulling complete")
}

func stop(container string) {
	glog.Infof("Stopping and removing %s", container)
	result := docker.Stop(container, true)
	glog.Infoln(strings.TrimSpace(result.Stdout))
	glog.Infoln(strings.TrimSpace(result.Stderr))
	if result.Err != nil {
		glog.Infof("Could not stop %s: %v", container, result.Err)
	} else {
		glog.Infoln("Removed.")
	}
}
<|endoftext|>


<commit_before>/*
 *  Bulldozer Framework
 *  Copyright (C) DesertBit
 */

package controlpanel

import (
	ht "html/template"

	"github.com/desertbit/bulldozer/auth"
	"github.com/desertbit/bulldozer/mux"
	"github.com/desertbit/bulldozer/sessions"
	"github.com/desertbit/bulldozer/template"
	"github.com/desertbit/bulldozer/templates"
	"github.com/desertbit/bulldozer/webcrawler"

	"fmt"
	"strings"
)

const (
	PageUrl = "/controlpanel"

	templateName = "bud/controlpanel/controlpanel"
)

//##############//
//### Public ###//
//##############//

// Init initializes this package. This is managed by the bulldozer main package.
func Init() (err error) {
	// Disallow the control panel page to be indexed.
	webcrawler.AddDisallowedPath(PageUrl + "/")

	// Obtain the control center template.
	t := templates.Templates.Lookup(templateName)
	if t == nil {
		return fmt.Errorf("failed to lookup control center template!")
	}

	// Set the custom ID.
	t.SetStaticDomID("bud-ctrl")

	// Add the control panel routes.
	mux.Route(PageUrl, routePage)
	mux.Route(PageUrl+"/*", routePage)

	return nil
}

// IsCurrentPage returns a boolean whenever the control panel
// is the current page.
func IsCurrentPage(s *sessions.Session) bool {
	return strings.HasPrefix(s.CurrentPath(), PageUrl)
}

//###############//
//### Private ###//
//###############//

type item struct {
	Url      string
	Title    string
	Icon     string
	IsActive bool
}

func routePage(s *sessions.Session, req *mux.Request) {
	// Get the user
	u := auth.GetUser(s)
	if u == nil {
		req.Error(fmt.Errorf("control panel: access not allowed"))
		return
	}

	// Get the requested control panel page ID.
	id := req.RouteData.RestPath
	if len(id) == 0 {
		if len(pages) == 0 {
			req.Error(fmt.Errorf("control panel: no pages"))
			return
		}

		// If no ID is set, then use the first page as default page.
		id = pages[0].ID
	}

	// The current page pointer.
	var currentPage *Page

	// Create a item slice of all items which the user has access to.
	var items []item
	var isActive bool
	for _, page := range pages {
		// Set the active flag if this is the current active item.
		// Also set the current page pointer.
		isActive = false
		if page.ID == id {
			isActive = true
			currentPage = page
		}

		// Skip if the user has no access permission.
		if !u.IsInGroup(page.AuthGroups...) {
			continue
		}

		// Create a new item.
		i := item{
			Url:      PageUrl + "/" + page.ID,
			Title:    page.Title,
			Icon:     page.Icon,
			IsActive: isActive,
		}

		items = append(items, i)
	}

	// Create the page render data.
	data := struct {
		Items        []item
		Body         ht.HTML
		CurrentTitle string
	}{
		Items: items,
	}

	// Śhow the not found error page if the current page is nil.
	if currentPage == nil {
		req.NotFoundError()
		return
	}

	// Check if the user is allowed to access the current page.
	if !u.IsInGroup(currentPage.AuthGroups...) {
		req.Error(fmt.Errorf("control panel: access not allowed"))
		return
	}

	// Check if the template is not nil.
	if currentPage.Template == nil {
		req.Error(fmt.Errorf("control panel: client requested the control panel page with ID '%s', but the template is nil!", id))
		return
	}

	// Execute the control panel page template.
	body, _, err := currentPage.Template.ExecuteToString(s)
	if err != nil {
		req.Error(fmt.Errorf("control panel: client requested the control panel page with ID '%s', but the template execution failed: %v", id, err))
		return
	}

	// Unescape the body.
	data.Body = ht.HTML(body)

	// Set the control panel and page title.
	req.Title = currentPage.Title
	data.CurrentTitle = currentPage.Title

	// Create the template execute options.
	opts := template.ExecOpts{
		Data: data,
	}

	// Execute the template page.
	req.Body, _, _, err = templates.Templates.ExecuteTemplateToString(s, templateName, opts)
	if err != nil {
		req.Error(err)
	}
}
<commit_msg>improved control panel page handling<commit_after>/*
 *  Bulldozer Framework
 *  Copyright (C) DesertBit
 */

package controlpanel

import (
	ht "html/template"

	"github.com/desertbit/bulldozer/auth"
	"github.com/desertbit/bulldozer/mux"
	"github.com/desertbit/bulldozer/sessions"
	"github.com/desertbit/bulldozer/template"
	"github.com/desertbit/bulldozer/templates"
	"github.com/desertbit/bulldozer/webcrawler"

	"fmt"
	"strings"
)

const (
	PageUrl = "/controlpanel"

	templateName = "bud/controlpanel/controlpanel"
)

//##############//
//### Public ###//
//##############//

// Init initializes this package. This is managed by the bulldozer main package.
func Init() (err error) {
	// Disallow the control panel page to be indexed.
	webcrawler.AddDisallowedPath(PageUrl + "/")

	// Obtain the control center template.
	t := templates.Templates.Lookup(templateName)
	if t == nil {
		return fmt.Errorf("failed to lookup control center template!")
	}

	// Set the custom ID.
	t.SetStaticDomID("bud-ctrl")

	// Add the control panel routes.
	mux.Route(PageUrl, routePage)
	mux.Route(PageUrl+"/*", routePage)

	return nil
}

// IsCurrentPage returns a boolean whenever the control panel
// is the current page.
func IsCurrentPage(s *sessions.Session) bool {
	return strings.HasPrefix(s.CurrentPath(), PageUrl)
}

//###############//
//### Private ###//
//###############//

type item struct {
	Url      string
	Title    string
	Icon     string
	IsActive bool
}

func routePage(s *sessions.Session, req *mux.Request) {
	// Get the user
	u := auth.GetUser(s)
	if u == nil {
		req.Error(fmt.Errorf("control panel: access not allowed"))
		return
	}

	// Get the requested control panel page ID.
	id := req.RouteData.RestPath

	// The current page pointer.
	var currentPage *Page

	// Create a item slice of all items which the user has access to.
	var items []item
	var isActive bool
	for _, page := range pages {
		// Skip if the user has no access permission.
		if !u.IsInGroup(page.AuthGroups...) {
			continue
		}

		// If no ID is set, then use the first page as default page.
		if len(id) == 0 {
			id = page.ID
		}

		// Set the active flag if this is the current active item.
		// Also set the current page pointer.
		isActive = false
		if page.ID == id {
			isActive = true
			currentPage = page
		}

		// Create a new item.
		i := item{
			Url:      PageUrl + "/" + page.ID,
			Title:    page.Title,
			Icon:     page.Icon,
			IsActive: isActive,
		}

		items = append(items, i)
	}

	// Check if any pages where found for the user.
	if len(items) == 0 {
		req.Error(fmt.Errorf("control panel: no pages"))
		return
	}

	// Create the page render data.
	data := struct {
		Items        []item
		Body         ht.HTML
		CurrentTitle string
	}{
		Items: items,
	}

	// Śhow the not found error page if the current page is nil.
	if currentPage == nil {
		req.NotFoundError()
		return
	}

	// Check if the user is allowed to access the current page.
	if !u.IsInGroup(currentPage.AuthGroups...) {
		req.Error(fmt.Errorf("control panel: access not allowed"))
		return
	}

	// Check if the template is not nil.
	if currentPage.Template == nil {
		req.Error(fmt.Errorf("control panel: client requested the control panel page with ID '%s', but the template is nil!", id))
		return
	}

	// Execute the control panel page template.
	body, _, err := currentPage.Template.ExecuteToString(s)
	if err != nil {
		req.Error(fmt.Errorf("control panel: client requested the control panel page with ID '%s', but the template execution failed: %v", id, err))
		return
	}

	// Unescape the body.
	data.Body = ht.HTML(body)

	// Set the control panel and page title.
	req.Title = currentPage.Title
	data.CurrentTitle = currentPage.Title

	// Create the template execute options.
	opts := template.ExecOpts{
		Data: data,
	}

	// Execute the template page.
	req.Body, _, _, err = templates.Templates.ExecuteTemplateToString(s, templateName, opts)
	if err != nil {
		req.Error(err)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package indexers

import (
	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/database"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/gcs/builder"
)

const (
	// cfIndexName is the human-readable name for the index.
	cfIndexName = "committed filter index"
)

// Committed filters come in two flavours: basic and extended. They are
// generated and dropped in pairs, and both are indexed by a block's hash.
// Besides holding different content, they also live in different buckets.
var (
	// cfBasicIndexKey is the name of the db bucket used to house the
	// block hash -> Basic cf index (cf#0).
	cfBasicIndexKey = []byte("cf0byhashidx")
	// cfExtendedIndexKey is the name of the db bucket used to house the
	// block hash -> Extended cf index (cf#1).
	cfExtendedIndexKey = []byte("cf1byhashidx")
)

// dbFetchBasicEntry() retrieves a block's basic filter. An entry's absence is
// not considered an error. The filter is returned serialized.
func dbFetchBasicEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Get(h[:]), nil
}

// dbFetchExtendedEntry() retrieves a block's extended filter. An entry's
// absence is not considered an error. The filter is returned serialized.
func dbFetchExtendedEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Get(h[:]), nil
}

// dbStoreBasicEntry() stores a block's basic filter.
func dbStoreBasicEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Put(h[:], f)
}

// dbStoreBasicEntry() stores a block's extended filter.
func dbStoreExtendedEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Put(h[:], f)
}

// dbDeleteBasicEntry() deletes a block's basic filter.
func dbDeleteBasicEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Delete(h[:])
}

// dbDeleteExtendedEntry() deletes a block's extended filter.
func dbDeleteExtendedEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Delete(h[:])
}

// CfIndex implements a committed filter (cf) by hash index.
type CfIndex struct {
	db database.DB
}

// Ensure the CfIndex type implements the Indexer interface.
var _ Indexer = (*CfIndex)(nil)

// Init initializes the hash-based cf index. This is part of the Indexer
// interface.
func (idx *CfIndex) Init() error {
	return nil // Nothing to do.
}

// Key returns the database key to use for the index as a byte slice. This is
// part of the Indexer interface.
func (idx *CfIndex) Key() []byte {
	return cfBasicIndexKey
}

// Name returns the human-readable name of the index. This is part of the
// Indexer interface.
func (idx *CfIndex) Name() string {
	return cfIndexName
}

// Create is invoked when the indexer manager determines the index needs to be
// created for the first time. It creates buckets for the two hash-based cf
// indexes (simple, extended).
func (idx *CfIndex) Create(dbTx database.Tx) error {
	meta := dbTx.Metadata()
	_, err := meta.CreateBucket(cfBasicIndexKey)
	if err != nil {
		return err
	}
	_, err = meta.CreateBucket(cfExtendedIndexKey)
	return err
}

// makeBasicFilter() builds a block's basic filter, which consists of all
// outpoints and pkscript data pushes referenced by transactions within the
// block.
func makeBasicFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddOutPoint(txIn.PreviousOutPoint)
		}
		for _, txOut := range tx.MsgTx().TxOut {
			b.AddScript(txOut.PkScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// makeExtendedFilter() builds a block's extended filter, which consists of
// all tx hashes and sigscript data pushes contained in the block.
func makeExtendedFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		b.AddHash(tx.Hash())
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddScript(txIn.SignatureScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain. This indexer adds a hash-to-cf mapping for
// every passed block. This is part of the Indexer interface.
func (idx *CfIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	f, err := makeBasicFilterForBlock(block)
	if err != nil {
		return err
	}
	err = dbStoreBasicEntry(dbTx, block.Hash(), f)
	if err != nil {
		return err
	}
	f, err = makeExtendedFilterForBlock(block)
	if err != nil {
		return err
	}
	return dbStoreExtendedEntry(dbTx, block.Hash(), f)
}

// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain.  This indexer removes the hash-to-cf
// mapping for every passed block. This is part of the Indexer interface.
func (idx *CfIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	return dbDeleteBasicEntry(dbTx, block.Hash())
}

func (idx *CfIndex) FilterByBlockHash(hash *chainhash.Hash, extended bool) ([]byte, error) {
	var filterBytes []byte
	err := idx.db.View(func(dbTx database.Tx) error {
		var err error
		if extended {
			filterBytes, err = dbFetchExtendedEntry(dbTx, hash)
		} else {
			filterBytes, err = dbFetchBasicEntry(dbTx, hash)
		}
		return err
	})
	return filterBytes, err
}

// NewCfIndex returns a new instance of an indexer that is used to create a
// mapping of the hashes of all blocks in the blockchain to their respective
// committed filters.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockchain package. This allows the index to be
// seamlessly maintained along with the chain.
func NewCfIndex(db database.DB) *CfIndex {
	return &CfIndex{db: db}
}

// DropCfIndex drops the CF index from the provided database if exists.
func DropCfIndex(db database.DB) error {
	return dropIndex(db, cfBasicIndexKey, cfIndexName)
}
<commit_msg>Delete extended filters in DisconnectBlock()<commit_after>// Copyright (c) 2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package indexers

import (
	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/database"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/gcs/builder"
)

const (
	// cfIndexName is the human-readable name for the index.
	cfIndexName = "committed filter index"
)

// Committed filters come in two flavours: basic and extended. They are
// generated and dropped in pairs, and both are indexed by a block's hash.
// Besides holding different content, they also live in different buckets.
var (
	// cfBasicIndexKey is the name of the db bucket used to house the
	// block hash -> Basic cf index (cf#0).
	cfBasicIndexKey = []byte("cf0byhashidx")
	// cfExtendedIndexKey is the name of the db bucket used to house the
	// block hash -> Extended cf index (cf#1).
	cfExtendedIndexKey = []byte("cf1byhashidx")
)

// dbFetchBasicEntry() retrieves a block's basic filter. An entry's absence is
// not considered an error. The filter is returned serialized.
func dbFetchBasicEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Get(h[:]), nil
}

// dbFetchExtendedEntry() retrieves a block's extended filter. An entry's
// absence is not considered an error. The filter is returned serialized.
func dbFetchExtendedEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Get(h[:]), nil
}

// dbStoreBasicEntry() stores a block's basic filter.
func dbStoreBasicEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Put(h[:], f)
}

// dbStoreBasicEntry() stores a block's extended filter.
func dbStoreExtendedEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Put(h[:], f)
}

// dbDeleteBasicEntry() deletes a block's basic filter.
func dbDeleteBasicEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfBasicIndexKey)
	return idx.Delete(h[:])
}

// dbDeleteExtendedEntry() deletes a block's extended filter.
func dbDeleteExtendedEntry(dbTx database.Tx, h *chainhash.Hash) error {
	idx := dbTx.Metadata().Bucket(cfExtendedIndexKey)
	return idx.Delete(h[:])
}

// CfIndex implements a committed filter (cf) by hash index.
type CfIndex struct {
	db database.DB
}

// Ensure the CfIndex type implements the Indexer interface.
var _ Indexer = (*CfIndex)(nil)

// Init initializes the hash-based cf index. This is part of the Indexer
// interface.
func (idx *CfIndex) Init() error {
	return nil // Nothing to do.
}

// Key returns the database key to use for the index as a byte slice. This is
// part of the Indexer interface.
func (idx *CfIndex) Key() []byte {
	return cfBasicIndexKey
}

// Name returns the human-readable name of the index. This is part of the
// Indexer interface.
func (idx *CfIndex) Name() string {
	return cfIndexName
}

// Create is invoked when the indexer manager determines the index needs to be
// created for the first time. It creates buckets for the two hash-based cf
// indexes (simple, extended).
func (idx *CfIndex) Create(dbTx database.Tx) error {
	meta := dbTx.Metadata()
	_, err := meta.CreateBucket(cfBasicIndexKey)
	if err != nil {
		return err
	}
	_, err = meta.CreateBucket(cfExtendedIndexKey)
	return err
}

// makeBasicFilter() builds a block's basic filter, which consists of all
// outpoints and pkscript data pushes referenced by transactions within the
// block.
func makeBasicFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddOutPoint(txIn.PreviousOutPoint)
		}
		for _, txOut := range tx.MsgTx().TxOut {
			b.AddScript(txOut.PkScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// makeExtendedFilter() builds a block's extended filter, which consists of
// all tx hashes and sigscript data pushes contained in the block.
func makeExtendedFilterForBlock(block *btcutil.Block) ([]byte, error) {
	b := builder.WithKeyHash(block.Hash())
	_, err := b.Key()
	if err != nil {
		return nil, err
	}
	for _, tx := range block.Transactions() {
		b.AddHash(tx.Hash())
		for _, txIn := range tx.MsgTx().TxIn {
			b.AddScript(txIn.SignatureScript)
		}
	}
	f, err := b.Build()
	if err != nil {
		return nil, err
	}
	return f.Bytes(), nil
}

// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain. This indexer adds a hash-to-cf mapping for
// every passed block. This is part of the Indexer interface.
func (idx *CfIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	f, err := makeBasicFilterForBlock(block)
	if err != nil {
		return err
	}
	err = dbStoreBasicEntry(dbTx, block.Hash(), f)
	if err != nil {
		return err
	}
	f, err = makeExtendedFilterForBlock(block)
	if err != nil {
		return err
	}
	return dbStoreExtendedEntry(dbTx, block.Hash(), f)
}

// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain.  This indexer removes the hash-to-cf
// mapping for every passed block. This is part of the Indexer interface.
func (idx *CfIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
    view *blockchain.UtxoViewpoint) error {
	err := dbDeleteBasicEntry(dbTx, block.Hash())
	if err != nil {
		return err
	}
	return dbDeleteExtendedEntry(dbTx, block.Hash())
}

func (idx *CfIndex) FilterByBlockHash(hash *chainhash.Hash, extended bool) ([]byte, error) {
	var filterBytes []byte
	err := idx.db.View(func(dbTx database.Tx) error {
		var err error
		if extended {
			filterBytes, err = dbFetchExtendedEntry(dbTx, hash)
		} else {
			filterBytes, err = dbFetchBasicEntry(dbTx, hash)
		}
		return err
	})
	return filterBytes, err
}

// NewCfIndex returns a new instance of an indexer that is used to create a
// mapping of the hashes of all blocks in the blockchain to their respective
// committed filters.
//
// It implements the Indexer interface which plugs into the IndexManager that in
// turn is used by the blockchain package. This allows the index to be
// seamlessly maintained along with the chain.
func NewCfIndex(db database.DB) *CfIndex {
	return &CfIndex{db: db}
}

// DropCfIndex drops the CF index from the provided database if exists.
func DropCfIndex(db database.DB) error {
	return dropIndex(db, cfBasicIndexKey, cfIndexName)
}
<|endoftext|>


<commit_before>
package gotomic

import (
	"math/rand"
	"time"
	"bytes"
	"sync/atomic"
	"fmt"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

/*
 Transaction controlled treap
 */
type treap struct {
	root *nodeHandle
}
func (self *treap) Clone() Clonable {
	return &treap{self.root}
}

func merge(t *Transaction, left, right *nodeHandle) (result *nodeHandle, err error) {
	if left == nil {
		result = right
		return
	}
	if right == nil {
		result = left
		return
	}
	var leftNode, rightNode *node
	var subMerge *nodeHandle
	if left.weight < right.weight {
		leftNode, err = left.wopen(t)
		if err != nil {
			return
		}
		result = left
		tmp := leftNode.right
		subMerge, err = merge(t, tmp, right)
		if err != nil {
			return
		}
		leftNode.right = subMerge
		return
	}
	rightNode, err = right.wopen(t)
	if err != nil {
		return
	}
	result = right
	tmp := rightNode.left
	subMerge, err = merge(t, left, tmp)
	if err != nil {
		return
	}
	rightNode.left = subMerge
	return
}

/*
 Non-transaction controlled "user space" type
 */
type Treap struct {
	handle *Handle
	size int64
}
func NewTreap() *Treap {
	return &Treap{NewHandle(&treap{}), 0}
}
/*
 Get a readable *treap from the Treap
 */
func (self *Treap) ropen(t *Transaction) (*treap, error) {
	r, err := t.Read(self.handle)
	if err != nil {
		return nil, err
	}
	return r.(*treap), nil
}
/*
 Get a writable *treap from the Treap
 */
func (self *Treap) wopen(t *Transaction) (*treap, error) {
	r, err := t.Write(self.handle)
	if err != nil {
		return nil, err
	}
	return r.(*treap), nil
}
func (treap *Treap) Describe() string {
	rval, err := treap.describe()
	for err != nil {
		rval, err = treap.describe()
	}
	return rval
}
func (treap *Treap) describe() (rval string, err error) {
	t := NewTransaction()
	self, err := treap.ropen(t)
	if err != nil {
		return
	}
	buf := bytes.NewBufferString(fmt.Sprintf("&Treap{%p size:%v}\n", treap, treap.size))
	if self.root != nil {
		err = self.root.describe(t, buf, 0)
		if err != nil {
			return
		}
	}
	return string(buf.Bytes()), nil
}
func (treap *Treap) Put(k Comparable, v Thing) Thing {
	rval, err := treap.put(k, v)
	for err != nil {
		rval, err = treap.put(k, v)
	}
	return rval
}
func (treap *Treap) put(k Comparable, v Thing) (Thing, error) {
	t := NewTransaction()
	self, err := treap.ropen(t)
	if err != nil {
		return nil, err
	}
	newNode := newNodeHandle(k, v)
	newRoot, err := self.root.insert(t, newNode)
	if err != nil {
		return nil, err
	}
	if newRoot != self.root {
		self, err = treap.wopen(t)
		if err != nil {
			return nil, err
		}
		self.root = newRoot
	}
	if !t.Commit() {
		return nil, fmt.Errorf("%v changed during put", treap)
	}
	atomic.AddInt64(&treap.size, 1)
	return nil, nil
}

type node struct {
	left *nodeHandle
	right *nodeHandle
	value Thing
}
func (self *node) Clone() Clonable {
	rval := *self
	return &rval
}

type nodeHandle struct {
	*Handle
	key Comparable
	weight int32
}
func (handle *nodeHandle) ropen(t *Transaction) (*node, error) {
	n, err := t.Read((*Handle)(handle.Handle))
	if err != nil {
		return nil, err
	}
	return n.(*node), nil
}
func (handle *nodeHandle) wopen(t *Transaction) (*node, error) {
	r, err := t.Write((*Handle)(handle.Handle))
	if err != nil {
		return nil, err
	}
	return r.(*node), nil
}
func (handle *nodeHandle) describe(t *Transaction, buf *bytes.Buffer, indent int) error {
	self, err := handle.ropen(t)
	if err != nil {
		return err
	}
	for i := 0; i < indent; i++ {
		fmt.Fprintf(buf, " ")
	}
	fmt.Fprintf(buf, "%v => %v (%v)\n", handle.key, self.value, handle.weight)
	if self.left != nil {
		fmt.Fprintf(buf, "l:")
		err = self.left.describe(t, buf, indent + 1)
		if err != nil {
			return err
		}
	}
	if self.right != nil {
		fmt.Fprintf(buf, "r:")
		err = self.right.describe(t, buf, indent + 1)
		if err != nil {
			return err
		}
	}
	return nil
}
func newNodeHandle(k Comparable, v Thing) *nodeHandle {
	return &nodeHandle{NewHandle(&node{nil, nil, v}), k, rand.Int31()}
}
func (handle *nodeHandle) rotateLeft(t *Transaction) (result *nodeHandle, err error) {
	self, err := handle.wopen(t)
	if err != nil {
		return
	}
	result = self.left
	resultNode, err := result.wopen(t)
	if err != nil {
		return
	}
	tmp := resultNode.right
	resultNode.right = handle
	self.left = tmp
	return
}
func (handle *nodeHandle) rotateRight(t *Transaction) (result *nodeHandle, err error) {
	self, err := handle.wopen(t)
	if err != nil {
		return
	}
	result = self.right
	resultNode, err := result.wopen(t)
	if err != nil {
		return
	}
	tmp := resultNode.left
	resultNode.left = handle
	self.right = tmp
	return
}
func (handle *nodeHandle) insert(t *Transaction, newHandle *nodeHandle) (result *nodeHandle, err error) {
	if handle == nil {
		return newHandle, nil
	}
	result = handle
	self, err := handle.ropen(t)
	if err != nil {
		return
	}
	switch cmp := newHandle.key.Compare(handle.key); {
	case cmp < 0:
		var newLeft *nodeHandle
		newLeft, err = self.left.insert(t, newHandle)
		if err != nil {
			return
		}
		if newLeft != self.left {
			self, err = handle.wopen(t)
			if err != nil {
				return
			}
			self.left = newLeft
			if newLeft.weight < handle.weight {
				result, err = handle.rotateLeft(t)
				if err != nil {
					return
				}
			}
		}
	case cmp > 0:
		var newRight *nodeHandle
		newRight, err = self.right.insert(t, newHandle)
		if err != nil {
			return
		}
		if newRight != self.right {
			self, err = handle.wopen(t)
			if err != nil {
				return
			}
			self.right = newRight
			if newRight.weight < handle.weight {
				result, err = handle.rotateRight(t)
				if err != nil {
					return
				}
			}
		}
	default:
		if self, err = handle.wopen(t); err != nil {
			return nil, err
		}
		var newNode *node
		newNode, err = newHandle.ropen(t)
		if err != nil {
			return
		}
		self.value = newNode.value
	}
	return
}

<commit_msg>made Put return the old value properly, I think<commit_after>
package gotomic

import (
	"math/rand"
	"time"
	"bytes"
	"sync/atomic"
	"fmt"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

/*
 Transaction controlled treap
 */
type treap struct {
	root *nodeHandle
}
func (self *treap) Clone() Clonable {
	return &treap{self.root}
}

func merge(t *Transaction, left, right *nodeHandle) (result *nodeHandle, err error) {
	if left == nil {
		result = right
		return
	}
	if right == nil {
		result = left
		return
	}
	var leftNode, rightNode *node
	var subMerge *nodeHandle
	if left.weight < right.weight {
		leftNode, err = left.wopen(t)
		if err != nil {
			return
		}
		result = left
		tmp := leftNode.right
		subMerge, err = merge(t, tmp, right)
		if err != nil {
			return
		}
		leftNode.right = subMerge
		return
	}
	rightNode, err = right.wopen(t)
	if err != nil {
		return
	}
	result = right
	tmp := rightNode.left
	subMerge, err = merge(t, left, tmp)
	if err != nil {
		return
	}
	rightNode.left = subMerge
	return
}

/*
 Non-transaction controlled "user space" type
 */
type Treap struct {
	handle *Handle
	size int64
}
func NewTreap() *Treap {
	return &Treap{NewHandle(&treap{}), 0}
}
/*
 Get a readable *treap from the Treap
 */
func (self *Treap) ropen(t *Transaction) (*treap, error) {
	r, err := t.Read(self.handle)
	if err != nil {
		return nil, err
	}
	return r.(*treap), nil
}
/*
 Get a writable *treap from the Treap
 */
func (self *Treap) wopen(t *Transaction) (*treap, error) {
	r, err := t.Write(self.handle)
	if err != nil {
		return nil, err
	}
	return r.(*treap), nil
}
func (treap *Treap) Describe() string {
	rval, err := treap.describe()
	for err != nil {
		rval, err = treap.describe()
	}
	return rval
}
func (treap *Treap) describe() (rval string, err error) {
	t := NewTransaction()
	self, err := treap.ropen(t)
	if err != nil {
		return
	}
	buf := bytes.NewBufferString(fmt.Sprintf("&Treap{%p size:%v}\n", treap, treap.size))
	if self.root != nil {
		err = self.root.describe(t, buf, 0)
		if err != nil {
			return
		}
	}
	return string(buf.Bytes()), nil
}
func (treap *Treap) Put(k Comparable, v Thing) Thing {
	rval, err := treap.put(k, v)
	for err != nil {
		rval, err = treap.put(k, v)
	}
	return rval
}
func (treap *Treap) put(k Comparable, v Thing) (old Thing, err error) {
	t := NewTransaction()
	self, err := treap.ropen(t)
	if err != nil {
		return
	}
	newNode := newNodeHandle(k, v)
	newRoot, old, err := self.root.insert(t, newNode)
	if err != nil {
		return
	}
	if newRoot != self.root {
		self, err = treap.wopen(t)
		if err != nil {
			return nil, err
		}
		self.root = newRoot
	}
	if !t.Commit() {
		return nil, fmt.Errorf("%v changed during put", treap)
	}
	atomic.AddInt64(&treap.size, 1)
	return nil, nil
}

type node struct {
	left *nodeHandle
	right *nodeHandle
	value Thing
}
func (self *node) Clone() Clonable {
	rval := *self
	return &rval
}

type nodeHandle struct {
	*Handle
	key Comparable
	weight int32
}
func (handle *nodeHandle) ropen(t *Transaction) (*node, error) {
	n, err := t.Read((*Handle)(handle.Handle))
	if err != nil {
		return nil, err
	}
	return n.(*node), nil
}
func (handle *nodeHandle) wopen(t *Transaction) (*node, error) {
	r, err := t.Write((*Handle)(handle.Handle))
	if err != nil {
		return nil, err
	}
	return r.(*node), nil
}
func (handle *nodeHandle) describe(t *Transaction, buf *bytes.Buffer, indent int) error {
	self, err := handle.ropen(t)
	if err != nil {
		return err
	}
	for i := 0; i < indent; i++ {
		fmt.Fprintf(buf, " ")
	}
	fmt.Fprintf(buf, "%v => %v (%v)\n", handle.key, self.value, handle.weight)
	if self.left != nil {
		fmt.Fprintf(buf, "l:")
		err = self.left.describe(t, buf, indent + 1)
		if err != nil {
			return err
		}
	}
	if self.right != nil {
		fmt.Fprintf(buf, "r:")
		err = self.right.describe(t, buf, indent + 1)
		if err != nil {
			return err
		}
	}
	return nil
}
func newNodeHandle(k Comparable, v Thing) *nodeHandle {
	return &nodeHandle{NewHandle(&node{nil, nil, v}), k, rand.Int31()}
}
func (handle *nodeHandle) rotateLeft(t *Transaction) (result *nodeHandle, err error) {
	self, err := handle.wopen(t)
	if err != nil {
		return
	}
	result = self.left
	resultNode, err := result.wopen(t)
	if err != nil {
		return
	}
	tmp := resultNode.right
	resultNode.right = handle
	self.left = tmp
	return
}
func (handle *nodeHandle) rotateRight(t *Transaction) (result *nodeHandle, err error) {
	self, err := handle.wopen(t)
	if err != nil {
		return
	}
	result = self.right
	resultNode, err := result.wopen(t)
	if err != nil {
		return
	}
	tmp := resultNode.left
	resultNode.left = handle
	self.right = tmp
	return
}
func (handle *nodeHandle) insert(t *Transaction, newHandle *nodeHandle) (result *nodeHandle, old Thing, err error) {
	if handle == nil {
		result = newHandle
		return
	}
	result = handle
	self, err := handle.ropen(t)
	if err != nil {
		return
	}
	switch cmp := newHandle.key.Compare(handle.key); {
	case cmp < 0:
		var newLeft *nodeHandle
		newLeft, old, err = self.left.insert(t, newHandle)
		if err != nil {
			return
		}
		if newLeft != self.left {
			self, err = handle.wopen(t)
			if err != nil {
				return
			}
			self.left = newLeft
			if newLeft.weight < handle.weight {
				result, err = handle.rotateLeft(t)
				if err != nil {
					return
				}
			}
		}
	case cmp > 0:
		var newRight *nodeHandle
		newRight, old, err = self.right.insert(t, newHandle)
		if err != nil {
			return
		}
		if newRight != self.right {
			self, err = handle.wopen(t)
			if err != nil {
				return
			}
			self.right = newRight
			if newRight.weight < handle.weight {
				result, err = handle.rotateRight(t)
				if err != nil {
					return
				}
			}
		}
	default:
		if self, err = handle.wopen(t); err != nil {
			return
		}
		var newNode *node
		newNode, err = newHandle.ropen(t)
		if err != nil {
			return
		}
		old = self.value
		self.value = newNode.value
	}
	return
}

<|endoftext|>