<commit_before>package udnssdk

import (
	"fmt"
	"log"
	"time"
)

// NotificationsService manages Probes
type NotificationsService struct {
	client *Client
}

// Notifications builds an NotificationsService from an SBTCService
func (s *SBTCService) Notifications() *NotificationsService {
	return &NotificationsService{client: s.client}
}

// NotificationDTO manages notifications
type NotificationDTO struct {
	Email       string                   `json:"email"`
	PoolRecords []NotificationPoolRecord `json:"poolRecords"`
}

// NotificationPoolRecord does things unknown
type NotificationPoolRecord struct {
	PoolRecord   string              `json:"poolRecord"`
	Notification NotificationInfoDTO `json:"notification"`
}

// NotificationInfoDTO does things unknown
type NotificationInfoDTO struct {
	Probe     bool `json:"probe"`
	Record    bool `json:"record"`
	Scheduled bool `json:"scheduled"`
}

// NotificationListDTO does things unknown
type NotificationListDTO struct {
	Notifications []NotificationDTO `json:"notifications"`
	Queryinfo     QueryInfo         `json:"queryInfo"`
	Resultinfo    ResultInfo        `json:"resultInfo"`
}

// NotificationPath generates a URI by zone, type & guid
func NotificationPath(zone, typ, name, guid string) string {
	n := NotificationKey{
		Zone: zone,
		Type: typ,
		Name: name,
		GUID: guid,
	}
	if guid == "" {
		return n.RRSetKey().NotificationsURI()
	}
	return n.URI()
}

// ListAllNotifications finds all notification by name, type & zone, with optional query
func (s *SBTCService) ListAllNotifications(query, name, typ, zone string) ([]NotificationDTO, *Response, error) {
	r := RRSetKey{
		Zone: zone,
		Type: typ,
		Name: name,
	}
	return s.Notifications().Select(r, query)
}

func notificationQueryPath(zone, typ, name, query string, offset int) string {
	r := RRSetKey{
		Zone: zone,
		Type: typ,
		Name: name,
	}
	return r.NotificationsQueryURI(query, offset)
}

// ListNotifications for things
func (s *SBTCService) ListNotifications(query, name, typ, zone string, offset int) ([]NotificationDTO, ResultInfo, *Response, error) {
	r := RRSetKey{
		Zone: zone,
		Type: typ,
		Name: name,
	}
	return s.Notifications().SelectWithOffset(r, query, offset)
}

// GetNotification returns a notification by name, type, zone & guid
func (s *SBTCService) GetNotification(name, typ, zone, guid string) (NotificationInfoDTO, *Response, error) {
	n := NotificationKey{
		Zone: zone,
		Type: typ,
		Name: name,
		GUID: guid,
	}
	return s.Notifications().Find(n)
}

// CreateNotification creates a notification by name, type & zone, with the NotificationInfoDTO ev
func (s *SBTCService) CreateNotification(name, typ, zone string, ev NotificationInfoDTO) (*Response, error) {
	r := RRSetKey{
		Zone: zone,
		Type: typ,
		Name: name,
	}
	return s.Notifications().Create(r, ev)
}

// UpdateNotification updates a notification by name, type, zone & guid, with NotificationInfoDTO ev
func (s *SBTCService) UpdateNotification(name, typ, zone, guid string, ev NotificationInfoDTO) (*Response, error) {
	n := NotificationKey{
		Zone: zone,
		Type: typ,
		Name: name,
		GUID: guid,
	}
	return s.Notifications().Update(n, ev)
}

// DeleteNotification deletes a notification by name, type, zone & guid
func (s *SBTCService) DeleteNotification(name, typ, zone, guid string) (*Response, error) {
	n := NotificationKey{
		Zone: zone,
		Type: typ,
		Name: name,
		GUID: guid,
	}
	return s.Notifications().Delete(n)
}

// ===== //

// NotificationKey collects the identifiers of an Notification
type NotificationKey struct {
	Zone string
	Type string
	Name string
	GUID string
}

// RRSetKey generates the RRSetKey for the NotificationKey
func (n *NotificationKey) RRSetKey() *RRSetKey {
	return &RRSetKey{
		Zone: n.Zone,
		Type: n.Type,
		Name: n.Name,
	}
}

// URI generates the URI for a probe
func (n *NotificationKey) URI() string {
	return fmt.Sprintf("%s/%s", n.RRSetKey().NotificationsURI(), n.GUID)
}

// Select requests all notifications by RRSetKey and optional query, using pagination and error handling
func (s *NotificationsService) Select(r RRSetKey, query string) ([]NotificationDTO, *Response, error) {
	// TODO: Sane Configuration for timeouts / retries
	maxerrs := 5
	waittime := 5 * time.Second

	// init accumulators
	pis := []NotificationDTO{}
	errcnt := 0
	offset := 0

	for {
		reqNotifications, ri, res, err := s.SelectWithOffset(r, query, offset)
		if err != nil {
			if res.StatusCode >= 500 {
				errcnt = errcnt + 1
				if errcnt < maxerrs {
					time.Sleep(waittime)
					continue
				}
			}
			return pis, res, err
		}

		log.Printf("[DEBUG] ResultInfo: %+v\n", ri)
		for _, pi := range reqNotifications {
			pis = append(pis, pi)
		}
		if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
			return pis, res, nil
		}
		offset = ri.ReturnedCount + ri.Offset
		continue
	}
}

// SelectWithOffset requests list of notifications by RRSetKey, query and offset, also returning list metadata, the actual response, or an error
func (s *NotificationsService) SelectWithOffset(r RRSetKey, query string, offset int) ([]NotificationDTO, ResultInfo, *Response, error) {
	var tld NotificationListDTO

	uri := r.NotificationsQueryURI(query, offset)
	res, err := s.client.get(uri, &tld)

	log.Printf("DEBUG - ResultInfo: %+v\n", tld.Resultinfo)
	pis := []NotificationDTO{}
	for _, pi := range tld.Notifications {
		pis = append(pis, pi)
	}
	return pis, tld.Resultinfo, res, err
}

// Find requests a notification by NotificationKey,returning the actual response, or an error
func (s *NotificationsService) Find(n NotificationKey) (NotificationInfoDTO, *Response, error) {
	var t NotificationInfoDTO
	res, err := s.client.get(n.URI(), &t)
	return t, res, err
}

// Create requests creation of an event by RRSetKey, with provided NotificationInfoDTO, returning actual response or an error
func (s *NotificationsService) Create(r RRSetKey, ev NotificationInfoDTO) (*Response, error) {
	return s.client.post(r.NotificationsURI(), ev, nil)
}

// Update requests update of an event by NotificationKey, with provided NotificationInfoDTO, returning the actual response or an error
func (s *NotificationsService) Update(n NotificationKey, ev NotificationInfoDTO) (*Response, error) {
	return s.client.put(n.URI(), ev, nil)
}

// Delete requests deletion of an event by NotificationKey, returning the actual response or an error
func (s *NotificationsService) Delete(n NotificationKey) (*Response, error) {
	return s.client.delete(n.URI(), nil)
}
<commit_msg>notification.go: remove old interface<commit_after>package udnssdk

import (
	"fmt"
	"log"
	"time"
)

// NotificationsService manages Probes
type NotificationsService struct {
	client *Client
}

// Notifications builds an NotificationsService from an SBTCService
func (s *SBTCService) Notifications() *NotificationsService {
	return &NotificationsService{client: s.client}
}

// NotificationDTO manages notifications
type NotificationDTO struct {
	Email       string                   `json:"email"`
	PoolRecords []NotificationPoolRecord `json:"poolRecords"`
}

// NotificationPoolRecord does things unknown
type NotificationPoolRecord struct {
	PoolRecord   string              `json:"poolRecord"`
	Notification NotificationInfoDTO `json:"notification"`
}

// NotificationInfoDTO does things unknown
type NotificationInfoDTO struct {
	Probe     bool `json:"probe"`
	Record    bool `json:"record"`
	Scheduled bool `json:"scheduled"`
}

// NotificationListDTO does things unknown
type NotificationListDTO struct {
	Notifications []NotificationDTO `json:"notifications"`
	Queryinfo     QueryInfo         `json:"queryInfo"`
	Resultinfo    ResultInfo        `json:"resultInfo"`
}

// NotificationKey collects the identifiers of an Notification
type NotificationKey struct {
	Zone string
	Type string
	Name string
	GUID string
}

// RRSetKey generates the RRSetKey for the NotificationKey
func (n *NotificationKey) RRSetKey() *RRSetKey {
	return &RRSetKey{
		Zone: n.Zone,
		Type: n.Type,
		Name: n.Name,
	}
}

// URI generates the URI for a probe
func (n *NotificationKey) URI() string {
	return fmt.Sprintf("%s/%s", n.RRSetKey().NotificationsURI(), n.GUID)
}

// Select requests all notifications by RRSetKey and optional query, using pagination and error handling
func (s *NotificationsService) Select(r RRSetKey, query string) ([]NotificationDTO, *Response, error) {
	// TODO: Sane Configuration for timeouts / retries
	maxerrs := 5
	waittime := 5 * time.Second

	// init accumulators
	pis := []NotificationDTO{}
	errcnt := 0
	offset := 0

	for {
		reqNotifications, ri, res, err := s.SelectWithOffset(r, query, offset)
		if err != nil {
			if res.StatusCode >= 500 {
				errcnt = errcnt + 1
				if errcnt < maxerrs {
					time.Sleep(waittime)
					continue
				}
			}
			return pis, res, err
		}

		log.Printf("[DEBUG] ResultInfo: %+v\n", ri)
		for _, pi := range reqNotifications {
			pis = append(pis, pi)
		}
		if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
			return pis, res, nil
		}
		offset = ri.ReturnedCount + ri.Offset
		continue
	}
}

// SelectWithOffset requests list of notifications by RRSetKey, query and offset, also returning list metadata, the actual response, or an error
func (s *NotificationsService) SelectWithOffset(r RRSetKey, query string, offset int) ([]NotificationDTO, ResultInfo, *Response, error) {
	var tld NotificationListDTO

	uri := r.NotificationsQueryURI(query, offset)
	res, err := s.client.get(uri, &tld)

	log.Printf("DEBUG - ResultInfo: %+v\n", tld.Resultinfo)
	pis := []NotificationDTO{}
	for _, pi := range tld.Notifications {
		pis = append(pis, pi)
	}
	return pis, tld.Resultinfo, res, err
}

// Find requests a notification by NotificationKey,returning the actual response, or an error
func (s *NotificationsService) Find(n NotificationKey) (NotificationInfoDTO, *Response, error) {
	var t NotificationInfoDTO
	res, err := s.client.get(n.URI(), &t)
	return t, res, err
}

// Create requests creation of an event by RRSetKey, with provided NotificationInfoDTO, returning actual response or an error
func (s *NotificationsService) Create(r RRSetKey, ev NotificationInfoDTO) (*Response, error) {
	return s.client.post(r.NotificationsURI(), ev, nil)
}

// Update requests update of an event by NotificationKey, with provided NotificationInfoDTO, returning the actual response or an error
func (s *NotificationsService) Update(n NotificationKey, ev NotificationInfoDTO) (*Response, error) {
	return s.client.put(n.URI(), ev, nil)
}

// Delete requests deletion of an event by NotificationKey, returning the actual response or an error
func (s *NotificationsService) Delete(n NotificationKey) (*Response, error) {
	return s.client.delete(n.URI(), nil)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package mungers

import (
	"net/http"
	"net/url"
	"strings"

	"k8s.io/contrib/mungegithub/github"

	"io/ioutil"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"k8s.io/contrib/mungegithub/features"
)

// LabelMunger will update a label on a PR based on how many lines are changed.
// It will exclude certain files in it's calculations based on the config
// file provided in --generated-files-config
type LabelMunger struct {
}

// Initialize will initialize the munger
func (LabelMunger) Initialize(config *github.Config, features *features.Features) error {
	return nil
}

// Name is the name usable in --pr-mungers
func (LabelMunger) Name() string { return "issue-triager" }

// RequiredFeatures is a slice of 'features' that must be provided
func (LabelMunger) RequiredFeatures() []string { return []string{} }

// AddFlags will add any request flags to the cobra `cmd`
func (LabelMunger) AddFlags(cmd *cobra.Command, config *github.Config) {

}

func init() {
	s := &LabelMunger{}
	RegisterMungerOrDie(s)
}

// EachLoop is called at the start of every munge loop
func (LabelMunger) EachLoop() error { return nil }

// Munge is the workhorse the will actually make updates to the PR
func (s *LabelMunger) Munge(obj *github.MungeObject) {
	//this munger only works on issues
	if obj.IsPR() {
		return
	}

	issue := obj.Issue

	if obj.HasLabel("kind/flake") {
		return
	}

	tLabels := github.GetLabelsWithPrefix(issue.Labels, "team/")

	if len(tLabels) != 0 {
		//already labeled
		return
	}

	cLabels := github.GetLabelsWithPrefix(issue.Labels, "components/")
	if len(cLabels) != 0 {
		//already labeled
		return
	}

	routingLabelsToApply, err := http.PostForm("http://issue-triager-service:5000",
		url.Values{"title": {*issue.Title}, "body": {*issue.Body}})

	if err != nil {
		//handle the error
		glog.Error(err)
		return
	}
	defer routingLabelsToApply.Body.Close()
	response, err := ioutil.ReadAll(routingLabelsToApply.Body)
	if routingLabelsToApply.StatusCode != 200 {
		glog.Errorf("%d: %s", routingLabelsToApply.StatusCode, response)
		return
	}

	obj.AddLabels(strings.Split(string(response), ";"))
}
<commit_msg>Split By Comma Instead of Semicolon<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package mungers

import (
	"net/http"
	"net/url"
	"strings"

	"k8s.io/contrib/mungegithub/github"

	"io/ioutil"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"k8s.io/contrib/mungegithub/features"
)

// LabelMunger will update a label on a PR based on how many lines are changed.
// It will exclude certain files in it's calculations based on the config
// file provided in --generated-files-config
type LabelMunger struct {
}

// Initialize will initialize the munger
func (LabelMunger) Initialize(config *github.Config, features *features.Features) error {
	return nil
}

// Name is the name usable in --pr-mungers
func (LabelMunger) Name() string { return "issue-triager" }

// RequiredFeatures is a slice of 'features' that must be provided
func (LabelMunger) RequiredFeatures() []string { return []string{} }

// AddFlags will add any request flags to the cobra `cmd`
func (LabelMunger) AddFlags(cmd *cobra.Command, config *github.Config) {

}

func init() {
	s := &LabelMunger{}
	RegisterMungerOrDie(s)
}

// EachLoop is called at the start of every munge loop
func (LabelMunger) EachLoop() error { return nil }

// Munge is the workhorse the will actually make updates to the PR
func (s *LabelMunger) Munge(obj *github.MungeObject) {
	//this munger only works on issues
	if obj.IsPR() {
		return
	}

	issue := obj.Issue

	if obj.HasLabel("kind/flake") {
		return
	}

	tLabels := github.GetLabelsWithPrefix(issue.Labels, "team/")

	if len(tLabels) != 0 {
		//already labeled
		return
	}

	cLabels := github.GetLabelsWithPrefix(issue.Labels, "components/")
	if len(cLabels) != 0 {
		//already labeled
		return
	}

	routingLabelsToApply, err := http.PostForm("http://issue-triager-service:5000",
		url.Values{"title": {*issue.Title}, "body": {*issue.Body}})

	if err != nil {
		//handle the error
		glog.Error(err)
		return
	}
	defer routingLabelsToApply.Body.Close()
	response, err := ioutil.ReadAll(routingLabelsToApply.Body)
	if routingLabelsToApply.StatusCode != 200 {
		glog.Errorf("%d: %s", routingLabelsToApply.StatusCode, response)
		return
	}

	obj.AddLabels(strings.Split(string(response), ","))
}
<|endoftext|>


<commit_before>// TODO(u): Implement error handling for CueSheet parsing.
// TODO(u): Check handling of padding during the parsing of CueSheet.

package meta

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
)

// A CueSheet describes how tracks are laid out within a FLAC stream.
//
// ref: https://www.xiph.org/flac/format.html#metadata_block_cuesheet
type CueSheet struct {
	// Media catalog number.
	MCN string
	// Number of lead-in samples. This field only has meaning for CD-DA cue
	// sheets; for other uses it should be 0. Refer to the spec for additional
	// information.
	NLeadInSamples uint64
	// Specifies if the cue sheet corresponds to a Compact Disc.
	IsCompactDisc bool
	// One or more tracks. The last track of a cue sheet is always the lead-out
	// track.
	Tracks []CueSheetTrack
}

// parseCueSheet reads and parses the body of an CueSheet metadata block.
func (block *Block) parseCueSheet() error {
	// Parse cue sheet.
	// 128 bytes: MCN.
	buf, err := readBytes(block.lr, 128)
	if err != nil {
		return err
	}
	cs := new(CueSheet)
	block.Body = cs
	cs.MCN = stringFromSZ(buf)

	// 64 bits: NLeadInSamples.
	err = binary.Read(block.lr, binary.BigEndian, &cs.NLeadInSamples)
	if err != nil {
		return err
	}

	// 1 bit: IsCompactDisc.
	var x uint8
	err = binary.Read(block.lr, binary.BigEndian, &x)
	if err != nil {
		return err
	}
	// mask = 10000000
	if x&0x80 != 0 {
		cs.IsCompactDisc = true
	}

	// 7 bits and 258 bytes: reserved.
	// mask = 01111111
	if x&0x7F != 0 {
		return ErrInvalidPadding
	}
	lr := io.LimitReader(block.lr, 258)
	zr := zeros{r: lr}
	_, err = io.Copy(ioutil.Discard, zr)
	if err != nil {
		return err
	}

	// Parse cue sheet tracks.
	// 8 bits: (number of tracks)
	err = binary.Read(block.lr, binary.BigEndian, &x)
	if err != nil {
		return err
	}
	if x < 1 {
		return errors.New("meta.Block.parseCueSheet: at least one track required")
	}
	cs.Tracks = make([]CueSheetTrack, x)
	for i := range cs.Tracks {
		// 64 bits: Offset.
		track := &cs.Tracks[i]
		err = binary.Read(block.lr, binary.BigEndian, &track.Offset)
		if err != nil {
			return err
		}

		// 8 bits: Num.
		err = binary.Read(block.lr, binary.BigEndian, &track.Num)
		if err != nil {
			return err
		}

		// 12 bytes: ISRC.
		buf, err = readBytes(block.lr, 12)
		if err != nil {
			return err
		}
		track.ISRC = stringFromSZ(buf)

		// 1 bit: IsAudio.
		err = binary.Read(block.lr, binary.BigEndian, &x)
		if err != nil {
			return err
		}
		// mask = 10000000
		if x&0x80 != 0 {
			track.IsAudio = true
		}

		// 1 bit: HasPreEmphasis.
		// mask = 01000000
		if x&0x40 != 0 {
			track.HasPreEmphasis = true
		}

		// 6 bits and 13 bytes: reserved.
		// mask = 00111111
		if x&0x3F != 0 {
			return ErrInvalidPadding
		}
		lr = io.LimitReader(block.lr, 13)
		zr = zeros{r: lr}
		_, err = io.Copy(ioutil.Discard, zr)
		if err != nil {
			return err
		}

		// Parse indicies.
		// 8 bits: (number of indicies)
		err = binary.Read(block.lr, binary.BigEndian, &x)
		if err != nil {
			return err
		}
		track.Indicies = make([]CueSheetTrackIndex, x)
		for i := range track.Indicies {
			index := &track.Indicies[i]
			// 64 bits: Offset.
			err = binary.Read(block.lr, binary.BigEndian, &index.Offset)
			if err != nil {
				return err
			}

			// 8 bits: Num.
			err = binary.Read(block.lr, binary.BigEndian, &index.Num)
			if err != nil {
				return err
			}

			// 3 bytes: reserved.
			lr = io.LimitReader(block.lr, 3)
			zr = zeros{r: lr}
			_, err = io.Copy(ioutil.Discard, zr)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// stringFromSZ converts the provided byte slice to a string after terminating
// it at the first occurance of a NULL character.
func stringFromSZ(buf []byte) string {
	pos := bytes.IndexByte(buf, 0)
	if pos == -1 {
		return string(buf)
	}
	return string(buf[:pos])
}

// CueSheetTrack contains the start offset of a track and other track specific
// metadata.
type CueSheetTrack struct {
	// Track offset in samples, relative to the beginning of the FLAC audio
	// stream.
	Offset uint64
	// Track number; never 0, always unique.
	Num uint8
	// International Standard Recording Code; empty string if not present.
	//
	// ref: http://isrc.ifpi.org/
	ISRC string
	// Specifies if the track contains audio or data.
	IsAudio bool
	// Specifies if the track has been recorded with pre-emphasis
	HasPreEmphasis bool
	// Every track has one or more track index points, except for the lead-out
	// track which has zero. Each index point specifies a position within the
	// track.
	Indicies []CueSheetTrackIndex
}

// A CueSheetTrackIndex specifies a position within a track.
type CueSheetTrackIndex struct {
	// Index point offset in samples, relative to the track offset.
	Offset uint64
	// Index point number; subsequently incrementing by 1 and always unique
	// within a track.
	Num uint8
}
<commit_msg>meta: Fix parsing of IsAudio and add error handling for cue sheet parsing.<commit_after>package meta

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
)

// A CueSheet describes how tracks are laid out within a FLAC stream.
//
// ref: https://www.xiph.org/flac/format.html#metadata_block_cuesheet
type CueSheet struct {
	// Media catalog number.
	MCN string
	// Number of lead-in samples. This field only has meaning for CD-DA cue
	// sheets; for other uses it should be 0. Refer to the spec for additional
	// information.
	NLeadInSamples uint64
	// Specifies if the cue sheet corresponds to a Compact Disc.
	IsCompactDisc bool
	// One or more tracks. The last track of a cue sheet is always the lead-out
	// track.
	Tracks []CueSheetTrack
}

// parseCueSheet reads and parses the body of an CueSheet metadata block.
func (block *Block) parseCueSheet() error {
	// Parse cue sheet.
	// 128 bytes: MCN.
	buf, err := readBytes(block.lr, 128)
	if err != nil {
		return err
	}
	cs := new(CueSheet)
	block.Body = cs
	cs.MCN = stringFromSZ(buf)

	// 64 bits: NLeadInSamples.
	err = binary.Read(block.lr, binary.BigEndian, &cs.NLeadInSamples)
	if err != nil {
		return err
	}

	// 1 bit: IsCompactDisc.
	var x uint8
	err = binary.Read(block.lr, binary.BigEndian, &x)
	if err != nil {
		return err
	}
	// mask = 10000000
	if x&0x80 != 0 {
		cs.IsCompactDisc = true
	}

	// 7 bits and 258 bytes: reserved.
	// mask = 01111111
	if x&0x7F != 0 {
		return ErrInvalidPadding
	}
	lr := io.LimitReader(block.lr, 258)
	zr := zeros{r: lr}
	_, err = io.Copy(ioutil.Discard, zr)
	if err != nil {
		return err
	}

	// Parse cue sheet tracks.
	// 8 bits: (number of tracks)
	err = binary.Read(block.lr, binary.BigEndian, &x)
	if err != nil {
		return err
	}
	if x < 1 {
		return errors.New("meta.Block.parseCueSheet: at least one track required")
	}
	if cs.IsCompactDisc && x > 100 {
		return fmt.Errorf("meta.Block.parseCueSheet: number of CD-DA tracks (%d) exceeds 100", x)
	}
	cs.Tracks = make([]CueSheetTrack, x)
	// Each track number within a cue sheet must be unique; use uniq to keep
	// track.
	uniq := make(map[uint8]struct{})
	for i := range cs.Tracks {
		// 64 bits: Offset.
		track := &cs.Tracks[i]
		err = binary.Read(block.lr, binary.BigEndian, &track.Offset)
		if err != nil {
			return err
		}
		if cs.IsCompactDisc && track.Offset%588 != 0 {
			return fmt.Errorf("meta.Block.parseCueSheet: CD-DA track offset (%d) must be evenly divisible by 588", track.Offset)
		}

		// 8 bits: Num.
		err = binary.Read(block.lr, binary.BigEndian, &track.Num)
		if err != nil {
			return err
		}
		if _, ok := uniq[track.Num]; ok {
			return fmt.Errorf("meta.Block.parseCueSheet: duplicated track number %d", track.Num)
		}
		uniq[track.Num] = struct{}{}
		if track.Num == 0 {
			return errors.New("meta.Block.parseCueSheet: invalid track number (0)")
		}
		isLeadOut := i == len(cs.Tracks)-1
		if cs.IsCompactDisc {
			if !isLeadOut {
				if track.Num >= 100 {
					return fmt.Errorf("meta.Block.parseCueSheet: CD-DA track number (%d) exceeds 99", track.Num)
				}
			} else {
				if track.Num != 170 {
					return fmt.Errorf("meta.Block.parseCueSheet: invalid lead-out CD-DA track number; expected 170, got %d", track.Num)
				}
			}
		} else {
			if isLeadOut && track.Num != 255 {
				return fmt.Errorf("meta.Block.parseCueSheet: invalid lead-out track number; expected 255, got %d", track.Num)
			}
		}

		// 12 bytes: ISRC.
		buf, err = readBytes(block.lr, 12)
		if err != nil {
			return err
		}
		track.ISRC = stringFromSZ(buf)

		// 1 bit: IsAudio.
		err = binary.Read(block.lr, binary.BigEndian, &x)
		if err != nil {
			return err
		}
		// mask = 10000000
		if x&0x80 == 0 {
			track.IsAudio = true
		}

		// 1 bit: HasPreEmphasis.
		// mask = 01000000
		if x&0x40 != 0 {
			track.HasPreEmphasis = true
		}

		// 6 bits and 13 bytes: reserved.
		// mask = 00111111
		if x&0x3F != 0 {
			return ErrInvalidPadding
		}
		lr = io.LimitReader(block.lr, 13)
		zr = zeros{r: lr}
		_, err = io.Copy(ioutil.Discard, zr)
		if err != nil {
			return err
		}

		// Parse indicies.
		// 8 bits: (number of indicies)
		err = binary.Read(block.lr, binary.BigEndian, &x)
		if err != nil {
			return err
		}
		if x < 1 {
			if !isLeadOut {
				return errors.New("meta.Block.parseCueSheet: at least one track index required")
			}
			continue
		}
		track.Indicies = make([]CueSheetTrackIndex, x)
		for i := range track.Indicies {
			index := &track.Indicies[i]
			// 64 bits: Offset.
			err = binary.Read(block.lr, binary.BigEndian, &index.Offset)
			if err != nil {
				return err
			}

			// 8 bits: Num.
			err = binary.Read(block.lr, binary.BigEndian, &index.Num)
			if err != nil {
				return err
			}

			// 3 bytes: reserved.
			lr = io.LimitReader(block.lr, 3)
			zr = zeros{r: lr}
			_, err = io.Copy(ioutil.Discard, zr)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// stringFromSZ converts the provided byte slice to a string after terminating
// it at the first occurance of a NULL character.
func stringFromSZ(buf []byte) string {
	pos := bytes.IndexByte(buf, 0)
	if pos == -1 {
		return string(buf)
	}
	return string(buf[:pos])
}

// CueSheetTrack contains the start offset of a track and other track specific
// metadata.
type CueSheetTrack struct {
	// Track offset in samples, relative to the beginning of the FLAC audio
	// stream.
	Offset uint64
	// Track number; never 0, always unique.
	Num uint8
	// International Standard Recording Code; empty string if not present.
	//
	// ref: http://isrc.ifpi.org/
	ISRC string
	// Specifies if the track contains audio or data.
	IsAudio bool
	// Specifies if the track has been recorded with pre-emphasis
	HasPreEmphasis bool
	// Every track has one or more track index points, except for the lead-out
	// track which has zero. Each index point specifies a position within the
	// track.
	Indicies []CueSheetTrackIndex
}

// A CueSheetTrackIndex specifies a position within a track.
type CueSheetTrackIndex struct {
	// Index point offset in samples, relative to the track offset.
	Offset uint64
	// Index point number; subsequently incrementing by 1 and always unique
	// within a track.
	Num uint8
}
<|endoftext|>


<commit_before>package metakv

import (
	"fmt"
)

func noPanic(err error) {
	if err != nil {
		panic(err)
	}
}

func doAppend(s *store, path string, value []byte) error {
	oldv, rev, err := s.get(path)
	if err != nil {
		return err
	}
	if rev == nil {
		rev = RevCreate
	}
	oldv = append(oldv, value...)
	return s.set(path, oldv, rev, false)
}

func kvEqual(a, b KVEntry) bool {
	if a.Value == nil && b.Value != nil || b.Value == nil && a.Value != nil {
		return false
	}
	return a.Path == b.Path && string(a.Value) == string(b.Value)
}

// ExecuteBasicSanityTest runs basic sanity test.
func ExecuteBasicSanityTest(log func(v ...interface{})) {
	doExecuteBasicSanityTest(log, defaultStore)
}

func doExecuteBasicSanityTest(log func(v ...interface{}), s *store) {
	log("Starting basic sanity test")
	l, err := s.listAllChildren("/_sanity/")
	noPanic(err)
	for _, kve := range l {
		err := s.delete(kve.Path, nil)
		noPanic(err)
	}
	log("cleaned up /_sanity/ subspace")

	v, r, err := s.get("/_sanity/nonexistant")
	noPanic(err)
	if v != nil || r != nil {
		panic("badness")
	}

	buf := make(chan KVEntry, 128)
	cancelChan := make(chan struct{})

	defer func() {
		if cancelChan != nil {
			close(cancelChan)
		}
	}()

	go func() {
		err := s.runObserveChildren("/_sanity/", func(path string, value []byte, rev interface{}) error {
			buf <- KVEntry{Path: path, Value: value, Rev: rev}
			return nil
		}, cancelChan)
		log("Sanity observe loop exited")
		close(buf)
		if err != nil {
			panic(err)
		}
	}()

	err = doAppend(s, "/_sanity/key", []byte("value"))
	noPanic(err)

	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r == nil || string(v) != "value" {
		panic("badness")
	}

	err = s.set("/_sanity/key", []byte("new value"), r, false)
	noPanic(err)

	err = s.delete("/_sanity/key", r)
	if err != ErrRevMismatch {
		panic("must have ErrRevMismatch")
	}

	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r == nil || string(v) != "new value" {
		panic("bad")
	}

	err = s.delete("/_sanity/key", r)
	noPanic(err)

	l, err = s.listAllChildren("/_sanity/")
	noPanic(err)
	if len(l) != 0 {
		panic("len is bad")
	}

	close(cancelChan)
	cancelChan = nil

	var allMutations []KVEntry
	for kve := range buf {
		allMutations = append(allMutations, kve)
	}

	if len(allMutations) != 3 {
		panic(fmt.Sprintf("bad mutations size: %d (%v)", len(allMutations), allMutations))
	}

	if !kvEqual(allMutations[0], KVEntry{Path: "/_sanity/key", Value: []byte("value")}) {
		panic("bad mutation")
	}

	if !kvEqual(allMutations[1], KVEntry{Path: "/_sanity/key", Value: []byte("new value")}) {
		panic("bad mutation")
	}

	// deletions signal rev that is interface{}([]byte(nil))
	if !kvEqual(allMutations[2], KVEntry{Path: "/_sanity/key", Value: nil}) || len(allMutations[2].Rev.([]byte)) != 0 {
		log(fmt.Sprintf("bad mutation: %v", allMutations[2]))
		panic("bad mutation")
	}

	err = s.set("/_sanity/key", []byte("more value"), nil, false)
	noPanic(err)
	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r == nil || string(v) != "more value" {
		panic("expecting more value got: " + string(v))
	}
	err = s.delete("/_sanity/key", nil)
	noPanic(err)
	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r != nil {
		panic("expected key to be missing after successful delete")
	}

	log("Completed metakv sanity test")
}
<commit_msg>fix ineffassign warning<commit_after>package metakv

import (
	"fmt"
)

func noPanic(err error) {
	if err != nil {
		panic(err)
	}
}

func doAppend(s *store, path string, value []byte) error {
	oldv, rev, err := s.get(path)
	if err != nil {
		return err
	}
	if rev == nil {
		rev = RevCreate
	}
	oldv = append(oldv, value...)
	return s.set(path, oldv, rev, false)
}

func kvEqual(a, b KVEntry) bool {
	if a.Value == nil && b.Value != nil || b.Value == nil && a.Value != nil {
		return false
	}
	return a.Path == b.Path && string(a.Value) == string(b.Value)
}

// ExecuteBasicSanityTest runs basic sanity test.
func ExecuteBasicSanityTest(log func(v ...interface{})) {
	doExecuteBasicSanityTest(log, defaultStore)
}

func doExecuteBasicSanityTest(log func(v ...interface{}), s *store) {
	log("Starting basic sanity test")
	l, err := s.listAllChildren("/_sanity/")
	noPanic(err)
	for _, kve := range l {
		err := s.delete(kve.Path, nil)
		noPanic(err)
	}
	log("cleaned up /_sanity/ subspace")

	v, r, err := s.get("/_sanity/nonexistant")
	noPanic(err)
	if v != nil || r != nil {
		panic("badness")
	}

	buf := make(chan KVEntry, 128)
	cancelChan := make(chan struct{})

	defer func() {
		if cancelChan != nil {
			close(cancelChan)
		}
	}()

	go func() {
		err := s.runObserveChildren("/_sanity/", func(path string, value []byte, rev interface{}) error {
			buf <- KVEntry{Path: path, Value: value, Rev: rev}
			return nil
		}, cancelChan)
		log("Sanity observe loop exited")
		close(buf)
		if err != nil {
			panic(err)
		}
	}()

	err = doAppend(s, "/_sanity/key", []byte("value"))
	noPanic(err)

	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r == nil || string(v) != "value" {
		panic("badness")
	}

	err = s.set("/_sanity/key", []byte("new value"), r, false)
	noPanic(err)

	err = s.delete("/_sanity/key", r)
	if err != ErrRevMismatch {
		panic("must have ErrRevMismatch")
	}

	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r == nil || string(v) != "new value" {
		panic("bad")
	}

	err = s.delete("/_sanity/key", r)
	noPanic(err)

	l, err = s.listAllChildren("/_sanity/")
	noPanic(err)
	if len(l) != 0 {
		panic("len is bad")
	}

	close(cancelChan)
	cancelChan = nil

	var allMutations []KVEntry
	for kve := range buf {
		allMutations = append(allMutations, kve)
	}

	if len(allMutations) != 3 {
		panic(fmt.Sprintf("bad mutations size: %d (%v)", len(allMutations), allMutations))
	}

	if !kvEqual(allMutations[0], KVEntry{Path: "/_sanity/key", Value: []byte("value")}) {
		panic("bad mutation")
	}

	if !kvEqual(allMutations[1], KVEntry{Path: "/_sanity/key", Value: []byte("new value")}) {
		panic("bad mutation")
	}

	// deletions signal rev that is interface{}([]byte(nil))
	if !kvEqual(allMutations[2], KVEntry{Path: "/_sanity/key", Value: nil}) || len(allMutations[2].Rev.([]byte)) != 0 {
		log(fmt.Sprintf("bad mutation: %v", allMutations[2]))
		panic("bad mutation")
	}

	err = s.set("/_sanity/key", []byte("more value"), nil, false)
	noPanic(err)
	v, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r == nil || string(v) != "more value" {
		panic("expecting more value got: " + string(v))
	}
	err = s.delete("/_sanity/key", nil)
	noPanic(err)
	_, r, err = s.get("/_sanity/key")
	noPanic(err)
	if r != nil {
		panic("expected key to be missing after successful delete")
	}

	log("Completed metakv sanity test")
}
<|endoftext|>


<commit_before>package daemon

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/dotcloud/docker/archive"
	"github.com/dotcloud/docker/daemon/execdriver"
	"github.com/dotcloud/docker/pkg/symlink"
)

type BindMap struct {
	SrcPath string
	DstPath string
	Mode    string
}

func prepareVolumesForContainer(container *Container) error {
	if container.Volumes == nil || len(container.Volumes) == 0 {
		container.Volumes = make(map[string]string)
		container.VolumesRW = make(map[string]bool)
		if err := applyVolumesFrom(container); err != nil {
			return err
		}
	}

	if err := createVolumes(container); err != nil {
		return err
	}
	return nil
}

func setupMountsForContainer(container *Container) error {
	mounts := []execdriver.Mount{
		{container.daemon.sysInitPath, "/.dockerinit", false, true},
		{container.ResolvConfPath, "/etc/resolv.conf", false, true},
	}

	if container.HostnamePath != "" {
		mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", false, true})
	}

	if container.HostsPath != "" {
		mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", false, true})
	}

	// Mount user specified volumes
	// Note, these are not private because you may want propagation of (un)mounts from host
	// volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you
	// want this new mount in the container
	for r, v := range container.Volumes {
		mounts = append(mounts, execdriver.Mount{v, r, container.VolumesRW[r], false})
	}

	container.command.Mounts = mounts

	return nil
}

func applyVolumesFrom(container *Container) error {
	volumesFrom := container.hostConfig.VolumesFrom
	if len(volumesFrom) > 0 {
		for _, containerSpec := range volumesFrom {
			var (
				mountRW   = true
				specParts = strings.SplitN(containerSpec, ":", 2)
			)

			switch len(specParts) {
			case 0:
				return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
			case 2:
				switch specParts[1] {
				case "ro":
					mountRW = false
				case "rw": // mountRW is already true
				default:
					return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
				}
			}

			c := container.daemon.Get(specParts[0])
			if c == nil {
				return fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0])
			}

			if err := c.Mount(); err != nil {
				return fmt.Errorf("Container %s failed to mount. Impossible to mount its volumes", specParts[0])
			}
			defer c.Unmount()

			for volPath, id := range c.Volumes {
				if _, exists := container.Volumes[volPath]; exists {
					continue
				}
				stat, err := os.Stat(c.getResourcePath(volPath))
				if err != nil {
					return err
				}
				if err := createIfNotExists(container.getResourcePath(volPath), stat.IsDir()); err != nil {
					return err
				}
				container.Volumes[volPath] = id
				if isRW, exists := c.VolumesRW[volPath]; exists {
					container.VolumesRW[volPath] = isRW && mountRW
				}
			}

		}
	}
	return nil
}

func getBindMap(container *Container) (map[string]BindMap, error) {
	var (
		// Create the requested bind mounts
		binds = make(map[string]BindMap)
		// Define illegal container destinations
		illegalDsts = []string{"/", "."}
	)

	for _, bind := range container.hostConfig.Binds {
		// FIXME: factorize bind parsing in parseBind
		var (
			src, dst, mode string
			arr            = strings.Split(bind, ":")
		)

		if len(arr) == 2 {
			src = arr[0]
			dst = arr[1]
			mode = "rw"
		} else if len(arr) == 3 {
			src = arr[0]
			dst = arr[1]
			mode = arr[2]
		} else {
			return nil, fmt.Errorf("Invalid bind specification: %s", bind)
		}

		// Bail if trying to mount to an illegal destination
		for _, illegal := range illegalDsts {
			if dst == illegal {
				return nil, fmt.Errorf("Illegal bind destination: %s", dst)
			}
		}

		bindMap := BindMap{
			SrcPath: src,
			DstPath: dst,
			Mode:    mode,
		}
		binds[filepath.Clean(dst)] = bindMap
	}
	return binds, nil
}

func createVolumes(container *Container) error {
	binds, err := getBindMap(container)
	if err != nil {
		return err
	}

	// Create the requested volumes if they don't exist
	for volPath := range container.Config.Volumes {
		if err := initializeVolume(container, volPath, binds); err != nil {
			return err
		}
	}

	for volPath := range binds {
		if err := initializeVolume(container, volPath, binds); err != nil {
			return err
		}
	}
	return nil
}

func createIfNotExists(path string, isDir bool) error {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			if isDir {
				if err := os.MkdirAll(path, 0755); err != nil {
					return err
				}
			} else {
				if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
					return err
				}
				f, err := os.OpenFile(path, os.O_CREATE, 0755)
				if err != nil {
					return err
				}
				defer f.Close()
			}
		}
	}
	return nil
}

func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error {
	volumesDriver := container.daemon.volumes.Driver()
	volPath = filepath.Clean(volPath)
	// Skip existing volumes
	if _, exists := container.Volumes[volPath]; exists {
		return nil
	}

	var (
		srcPath     string
		isBindMount bool
		volIsDir    = true

		srcRW = false
	)

	// If an external bind is defined for this volume, use that as a source
	if bindMap, exists := binds[volPath]; exists {
		isBindMount = true
		srcPath = bindMap.SrcPath
		if !filepath.IsAbs(srcPath) {
			return fmt.Errorf("%s must be an absolute path", srcPath)
		}
		if strings.ToLower(bindMap.Mode) == "rw" {
			srcRW = true
		}
		if stat, err := os.Stat(bindMap.SrcPath); err != nil {
			return err
		} else {
			volIsDir = stat.IsDir()
		}
		// Otherwise create an directory in $ROOT/volumes/ and use that
	} else {
		// Do not pass a container as the parameter for the volume creation.
		// The graph driver using the container's information ( Image ) to
		// create the parent.
		c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil)
		if err != nil {
			return err
		}
		srcPath, err = volumesDriver.Get(c.ID, "")
		if err != nil {
			return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
		}
		srcRW = true // RW by default
	}

	if p, err := filepath.EvalSymlinks(srcPath); err != nil {
		return err
	} else {
		srcPath = p
	}

	// Create the mountpoint
	rootVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs)
	if err != nil {
		return err
	}

	newVolPath, err := filepath.Rel(container.basefs, rootVolPath)
	if err != nil {
		return err
	}
	newVolPath = "/" + newVolPath

	if volPath != newVolPath {
		delete(container.Volumes, volPath)
		delete(container.VolumesRW, volPath)
	}

	container.Volumes[newVolPath] = srcPath
	container.VolumesRW[newVolPath] = srcRW

	if err := createIfNotExists(rootVolPath, volIsDir); err != nil {
		return err
	}

	// Do not copy or change permissions if we are mounting from the host
	if srcRW && !isBindMount {
		if err := copyExistingContents(rootVolPath, srcPath); err != nil {
			return err
		}
	}
	return nil
}

func copyExistingContents(rootVolPath, srcPath string) error {
	volList, err := ioutil.ReadDir(rootVolPath)
	if err != nil {
		return err
	}

	if len(volList) > 0 {
		srcList, err := ioutil.ReadDir(srcPath)
		if err != nil {
			return err
		}

		if len(srcList) == 0 {
			// If the source volume is empty copy files from the root into the volume
			if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
				return err
			}
		}
	}

	var (
		stat    syscall.Stat_t
		srcStat syscall.Stat_t
	)

	if err := syscall.Stat(rootVolPath, &stat); err != nil {
		return err
	}
	if err := syscall.Stat(srcPath, &srcStat); err != nil {
		return err
	}
	// Change the source volume's ownership if it differs from the root
	// files that were just copied
	if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
		if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Ensure that ownership and perms are copied to volume<commit_after>package daemon

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/dotcloud/docker/archive"
	"github.com/dotcloud/docker/daemon/execdriver"
	"github.com/dotcloud/docker/pkg/symlink"
)

type BindMap struct {
	SrcPath string
	DstPath string
	Mode    string
}

func prepareVolumesForContainer(container *Container) error {
	if container.Volumes == nil || len(container.Volumes) == 0 {
		container.Volumes = make(map[string]string)
		container.VolumesRW = make(map[string]bool)
		if err := applyVolumesFrom(container); err != nil {
			return err
		}
	}

	if err := createVolumes(container); err != nil {
		return err
	}
	return nil
}

func setupMountsForContainer(container *Container) error {
	mounts := []execdriver.Mount{
		{container.daemon.sysInitPath, "/.dockerinit", false, true},
		{container.ResolvConfPath, "/etc/resolv.conf", false, true},
	}

	if container.HostnamePath != "" {
		mounts = append(mounts, execdriver.Mount{container.HostnamePath, "/etc/hostname", false, true})
	}

	if container.HostsPath != "" {
		mounts = append(mounts, execdriver.Mount{container.HostsPath, "/etc/hosts", false, true})
	}

	// Mount user specified volumes
	// Note, these are not private because you may want propagation of (un)mounts from host
	// volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you
	// want this new mount in the container
	for r, v := range container.Volumes {
		mounts = append(mounts, execdriver.Mount{v, r, container.VolumesRW[r], false})
	}

	container.command.Mounts = mounts

	return nil
}

func applyVolumesFrom(container *Container) error {
	volumesFrom := container.hostConfig.VolumesFrom
	if len(volumesFrom) > 0 {
		for _, containerSpec := range volumesFrom {
			var (
				mountRW   = true
				specParts = strings.SplitN(containerSpec, ":", 2)
			)

			switch len(specParts) {
			case 0:
				return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
			case 2:
				switch specParts[1] {
				case "ro":
					mountRW = false
				case "rw": // mountRW is already true
				default:
					return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
				}
			}

			c := container.daemon.Get(specParts[0])
			if c == nil {
				return fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0])
			}

			if err := c.Mount(); err != nil {
				return fmt.Errorf("Container %s failed to mount. Impossible to mount its volumes", specParts[0])
			}
			defer c.Unmount()

			for volPath, id := range c.Volumes {
				if _, exists := container.Volumes[volPath]; exists {
					continue
				}

				stat, err := os.Stat(c.getResourcePath(volPath))
				if err != nil {
					return err
				}

				if err := createIfNotExists(container.getResourcePath(volPath), stat.IsDir()); err != nil {
					return err
				}

				container.Volumes[volPath] = id
				if isRW, exists := c.VolumesRW[volPath]; exists {
					container.VolumesRW[volPath] = isRW && mountRW
				}
			}

		}
	}
	return nil
}

func getBindMap(container *Container) (map[string]BindMap, error) {
	var (
		// Create the requested bind mounts
		binds = make(map[string]BindMap)
		// Define illegal container destinations
		illegalDsts = []string{"/", "."}
	)

	for _, bind := range container.hostConfig.Binds {
		// FIXME: factorize bind parsing in parseBind
		var (
			src, dst, mode string
			arr            = strings.Split(bind, ":")
		)

		if len(arr) == 2 {
			src = arr[0]
			dst = arr[1]
			mode = "rw"
		} else if len(arr) == 3 {
			src = arr[0]
			dst = arr[1]
			mode = arr[2]
		} else {
			return nil, fmt.Errorf("Invalid bind specification: %s", bind)
		}

		// Bail if trying to mount to an illegal destination
		for _, illegal := range illegalDsts {
			if dst == illegal {
				return nil, fmt.Errorf("Illegal bind destination: %s", dst)
			}
		}

		bindMap := BindMap{
			SrcPath: src,
			DstPath: dst,
			Mode:    mode,
		}
		binds[filepath.Clean(dst)] = bindMap
	}
	return binds, nil
}

func createVolumes(container *Container) error {
	binds, err := getBindMap(container)
	if err != nil {
		return err
	}

	// Create the requested volumes if they don't exist
	for volPath := range container.Config.Volumes {
		if err := initializeVolume(container, volPath, binds); err != nil {
			return err
		}
	}

	for volPath := range binds {
		if err := initializeVolume(container, volPath, binds); err != nil {
			return err
		}
	}
	return nil
}

func createIfNotExists(destination string, isDir bool) error {
	if _, err := os.Stat(destination); err != nil && os.IsNotExist(err) {
		if isDir {
			if err := os.MkdirAll(destination, 0755); err != nil {
				return err
			}
		} else {
			if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil {
				return err
			}

			f, err := os.OpenFile(destination, os.O_CREATE, 0755)
			if err != nil {
				return err
			}
			f.Close()
		}
	}

	return nil
}

func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error {
	volumesDriver := container.daemon.volumes.Driver()
	volPath = filepath.Clean(volPath)

	// Skip existing volumes
	if _, exists := container.Volumes[volPath]; exists {
		return nil
	}

	var (
		destination string
		isBindMount bool
		volIsDir    = true

		srcRW = false
	)

	// If an external bind is defined for this volume, use that as a source
	if bindMap, exists := binds[volPath]; exists {
		isBindMount = true
		destination = bindMap.SrcPath

		if !filepath.IsAbs(destination) {
			return fmt.Errorf("%s must be an absolute path", destination)
		}

		if strings.ToLower(bindMap.Mode) == "rw" {
			srcRW = true
		}

		if stat, err := os.Stat(bindMap.SrcPath); err != nil {
			return err
		} else {
			volIsDir = stat.IsDir()
		}
	} else {
		// Do not pass a container as the parameter for the volume creation.
		// The graph driver using the container's information ( Image ) to
		// create the parent.
		c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil)
		if err != nil {
			return err
		}

		destination, err = volumesDriver.Get(c.ID, "")
		if err != nil {
			return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
		}

		srcRW = true
	}

	if p, err := filepath.EvalSymlinks(destination); err != nil {
		return err
	} else {
		destination = p
	}

	// Create the mountpoint
	source, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs)
	if err != nil {
		return err
	}

	newVolPath, err := filepath.Rel(container.basefs, source)
	if err != nil {
		return err
	}
	newVolPath = "/" + newVolPath

	if volPath != newVolPath {
		delete(container.Volumes, volPath)
		delete(container.VolumesRW, volPath)
	}

	container.Volumes[newVolPath] = destination
	container.VolumesRW[newVolPath] = srcRW

	if err := createIfNotExists(source, volIsDir); err != nil {
		return err
	}

	// Do not copy or change permissions if we are mounting from the host
	if srcRW && !isBindMount {
		if err := copyExistingContents(source, destination); err != nil {
			return err
		}
	}
	return nil
}

func copyExistingContents(source, destination string) error {
	volList, err := ioutil.ReadDir(source)
	if err != nil {
		return err
	}

	if len(volList) > 0 {
		srcList, err := ioutil.ReadDir(destination)
		if err != nil {
			return err
		}

		if len(srcList) == 0 {
			// If the source volume is empty copy files from the root into the volume
			if err := archive.CopyWithTar(source, destination); err != nil {
				return err
			}
		}
	}

	return copyOwnership(source, destination)
}

// copyOwnership copies the permissions and uid:gid of the source file
// into the destination file
func copyOwnership(source, destination string) error {
	var stat syscall.Stat_t

	if err := syscall.Stat(source, &stat); err != nil {
		return err
	}

	if err := os.Chown(destination, int(stat.Uid), int(stat.Gid)); err != nil {
		return err
	}

	return os.Chmod(destination, os.FileMode(stat.Mode))
}
<|endoftext|>


<commit_before>package ohgi

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/hico-horiuchi/ohgi/sensu"
)

func GetClients(api *sensu.API, limit int, offset int) string {
	clients, err := api.GetClients(limit, offset)
	checkError(err)

	if len(clients) == 0 {
		return "No clients\n"
	}

	table := newUitable()
	table.AddRow(bold("NAME"), bold("ADDRESS"), bold("TIMESTAMP"))
	for _, client := range clients {
		table.AddRow(
			client.Name,
			client.Address,
			client.Timestamp,
		)
	}

	return fmt.Sprintln(table)
}

func GetClientsWildcard(api *sensu.API, pattern string) string {
	var match []string
	var matches []int
	var client sensu.ClientStruct

	clients, err := api.GetClients(-1, -1)
	checkError(err)

	re := regexp.MustCompile("^" + strings.Replace(pattern, "*", ".*", -1) + "$")
	for i, client := range clients {
		match = re.FindStringSubmatch(client.Name)
		if len(match) > 0 {
			matches = append(matches, i)
		}
	}

	if len(matches) == 0 {
		return "No clients that matches " + pattern + "\n"
	}

	table := newUitable()
	table.AddRow(bold("NAME"), bold("ADDRESS"), bold("TIMESTAMP"))
	for _, i := range matches {
		client = clients[i]
		table.AddRow(
			client.Name,
			client.Address,
			client.Timestamp,
		)
	}

	return fmt.Sprintln(table)
}

func GetClientsClient(api *sensu.API, name string) string {
	client, err := api.GetClientsClient(name)
	checkError(err)

	table := newUitable(true)
	table.AddRow(bold("NAME:"), client.Name)
	table.AddRow(bold("ADDRESS:"), client.Address)
	table.AddRow(bold("SUBSCRIPTIONS:"), strings.Join(client.Subscriptions, ", "))
	table.AddRow(bold("TIMESTAMP:"), utoa(client.Timestamp))
	table.AddRow(bold("VERSION:"), client.Version)

	return fmt.Sprintln(table)
}

func PostClients(api *sensu.API, name string, address string, subscriptions []string) string {
	err := api.PostClients(name, address, subscriptions)
	checkError(err)

	return "Created\n"
}

func DeleteClientsClient(api *sensu.API, name string) string {
	err := api.DeleteClientsClient(name)
	checkError(err)

	return "Accepted\n"
}
<commit_msg>forget conversion of time to string<commit_after>package ohgi

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/hico-horiuchi/ohgi/sensu"
)

func GetClients(api *sensu.API, limit int, offset int) string {
	clients, err := api.GetClients(limit, offset)
	checkError(err)

	if len(clients) == 0 {
		return "No clients\n"
	}

	table := newUitable()
	table.AddRow(bold("NAME"), bold("ADDRESS"), bold("TIMESTAMP"))
	for _, client := range clients {
		table.AddRow(
			client.Name,
			client.Address,
			utoa(client.Timestamp),
		)
	}

	return fmt.Sprintln(table)
}

func GetClientsWildcard(api *sensu.API, pattern string) string {
	var match []string
	var matches []int
	var client sensu.ClientStruct

	clients, err := api.GetClients(-1, -1)
	checkError(err)

	re := regexp.MustCompile("^" + strings.Replace(pattern, "*", ".*", -1) + "$")
	for i, client := range clients {
		match = re.FindStringSubmatch(client.Name)
		if len(match) > 0 {
			matches = append(matches, i)
		}
	}

	if len(matches) == 0 {
		return "No clients that matches " + pattern + "\n"
	}

	table := newUitable()
	table.AddRow(bold("NAME"), bold("ADDRESS"), bold("TIMESTAMP"))
	for _, i := range matches {
		client = clients[i]
		table.AddRow(
			client.Name,
			client.Address,
			utoa(client.Timestamp),
		)
	}

	return fmt.Sprintln(table)
}

func GetClientsClient(api *sensu.API, name string) string {
	client, err := api.GetClientsClient(name)
	checkError(err)

	table := newUitable(true)
	table.AddRow(bold("NAME:"), client.Name)
	table.AddRow(bold("ADDRESS:"), client.Address)
	table.AddRow(bold("SUBSCRIPTIONS:"), strings.Join(client.Subscriptions, ", "))
	table.AddRow(bold("TIMESTAMP:"), utoa(client.Timestamp))
	table.AddRow(bold("VERSION:"), client.Version)

	return fmt.Sprintln(table)
}

func PostClients(api *sensu.API, name string, address string, subscriptions []string) string {
	err := api.PostClients(name, address, subscriptions)
	checkError(err)

	return "Created\n"
}

func DeleteClientsClient(api *sensu.API, name string) string {
	err := api.DeleteClientsClient(name)
	checkError(err)

	return "Accepted\n"
}
<|endoftext|>


<commit_before>package gitmedia

import (
	"bytes"
	"testing"
)

func TestEncode(t *testing.T) {
	var buf bytes.Buffer
	n, err := Encode(&buf, "abc")
	if err != nil {
		t.Errorf("Error encoding: %s", err)
	}

	if n != len(MediaWarning)+3 {
		t.Errorf("wrong number of written bytes")
	}

	header := make([]byte, len(MediaWarning))
	buf.Read(header)

	if head := string(header); head != string(MediaWarning) {
		t.Errorf("Media warning not read:\n", head)
	}

	shabytes := make([]byte, 3)
	buf.Read(shabytes)

	if sha := string(shabytes); sha != "abc" {
		t.Errorf("Invalid sha: %#v", sha)
	}
}

func TestDecode(t *testing.T) {
	buf := bytes.NewBufferString("# comment comment\n# comment\n #comment\nabc\n")
	if sha, _ := Decode(buf); sha != "abc" {
		t.Errorf("Invalid SHA: %#v", sha)
	}
}
<commit_msg>アアー アアアア アーアア<commit_after>package gitmedia

import (
	"bytes"
	"testing"
)

func TestEncode(t *testing.T) {
	var buf bytes.Buffer
	n, err := Encode(&buf, "abc")
	if err != nil {
		t.Errorf("Error encoding: %s", err)
	}

	if n != len(MediaWarning)+4 {
		t.Errorf("wrong number of written bytes")
	}

	header := make([]byte, len(MediaWarning))
	buf.Read(header)

	if head := string(header); head != string(MediaWarning) {
		t.Errorf("Media warning not read:\n", head)
	}

	shabytes := make([]byte, 3)
	buf.Read(shabytes)

	if sha := string(shabytes); sha != "abc" {
		t.Errorf("Invalid sha: %#v", sha)
	}
}

func TestDecode(t *testing.T) {
	buf := bytes.NewBufferString("# comment comment\n# comment\n #comment\nabc\n")
	if sha, _ := Decode(buf); sha != "abc" {
		t.Errorf("Invalid SHA: %#v", sha)
	}
}
<|endoftext|>


<commit_before>package renter

import (
	"crypto/rand"
	"errors"
	"io"
	"os"
	"time"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
)

// A Download is a download that has been queued by the renter.
type Download struct {
	completed   bool
	destination string
	nickname    string

	renter *Renter
}

var (
	downloadAttempts = 5
)

// downloadPiece attempts to retrieve a file from a host.
func (r *Renter) downloadPiece(piece FilePiece, path string) error {
	return r.gateway.RPC(piece.HostIP, "RetrieveFile", func(conn modules.NetConn) (err error) {
		// Send the id of the contract for the file piece we're requesting. The
		// response will be the file piece contents.
		if err = conn.WriteObject(piece.ContractID); err != nil {
			return
		}

		// Create the file on disk.
		file, err := os.Create(path)
		if err != nil {
			return
		}
		defer file.Close()

		// Simultaneously download file and calculate its Merkle root.
		tee := io.TeeReader(
			// use a LimitedReader to ensure we don't read indefinitely
			io.LimitReader(conn, int64(piece.Contract.FileSize)),
			// each byte we read from tee will also be written to file
			file,
		)
		merkleRoot, err := crypto.ReaderMerkleRoot(tee)
		if err != nil {
			return
		}

		if merkleRoot != piece.Contract.FileMerkleRoot {
			return errors.New("host provided a file that's invalid")
		}

		return
	})
}

// Download downloads a file. Mutex conventions are broken to prevent doing
// network communication with io in place.
func (r *Renter) Download(nickname, filename string) error {
	// Grab the set of pieces we're downloading.
	r.mu.RLock()
	var pieces []FilePiece
	_, exists := r.files[nickname]
	if !exists {
		r.mu.RUnlock()
		return errors.New("no file of that nickname")
	}
	for _, piece := range r.files[nickname].pieces {
		if piece.Active {
			pieces = append(pieces, piece)
		}
	}
	r.mu.RUnlock()

	// Create an object for the download in the download queue.
	r.mu.Lock()
	downloadIndex := len(r.downloadQueue)
	r.downloadQueue = append(r.downloadQueue, Download{
		completed:   false,
		destination: filename,
		nickname:    nickname,

		renter: r,
	})
	r.mu.Unlock()

	// We only need one piece, so iterate through the hosts until a download
	// succeeds.
	go func() {
		for i := 0; i < downloadAttempts; i++ {
			for _, piece := range pieces {
				downloadErr := r.downloadPiece(piece, filename)
				if downloadErr == nil {
					// Mark the download as complete.
					r.mu.Lock()
					r.downloadQueue[downloadIndex].completed = true
					r.mu.Unlock()
					return
				}
			}

			// This iteration failed, no hosts returned the piece. Try again
			// after waiting a random amount of time.
			randSource := make([]byte, 1)
			rand.Read(randSource)
			time.Sleep(time.Second * time.Duration(i*i) * time.Duration(randSource[0]))
		}
	}()

	return nil
}

// DownloadQueue returns the list of downloads in the queue.
func (r *Renter) DownloadQueue() []modules.DownloadInfo {
	r.mu.RLock()
	defer r.mu.RUnlock()

	downloads := make([]modules.DownloadInfo, len(r.downloadQueue))
	for i := range r.downloadQueue {
		downloads[i] = &r.downloadQueue[i]
	}
	return downloads
}

// Getter for the completed status of the download.
func (d *Download) Completed() bool {
	d.renter.mu.RLock()
	defer d.renter.mu.RUnlock()
	return d.completed
}

// Getter for the destination of the download.
func (d *Download) Destination() string {
	d.renter.mu.RLock()
	defer d.renter.mu.RUnlock()
	return d.destination
}

// Getter for the nickname of the download.
func (d *Download) Nickname() string {
	d.renter.mu.RLock()
	defer d.renter.mu.RUnlock()
	return d.nickname
}
<commit_msg>downloadPiece deletes bad files<commit_after>package renter

import (
	"crypto/rand"
	"errors"
	"io"
	"os"
	"time"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
)

// A Download is a download that has been queued by the renter.
type Download struct {
	completed   bool
	destination string
	nickname    string

	renter *Renter
}

var (
	downloadAttempts = 5
)

// downloadPiece attempts to retrieve a file from a host.
func (r *Renter) downloadPiece(piece FilePiece, path string) error {
	return r.gateway.RPC(piece.HostIP, "RetrieveFile", func(conn modules.NetConn) (err error) {
		// Send the id of the contract for the file piece we're requesting. The
		// response will be the file piece contents.
		if err = conn.WriteObject(piece.ContractID); err != nil {
			return
		}

		// Create the file on disk.
		file, err := os.Create(path)
		if err != nil {
			return
		}
		defer func() {
			file.Close()
			// If something goes wrong, delete the file.
			if err != nil {
				os.Remove(path)
			}
		}()

		// Simultaneously download file and calculate its Merkle root.
		tee := io.TeeReader(
			// use a LimitedReader to ensure we don't read indefinitely
			io.LimitReader(conn, int64(piece.Contract.FileSize)),
			// each byte we read from tee will also be written to file
			file,
		)
		merkleRoot, err := crypto.ReaderMerkleRoot(tee)
		if err != nil {
			return
		}

		if merkleRoot != piece.Contract.FileMerkleRoot {
			return errors.New("host provided a file that's invalid")
		}

		return
	})
}

// Download downloads a file. Mutex conventions are broken to prevent doing
// network communication with io in place.
func (r *Renter) Download(nickname, filename string) error {
	// Grab the set of pieces we're downloading.
	r.mu.RLock()
	var pieces []FilePiece
	_, exists := r.files[nickname]
	if !exists {
		r.mu.RUnlock()
		return errors.New("no file of that nickname")
	}
	for _, piece := range r.files[nickname].pieces {
		if piece.Active {
			pieces = append(pieces, piece)
		}
	}
	r.mu.RUnlock()

	// Create an object for the download in the download queue.
	r.mu.Lock()
	downloadIndex := len(r.downloadQueue)
	r.downloadQueue = append(r.downloadQueue, Download{
		completed:   false,
		destination: filename,
		nickname:    nickname,

		renter: r,
	})
	r.mu.Unlock()

	// We only need one piece, so iterate through the hosts until a download
	// succeeds.
	go func() {
		for i := 0; i < downloadAttempts; i++ {
			for _, piece := range pieces {
				downloadErr := r.downloadPiece(piece, filename)
				if downloadErr == nil {
					// Mark the download as complete.
					r.mu.Lock()
					r.downloadQueue[downloadIndex].completed = true
					r.mu.Unlock()
					return
				}
			}

			// This iteration failed, no hosts returned the piece. Try again
			// after waiting a random amount of time.
			randSource := make([]byte, 1)
			rand.Read(randSource)
			time.Sleep(time.Second * time.Duration(i*i) * time.Duration(randSource[0]))
		}
	}()

	return nil
}

// DownloadQueue returns the list of downloads in the queue.
func (r *Renter) DownloadQueue() []modules.DownloadInfo {
	r.mu.RLock()
	defer r.mu.RUnlock()

	downloads := make([]modules.DownloadInfo, len(r.downloadQueue))
	for i := range r.downloadQueue {
		downloads[i] = &r.downloadQueue[i]
	}
	return downloads
}

// Getter for the completed status of the download.
func (d *Download) Completed() bool {
	d.renter.mu.RLock()
	defer d.renter.mu.RUnlock()
	return d.completed
}

// Getter for the destination of the download.
func (d *Download) Destination() string {
	d.renter.mu.RLock()
	defer d.renter.mu.RUnlock()
	return d.destination
}

// Getter for the nickname of the download.
func (d *Download) Nickname() string {
	d.renter.mu.RLock()
	defer d.renter.mu.RUnlock()
	return d.nickname
}
<|endoftext|>


<commit_before>package build_cache

import (
	"fmt"
	"regexp"
	"sync"
	"time"

	"github.com/skia-dev/glog"

	"go.skia.org/infra/go/buildbot"
	"go.skia.org/infra/go/timer"
	"go.skia.org/infra/go/util"
)

/*
	Utilities for caching buildbot data.
*/

var (
	// Patterns indicating which bots to skip.
	BOT_BLACKLIST = []*regexp.Regexp{
		regexp.MustCompile(".*-Trybot"),
	}
)

const (
	// How long to keep builds in the cache.
	BUILD_EXPIRATION = 14 * 24 * time.Hour

	// Time period of builds to load at a time.
	BUILD_LOADING_CHUNK = 24 * time.Hour

	// Load all builds for this time period when starting up.
	BUILD_LOADING_PERIOD = 14 * 24 * time.Hour
)

// BuildCache is a struct used for caching build data.
type BuildCache struct {
	byId            map[string]*buildbot.Build
	byCommit        map[string]map[string]*buildbot.BuildSummary
	byTime          *TimeRangeTree
	builders        map[string]bool
	builderComments map[string][]*buildbot.BuilderComment
	lastLoad        time.Time
	mutex           sync.RWMutex
	db              buildbot.DB
	dbId            string
}

// NewBuildCache creates a new BuildCache instance.
func NewBuildCache(db buildbot.DB) (*BuildCache, error) {
	// Start tracking build changes in the DB.
	dbId, err := db.StartTrackingModifiedBuilds()
	if err != nil {
		return nil, err
	}
	bc := &BuildCache{
		builders:        map[string]bool{},
		builderComments: map[string][]*buildbot.BuilderComment{},
		byId:            map[string]*buildbot.Build{},
		byCommit:        map[string]map[string]*buildbot.BuildSummary{},
		byTime:          NewTimeRangeTree(),
		lastLoad:        time.Now(),
		db:              db,
		dbId:            dbId,
	}
	// Populate the cache with data.
	to := time.Now()
	from := to.Add(BUILD_LOADING_CHUNK)
	for time.Now().Sub(from) < BUILD_LOADING_PERIOD {
		glog.Infof("Loading builds from %s to %s", from, to)
		builds, err := db.GetBuildsFromDateRange(from, to)
		if err != nil {
			return nil, err
		}
		if err := bc.updateWithBuilds(builds); err != nil {
			return nil, err
		}
		to = from
		from = to.Add(-BUILD_LOADING_CHUNK)
	}
	if err := bc.update(); err != nil {
		return nil, err
	}
	go func() {
		for _ = range time.Tick(time.Minute) {
			if err := bc.update(); err != nil {
				glog.Error(err)
			}
		}
	}()
	return bc, nil
}

// update obtains the set of modified builds since the last update
// and inserts them into the cache.
func (c *BuildCache) update() error {
	builds, err := c.db.GetModifiedBuilds(c.dbId)
	if err != nil {
		if time.Now().Sub(c.lastLoad) >= 10*time.Minute {
			glog.Errorf("Failed to GetModifiedBuilds. Attempting to re-establish connection to database.")
			id, err := c.db.StartTrackingModifiedBuilds()
			if err != nil {
				return err
			}
			c.dbId = id
			b1, err := c.db.GetBuildsFromDateRange(c.lastLoad, time.Now())
			if err != nil {
				return err
			}
			b2, err := c.db.GetModifiedBuilds(c.dbId)
			if err != nil {
				return err
			}
			builds = append(b1, b2...)
			glog.Errorf("Re-connected successfully.")
		} else {
			return err
		}
	}
	if err := c.updateWithBuilds(builds); err != nil {
		return err
	}
	c.evictExpiredBuilds()
	return c.updateBuilderComments()
}

// getBuilderList returns the list of all known builders.
func (c *BuildCache) getBuilderList() []string {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	builderList := make([]string, 0, len(c.builders))
	for b, _ := range c.builders {
		builderList = append(builderList, b)
	}
	return builderList
}

// updateBuilderComments updates the comments for all builders.
func (c *BuildCache) updateBuilderComments() error {
	defer timer.New("BuildCache.updateBuilderComments").Stop()
	builderComments, err := c.db.GetBuildersComments(c.getBuilderList())
	if err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.builderComments = builderComments
	return nil
}

// insert adds the given build to the cache. Assumes the caller holds a lock.
func (c *BuildCache) insert(b *buildbot.Build) {
	idStr := string(b.Id())
	c.byId[idStr] = b
	summary := b.GetSummary()
	for _, h := range b.Commits {
		if _, ok := c.byCommit[h]; !ok {
			c.byCommit[h] = map[string]*buildbot.BuildSummary{}
		}
		c.byCommit[h][b.Builder] = summary
	}
	if b.IsFinished() {
		c.byTime.Insert(b.Finished, idStr)
	}
	c.builders[b.Builder] = true
}

// get retrieves the given build from the cache. Assumes the caller holds a lock.
func (c *BuildCache) get(id string) *buildbot.Build {
	return c.byId[id]
}

// delete removes the given build from the cache. Assumes the caller holds a lock.
func (c *BuildCache) delete(id string) {
	b := c.byId[id]
	for _, h := range b.Commits {
		if _, ok := c.byCommit[h][b.Builder]; ok {
			delete(c.byCommit[h], b.Builder)
		}
	}
	c.byTime.Delete(b.Finished, id)
	delete(c.byId, id)
}

// evictExpiredBuilds removes builds which have expired from the cache.
func (c *BuildCache) evictExpiredBuilds() {
	defer timer.New("BuildCache.evictExpiredBuilds").Stop()
	c.mutex.Lock()
	defer c.mutex.Unlock()
	expiredBefore := time.Now().Add(-BUILD_EXPIRATION)
	expiredIds := c.byTime.GetRange(util.TimeUnixZero, expiredBefore)
	for _, id := range expiredIds {
		c.delete(id)
	}
	glog.Infof("Deleted %d expired builds.", len(expiredIds))
}

// updateWithBuilds inserts the given builds into the cache.
func (c *BuildCache) updateWithBuilds(builds []*buildbot.Build) error {
	defer timer.New("  BuildCache locked").Stop()
	c.mutex.Lock()
	defer c.mutex.Unlock()
	glog.Infof("Inserting %d builds.", len(builds))
	for _, b := range builds {
		idStr := string(b.Id())
		if c.get(idStr) != nil {
			c.delete(idStr)
		}
		c.insert(b)
	}
	return nil
}

// GetBuildsForCommits returns the build data for the given commits.
func (c *BuildCache) GetBuildsForCommits(commits []string) (map[string]map[string]*buildbot.BuildSummary, error) {
	defer timer.New("BuildCache.GetBuildsForCommits").Stop()
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	byCommit := map[string]map[string]*buildbot.BuildSummary{}
	for _, hash := range commits {
		builds, ok := c.byCommit[hash]
		if ok {
			cpyBuilds := map[string]*buildbot.BuildSummary{}
			for k, v := range builds {
				cpyBuilds[k] = v
			}
			byCommit[hash] = cpyBuilds
		}
	}
	return byCommit, nil
}

// GetBuildsFromDateRange returns builds within the given date range.
func (c *BuildCache) GetBuildsFromDateRange(from, to time.Time) ([]*buildbot.Build, error) {
	defer timer.New("BuildCache.GetBuildsFromDateRange").Stop()
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	ids := c.byTime.GetRange(from, to)
	rv := make([]*buildbot.Build, 0, len(ids))
	for _, id := range ids {
		rv = append(rv, c.byId[id])
	}
	return rv, nil
}

// GetBuildersComments returns comments for all builders.
func (c *BuildCache) GetBuildersComments() map[string][]*buildbot.BuilderComment {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	rv := map[string][]*buildbot.BuilderComment{}
	for k, v := range c.builderComments {
		cpy := make([]*buildbot.BuilderComment, len(v))
		copy(cpy, v)
		rv[k] = cpy
	}
	return c.builderComments
}

// AddBuilderComment adds a comment for the given builder.
func (c *BuildCache) AddBuilderComment(builder string, comment *buildbot.BuilderComment) error {
	if err := c.db.PutBuilderComment(comment); err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.builderComments[builder] = append(c.builderComments[builder], comment)
	return nil
}

// DeleteBuilderComment deletes the given comment.
func (c *BuildCache) DeleteBuilderComment(builder string, commentId int64) error {
	if err := c.db.DeleteBuilderComment(commentId); err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	idx := -1
	for i, comment := range c.builderComments[builder] {
		if comment.Id == commentId {
			idx = i
		}
	}
	if idx == -1 {
		return fmt.Errorf("No such comment")
	}
	c.builderComments[builder] = append(c.builderComments[builder][:idx], c.builderComments[builder][idx+1:]...)
	return nil
}

// refreshBuild reloads the given build from the DB.
func (c *BuildCache) refreshBuild(id buildbot.BuildID) error {
	b, err := c.db.GetBuild(id)
	if err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.delete(string(b.Id()))
	c.insert(b)
	return nil
}

// AddBuildComment adds the given comment to the given build.
func (c *BuildCache) AddBuildComment(master, builder string, number int, comment *buildbot.BuildComment) error {
	if err := c.db.PutBuildComment(master, builder, number, comment); err != nil {
		return fmt.Errorf("Failed to add comment: %s", err)
	}
	return c.refreshBuild(buildbot.MakeBuildID(master, builder, number))
}

// DeleteBuildComment deletes the given comment from the given build.
func (c *BuildCache) DeleteBuildComment(master, builder string, number int, commentId int64) error {
	if err := c.db.DeleteBuildComment(master, builder, number, commentId); err != nil {
		return fmt.Errorf("Failed to delete comment: %s", err)
	}
	return c.refreshBuild(buildbot.MakeBuildID(master, builder, number))
}

// GetBuildsForCommit returns the builds which ran at the given commit.
func (c *BuildCache) GetBuildsForCommit(hash string) ([]*buildbot.BuildSummary, error) {
	builds, err := c.GetBuildsForCommits([]string{hash})
	if err != nil {
		return nil, fmt.Errorf("Failed to get build data for commit: %v", err)
	}
	rv := make([]*buildbot.BuildSummary, 0, len(builds[hash]))
	for _, b := range builds[hash] {
		rv = append(rv, b)
	}
	return rv, nil
}
<commit_msg>Really fix the builderComments race in Status<commit_after>package build_cache

import (
	"fmt"
	"regexp"
	"sync"
	"time"

	"github.com/skia-dev/glog"

	"go.skia.org/infra/go/buildbot"
	"go.skia.org/infra/go/timer"
	"go.skia.org/infra/go/util"
)

/*
	Utilities for caching buildbot data.
*/

var (
	// Patterns indicating which bots to skip.
	BOT_BLACKLIST = []*regexp.Regexp{
		regexp.MustCompile(".*-Trybot"),
	}
)

const (
	// How long to keep builds in the cache.
	BUILD_EXPIRATION = 14 * 24 * time.Hour

	// Time period of builds to load at a time.
	BUILD_LOADING_CHUNK = 24 * time.Hour

	// Load all builds for this time period when starting up.
	BUILD_LOADING_PERIOD = 14 * 24 * time.Hour
)

// BuildCache is a struct used for caching build data.
type BuildCache struct {
	byId            map[string]*buildbot.Build
	byCommit        map[string]map[string]*buildbot.BuildSummary
	byTime          *TimeRangeTree
	builders        map[string]bool
	builderComments map[string][]*buildbot.BuilderComment
	lastLoad        time.Time
	mutex           sync.RWMutex
	db              buildbot.DB
	dbId            string
}

// NewBuildCache creates a new BuildCache instance.
func NewBuildCache(db buildbot.DB) (*BuildCache, error) {
	// Start tracking build changes in the DB.
	dbId, err := db.StartTrackingModifiedBuilds()
	if err != nil {
		return nil, err
	}
	bc := &BuildCache{
		builders:        map[string]bool{},
		builderComments: map[string][]*buildbot.BuilderComment{},
		byId:            map[string]*buildbot.Build{},
		byCommit:        map[string]map[string]*buildbot.BuildSummary{},
		byTime:          NewTimeRangeTree(),
		lastLoad:        time.Now(),
		db:              db,
		dbId:            dbId,
	}
	// Populate the cache with data.
	to := time.Now()
	from := to.Add(BUILD_LOADING_CHUNK)
	for time.Now().Sub(from) < BUILD_LOADING_PERIOD {
		glog.Infof("Loading builds from %s to %s", from, to)
		builds, err := db.GetBuildsFromDateRange(from, to)
		if err != nil {
			return nil, err
		}
		if err := bc.updateWithBuilds(builds); err != nil {
			return nil, err
		}
		to = from
		from = to.Add(-BUILD_LOADING_CHUNK)
	}
	if err := bc.update(); err != nil {
		return nil, err
	}
	go func() {
		for _ = range time.Tick(time.Minute) {
			if err := bc.update(); err != nil {
				glog.Error(err)
			}
		}
	}()
	return bc, nil
}

// update obtains the set of modified builds since the last update
// and inserts them into the cache.
func (c *BuildCache) update() error {
	builds, err := c.db.GetModifiedBuilds(c.dbId)
	if err != nil {
		if time.Now().Sub(c.lastLoad) >= 10*time.Minute {
			glog.Errorf("Failed to GetModifiedBuilds. Attempting to re-establish connection to database.")
			id, err := c.db.StartTrackingModifiedBuilds()
			if err != nil {
				return err
			}
			c.dbId = id
			b1, err := c.db.GetBuildsFromDateRange(c.lastLoad, time.Now())
			if err != nil {
				return err
			}
			b2, err := c.db.GetModifiedBuilds(c.dbId)
			if err != nil {
				return err
			}
			builds = append(b1, b2...)
			glog.Errorf("Re-connected successfully.")
		} else {
			return err
		}
	}
	if err := c.updateWithBuilds(builds); err != nil {
		return err
	}
	c.evictExpiredBuilds()
	return c.updateBuilderComments()
}

// getBuilderList returns the list of all known builders.
func (c *BuildCache) getBuilderList() []string {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	builderList := make([]string, 0, len(c.builders))
	for b, _ := range c.builders {
		builderList = append(builderList, b)
	}
	return builderList
}

// updateBuilderComments updates the comments for all builders.
func (c *BuildCache) updateBuilderComments() error {
	defer timer.New("BuildCache.updateBuilderComments").Stop()
	builderComments, err := c.db.GetBuildersComments(c.getBuilderList())
	if err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.builderComments = builderComments
	return nil
}

// insert adds the given build to the cache. Assumes the caller holds a lock.
func (c *BuildCache) insert(b *buildbot.Build) {
	idStr := string(b.Id())
	c.byId[idStr] = b
	summary := b.GetSummary()
	for _, h := range b.Commits {
		if _, ok := c.byCommit[h]; !ok {
			c.byCommit[h] = map[string]*buildbot.BuildSummary{}
		}
		c.byCommit[h][b.Builder] = summary
	}
	if b.IsFinished() {
		c.byTime.Insert(b.Finished, idStr)
	}
	c.builders[b.Builder] = true
}

// get retrieves the given build from the cache. Assumes the caller holds a lock.
func (c *BuildCache) get(id string) *buildbot.Build {
	return c.byId[id]
}

// delete removes the given build from the cache. Assumes the caller holds a lock.
func (c *BuildCache) delete(id string) {
	b := c.byId[id]
	for _, h := range b.Commits {
		if _, ok := c.byCommit[h][b.Builder]; ok {
			delete(c.byCommit[h], b.Builder)
		}
	}
	c.byTime.Delete(b.Finished, id)
	delete(c.byId, id)
}

// evictExpiredBuilds removes builds which have expired from the cache.
func (c *BuildCache) evictExpiredBuilds() {
	defer timer.New("BuildCache.evictExpiredBuilds").Stop()
	c.mutex.Lock()
	defer c.mutex.Unlock()
	expiredBefore := time.Now().Add(-BUILD_EXPIRATION)
	expiredIds := c.byTime.GetRange(util.TimeUnixZero, expiredBefore)
	for _, id := range expiredIds {
		c.delete(id)
	}
	glog.Infof("Deleted %d expired builds.", len(expiredIds))
}

// updateWithBuilds inserts the given builds into the cache.
func (c *BuildCache) updateWithBuilds(builds []*buildbot.Build) error {
	defer timer.New("  BuildCache locked").Stop()
	c.mutex.Lock()
	defer c.mutex.Unlock()
	glog.Infof("Inserting %d builds.", len(builds))
	for _, b := range builds {
		idStr := string(b.Id())
		if c.get(idStr) != nil {
			c.delete(idStr)
		}
		c.insert(b)
	}
	return nil
}

// GetBuildsForCommits returns the build data for the given commits.
func (c *BuildCache) GetBuildsForCommits(commits []string) (map[string]map[string]*buildbot.BuildSummary, error) {
	defer timer.New("BuildCache.GetBuildsForCommits").Stop()
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	byCommit := map[string]map[string]*buildbot.BuildSummary{}
	for _, hash := range commits {
		builds, ok := c.byCommit[hash]
		if ok {
			cpyBuilds := map[string]*buildbot.BuildSummary{}
			for k, v := range builds {
				cpyBuilds[k] = v
			}
			byCommit[hash] = cpyBuilds
		}
	}
	return byCommit, nil
}

// GetBuildsFromDateRange returns builds within the given date range.
func (c *BuildCache) GetBuildsFromDateRange(from, to time.Time) ([]*buildbot.Build, error) {
	defer timer.New("BuildCache.GetBuildsFromDateRange").Stop()
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	ids := c.byTime.GetRange(from, to)
	rv := make([]*buildbot.Build, 0, len(ids))
	for _, id := range ids {
		rv = append(rv, c.byId[id])
	}
	return rv, nil
}

// GetBuildersComments returns comments for all builders.
func (c *BuildCache) GetBuildersComments() map[string][]*buildbot.BuilderComment {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	rv := map[string][]*buildbot.BuilderComment{}
	for k, v := range c.builderComments {
		cpy := make([]*buildbot.BuilderComment, len(v))
		copy(cpy, v)
		rv[k] = cpy
	}
	return rv
}

// AddBuilderComment adds a comment for the given builder.
func (c *BuildCache) AddBuilderComment(builder string, comment *buildbot.BuilderComment) error {
	if err := c.db.PutBuilderComment(comment); err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.builderComments[builder] = append(c.builderComments[builder], comment)
	return nil
}

// DeleteBuilderComment deletes the given comment.
func (c *BuildCache) DeleteBuilderComment(builder string, commentId int64) error {
	if err := c.db.DeleteBuilderComment(commentId); err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	idx := -1
	for i, comment := range c.builderComments[builder] {
		if comment.Id == commentId {
			idx = i
		}
	}
	if idx == -1 {
		return fmt.Errorf("No such comment")
	}
	c.builderComments[builder] = append(c.builderComments[builder][:idx], c.builderComments[builder][idx+1:]...)
	return nil
}

// refreshBuild reloads the given build from the DB.
func (c *BuildCache) refreshBuild(id buildbot.BuildID) error {
	b, err := c.db.GetBuild(id)
	if err != nil {
		return err
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.delete(string(b.Id()))
	c.insert(b)
	return nil
}

// AddBuildComment adds the given comment to the given build.
func (c *BuildCache) AddBuildComment(master, builder string, number int, comment *buildbot.BuildComment) error {
	if err := c.db.PutBuildComment(master, builder, number, comment); err != nil {
		return fmt.Errorf("Failed to add comment: %s", err)
	}
	return c.refreshBuild(buildbot.MakeBuildID(master, builder, number))
}

// DeleteBuildComment deletes the given comment from the given build.
func (c *BuildCache) DeleteBuildComment(master, builder string, number int, commentId int64) error {
	if err := c.db.DeleteBuildComment(master, builder, number, commentId); err != nil {
		return fmt.Errorf("Failed to delete comment: %s", err)
	}
	return c.refreshBuild(buildbot.MakeBuildID(master, builder, number))
}

// GetBuildsForCommit returns the builds which ran at the given commit.
func (c *BuildCache) GetBuildsForCommit(hash string) ([]*buildbot.BuildSummary, error) {
	builds, err := c.GetBuildsForCommits([]string{hash})
	if err != nil {
		return nil, fmt.Errorf("Failed to get build data for commit: %v", err)
	}
	rv := make([]*buildbot.BuildSummary, 0, len(builds[hash]))
	for _, b := range builds[hash] {
		rv = append(rv, b)
	}
	return rv, nil
}
<|endoftext|>


<commit_before>package mvoe

func findMaxValueOfEquation(points [][]int, k int) int {
	return -1
}<commit_msg>solve 1499 use brute force but got tle<commit_after>package mvoe

import "math"

func findMaxValueOfEquation(points [][]int, k int) int {
	return useBruteForce(points, k)
}

// useBruteForce time complexity O(N^2), space complexity O(N)
// got tle
func useBruteForce(points [][]int, k int) int {
	type pair struct {
		x, y int
	}
	n := len(points)
	pairs := make([]pair, 0, n)
	for i := 0; i < n-1; i++ {
		for j := i + 1; j < n; j++ {
			if points[j][0]-points[i][0] <= k {
				pairs = append(pairs, pair{i, j})
			} else {
				break
			}
		}
	}
	ans := math.MinInt32
	for _, p := range pairs {
		i, j := p.x, p.y
		tmp := points[i][1] + points[j][1] + points[j][0] - points[i][0]
		if tmp > ans {
			ans = tmp
		}
	}
	return ans
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 backup_test

import (
	"errors"
	"github.com/jacobsa/aws/sdb"
	"github.com/jacobsa/aws/sdb/mock"
	"github.com/jacobsa/comeback/backup"
	"github.com/jacobsa/comeback/crypto"
	"github.com/jacobsa/comeback/crypto/mock"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestRegistry(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type registryTest struct {
	crypter mock_crypto.MockCrypter
	domain  mock_sdb.MockDomain
}

func (t *registryTest) SetUp(i *TestInfo) {
	t.crypter = mock_crypto.NewMockCrypter(i.MockController, "crypter")
	t.domain = mock_sdb.NewMockDomain(i.MockController, "domain")
}

////////////////////////////////////////////////////////////////////////
// NewRegistry
////////////////////////////////////////////////////////////////////////

type NewRegistryTest struct {
	registryTest

	registry backup.Registry
	err error
}

func init() { RegisterTestSuite(&NewRegistryTest{}) }

func (t *NewRegistryTest) callConstructor() {
	t.registry, t.err = backup.NewRegistry(t.crypter, t.domain)
}

func (t *NewRegistryTest) CallsGetAttributes() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(
		"comeback_marker",
		false,
		ElementsAre("encrypted_data")).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) GetAttributesReturnsError() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("GetAttributes")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) CallsDecrypt() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data", Value: "taco"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(DeepEquals([]byte("taco"))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) DecryptReturnsGenericError() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("Decrypt")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) DecryptReturnsNotAuthenticError() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(Any()).
		WillOnce(oglemock.Return(nil, &crypto.NotAuthenticError{}))

	// Call
	t.callConstructor()

	_, ok := t.err.(*backup.IncompatibleCrypterError)
	AssertTrue(ok, "Error: %v", t.err)

	ExpectThat(t.err, Error(HasSubstr("crypter")))
	ExpectThat(t.err, Error(HasSubstr("not compatible")))
}

func (t *NewRegistryTest) DecryptSucceeds() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(Any()).
		WillOnce(oglemock.Return([]byte{}, nil))

	// Call
	t.callConstructor()

	AssertEq(nil, t.err)
	ExpectNe(nil, t.registry)
}

func (t *NewRegistryTest) CallsEncrypt() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) EncryptReturnsError() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("Encrypt")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) CallsPutAttributes() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ciphertext := []byte("taco")
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return(ciphertext, nil))

	// Domain
	expectedUpdate := sdb.PutUpdate{Name: "encrypted_data", Value: "taco"}
	expectedPrecondition := sdb.Precondition{Name: "encrypted_data"}

	ExpectCall(t.domain, "PutAttributes")(
		"comeback_marker",
		ElementsAre(DeepEquals(expectedUpdate)),
		ElementsAre(Pointee(DeepEquals(expectedPrecondition)))).
		WillOnce(oglemock.Return(errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) PutAttributesReturnsError() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return([]byte{}, nil))

	// Domain
	ExpectCall(t.domain, "PutAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return(errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("PutAttributes")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) PutAttributesSucceeds() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return([]byte{}, nil))

	// Domain
	ExpectCall(t.domain, "PutAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return(nil))

	// Call
	t.callConstructor()

	AssertEq(nil, t.err)
	ExpectNe(nil, t.registry)
}
<commit_msg>Fixed a test bug.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 backup_test

import (
	"errors"
	"github.com/jacobsa/aws/sdb"
	"github.com/jacobsa/aws/sdb/mock"
	"github.com/jacobsa/comeback/backup"
	"github.com/jacobsa/comeback/crypto"
	"github.com/jacobsa/comeback/crypto/mock"
	. "github.com/jacobsa/oglematchers"
	"github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestRegistry(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type registryTest struct {
	crypter mock_crypto.MockCrypter
	domain  mock_sdb.MockDomain
}

func (t *registryTest) SetUp(i *TestInfo) {
	t.crypter = mock_crypto.NewMockCrypter(i.MockController, "crypter")
	t.domain = mock_sdb.NewMockDomain(i.MockController, "domain")
}

////////////////////////////////////////////////////////////////////////
// NewRegistry
////////////////////////////////////////////////////////////////////////

type NewRegistryTest struct {
	registryTest

	registry backup.Registry
	err error
}

func init() { RegisterTestSuite(&NewRegistryTest{}) }

func (t *NewRegistryTest) callConstructor() {
	t.registry, t.err = backup.NewRegistry(t.crypter, t.domain)
}

func (t *NewRegistryTest) CallsGetAttributes() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(
		"comeback_marker",
		false,
		ElementsAre("encrypted_data")).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) GetAttributesReturnsError() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("GetAttributes")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) CallsDecrypt() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data", Value: "taco"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(DeepEquals([]byte("taco"))).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) DecryptReturnsGenericError() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("Decrypt")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) DecryptReturnsNotAuthenticError() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(Any()).
		WillOnce(oglemock.Return(nil, &crypto.NotAuthenticError{}))

	// Call
	t.callConstructor()

	_, ok := t.err.(*backup.IncompatibleCrypterError)
	AssertTrue(ok, "Error: %v", t.err)

	ExpectThat(t.err, Error(HasSubstr("crypter")))
	ExpectThat(t.err, Error(HasSubstr("not compatible")))
}

func (t *NewRegistryTest) DecryptSucceeds() {
	// Domain
	attr := sdb.Attribute{Name: "encrypted_data"}
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{attr}, nil))

	// Crypter
	ExpectCall(t.crypter, "Decrypt")(Any()).
		WillOnce(oglemock.Return([]byte{}, nil))

	// Call
	t.callConstructor()

	AssertEq(nil, t.err)
	ExpectNe(nil, t.registry)
}

func (t *NewRegistryTest) CallsEncrypt() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) EncryptReturnsError() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return(nil, errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("Encrypt")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) CallsPutAttributes() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ciphertext := []byte("taco")
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return(ciphertext, nil))

	// Domain
	expectedUpdate := sdb.PutUpdate{Name: "encrypted_data", Value: "taco"}
	expectedPrecondition := sdb.Precondition{Name: "encrypted_data"}

	ExpectCall(t.domain, "PutAttributes")(
		"comeback_marker",
		ElementsAre(DeepEquals(expectedUpdate)),
		Pointee(DeepEquals(expectedPrecondition))).
		WillOnce(oglemock.Return(errors.New("")))

	// Call
	t.callConstructor()
}

func (t *NewRegistryTest) PutAttributesReturnsError() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return([]byte{}, nil))

	// Domain
	ExpectCall(t.domain, "PutAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return(errors.New("taco")))

	// Call
	t.callConstructor()

	ExpectThat(t.err, Error(HasSubstr("PutAttributes")))
	ExpectThat(t.err, Error(HasSubstr("taco")))
}

func (t *NewRegistryTest) PutAttributesSucceeds() {
	// Domain
	ExpectCall(t.domain, "GetAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return([]sdb.Attribute{}, nil))

	// Crypter
	ExpectCall(t.crypter, "Encrypt")(Any()).
		WillOnce(oglemock.Return([]byte{}, nil))

	// Domain
	ExpectCall(t.domain, "PutAttributes")(Any(), Any(), Any()).
		WillOnce(oglemock.Return(nil))

	// Call
	t.callConstructor()

	AssertEq(nil, t.err)
	ExpectNe(nil, t.registry)
}
<|endoftext|>