<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package kepctl

import (
	"fmt"
	"regexp"

	"github.com/pkg/errors"

	"k8s.io/enhancements/pkg/kepval/keps"
	"k8s.io/enhancements/pkg/kepval/keps/validations"
)

var (
	// SupportedOutputOpts stores all allowed query output formats
	SupportedOutputOpts = []string{
		"table",
		"json",
		"yaml",
	}

	// DefaultOutputOpt is the default output format for kepctl query
	DefaultOutputOpt = "table"
)

type QueryOpts struct {
	CommonArgs
	SIG         []string
	Status      []string
	Stage       []string
	PRRApprover []string
	IncludePRs  bool
	Output      string
}

// Validate checks the args and cleans them up if needed
func (c *QueryOpts) Validate(args []string) error {
	if len(c.SIG) > 0 {
		sigs, err := selectByRegexp(validations.Sigs(), c.SIG)
		if err != nil {
			return err
		}
		if len(sigs) == 0 {
			return fmt.Errorf("No SIG matches any of the passed regular expressions")
		}
		c.SIG = sigs
	}

	// check if the Output specified is one of "", "json" or "yaml"
	if !sliceContains(SupportedOutputOpts, c.Output) {
		return fmt.Errorf("unsupported output format: %s. Valid values: %v", c.Output, SupportedOutputOpts)
	}

	//TODO: check the valid values of stage, status, etc.
	return nil
}

// Query searches the local repo and possibly GitHub for KEPs
// that match the search criteria.
func (c *Client) Query(opts QueryOpts) error {
	fmt.Fprintf(c.Out, "Searching for KEPs...\n")
	repoPath, err := c.findEnhancementsRepo(opts.CommonArgs)
	if err != nil {
		return errors.Wrap(err, "unable to search KEPs")
	}

	c.SetGitHubToken(opts.CommonArgs)

	var allKEPs []*keps.Proposal
	// load the KEPs for each listed SIG
	for _, sig := range opts.SIG {
		// KEPs in the local filesystem
		allKEPs = append(allKEPs, c.loadLocalKEPs(repoPath, sig)...)

		// Open PRs; existing KEPs with open PRs will be shown twice
		if opts.IncludePRs {
			prKeps, err := c.loadKEPPullRequests(sig)
			if err != nil {
				fmt.Fprintf(c.Err, "error searching for KEP PRs from %s: %s\n", sig, err)
			}
			if prKeps != nil {
				allKEPs = append(allKEPs, prKeps...)
			}
		}
	}

	// filter the KEPs by criteria
	allowedStatus := sliceToMap(opts.Status)
	allowedStage := sliceToMap(opts.Stage)
	allowedPRR := sliceToMap(opts.PRRApprover)

	var keep []*keps.Proposal
	for _, k := range allKEPs {
		if len(opts.Status) > 0 && !allowedStatus[k.Status] {
			continue
		}
		if len(opts.Stage) > 0 && !allowedStage[k.Stage] {
			continue
		}
		if len(opts.PRRApprover) > 0 && !atLeastOne(k.PRRApprovers, allowedPRR) {
			continue
		}
		keep = append(keep, k)
	}

	switch opts.Output {
	case "table":
		c.PrintTable(DefaultPrintConfigs("LastUpdated", "Stage", "Status", "SIG", "Authors", "Title", "Link"), keep)
	case "yaml":
		c.PrintYAML(keep)
	case "json":
		c.PrintJSON(keep)
	default:
		// this check happens as a validation step in cobra as well
		// added it for additional verbosity
		return fmt.Errorf("unsupported output format: %s. Valid values: %s", opts.Output, SupportedOutputOpts)
	}
	return nil
}

func sliceToMap(s []string) map[string]bool {
	m := make(map[string]bool, len(s))
	for _, v := range s {
		m[v] = true
	}
	return m
}

func sliceContains(s []string, e string) bool {
	for _, k := range s {
		if k == e {
			return true
		}
	}

	return false
}

// returns all strings in vals that match at least one
// regexp in regexps
func selectByRegexp(vals []string, regexps []string) ([]string, error) {
	var matches []string
	for _, s := range vals {
		for _, r := range regexps {
			found, err := regexp.MatchString(r, s)
			if err != nil {
				return matches, err
			}
			if found {
				matches = append(matches, s)
				break
			}
		}
	}
	return matches, nil
}

// returns true if at least one of vals is in the allowed map
func atLeastOne(vals []string, allowed map[string]bool) bool {
	for _, v := range vals {
		if allowed[v] {
			return true
		}
	}

	return false
}
<commit_msg>Add functionality to suppress log like when using structured outputs<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package kepctl

import (
	"fmt"
	"regexp"

	"github.com/pkg/errors"

	"k8s.io/enhancements/pkg/kepval/keps"
	"k8s.io/enhancements/pkg/kepval/keps/validations"
)

var (
	// SupportedOutputOpts stores all allowed query output formats
	SupportedOutputOpts = []string{
		"table",
		"json",
		"yaml",
	}

	// DefaultOutputOpt is the default output format for kepctl query
	DefaultOutputOpt = "table"

	StructuredOutputFormats = []string{
		"json",
		"yaml",
	}
)

type QueryOpts struct {
	CommonArgs
	SIG         []string
	Status      []string
	Stage       []string
	PRRApprover []string
	IncludePRs  bool
	Output      string
}

// Validate checks the args and cleans them up if needed
func (c *QueryOpts) Validate(args []string) error {
	if len(c.SIG) > 0 {
		sigs, err := selectByRegexp(validations.Sigs(), c.SIG)
		if err != nil {
			return err
		}
		if len(sigs) == 0 {
			return fmt.Errorf("No SIG matches any of the passed regular expressions")
		}
		c.SIG = sigs
	}

	// check if the Output specified is one of "", "json" or "yaml"
	if !sliceContains(SupportedOutputOpts, c.Output) {
		return fmt.Errorf("unsupported output format: %s. Valid values: %v", c.Output, SupportedOutputOpts)
	}

	//TODO: check the valid values of stage, status, etc.
	return nil
}

// Query searches the local repo and possibly GitHub for KEPs
// that match the search criteria.
func (c *Client) Query(opts QueryOpts) error {
	// if output format is json/yaml, suppress other outputs
	// json/yaml are structured formats, logging events which
	// do not conform to the spec will create formatting issues
	var suppressOutputs bool
	if sliceContains(StructuredOutputFormats, opts.Output) {
		suppressOutputs = true
	} else {
		suppressOutputs = false
	}

	if !suppressOutputs {
		fmt.Fprintf(c.Out, "Searching for KEPs...\n")
	}

	repoPath, err := c.findEnhancementsRepo(opts.CommonArgs)
	if err != nil {
		return errors.Wrap(err, "unable to search KEPs")
	}

	c.SetGitHubToken(opts.CommonArgs)

	var allKEPs []*keps.Proposal
	// load the KEPs for each listed SIG
	for _, sig := range opts.SIG {
		// KEPs in the local filesystem
		allKEPs = append(allKEPs, c.loadLocalKEPs(repoPath, sig)...)

		// Open PRs; existing KEPs with open PRs will be shown twice
		if opts.IncludePRs {
			prKeps, err := c.loadKEPPullRequests(sig)
			if err != nil {
				fmt.Fprintf(c.Err, "error searching for KEP PRs from %s: %s\n", sig, err)
			}
			if prKeps != nil {
				allKEPs = append(allKEPs, prKeps...)
			}
		}
	}

	// filter the KEPs by criteria
	allowedStatus := sliceToMap(opts.Status)
	allowedStage := sliceToMap(opts.Stage)
	allowedPRR := sliceToMap(opts.PRRApprover)

	var keep []*keps.Proposal
	for _, k := range allKEPs {
		if len(opts.Status) > 0 && !allowedStatus[k.Status] {
			continue
		}
		if len(opts.Stage) > 0 && !allowedStage[k.Stage] {
			continue
		}
		if len(opts.PRRApprover) > 0 && !atLeastOne(k.PRRApprovers, allowedPRR) {
			continue
		}
		keep = append(keep, k)
	}

	switch opts.Output {
	case "table":
		c.PrintTable(DefaultPrintConfigs("LastUpdated", "Stage", "Status", "SIG", "Authors", "Title", "Link"), keep)
	case "yaml":
		c.PrintYAML(keep)
	case "json":
		c.PrintJSON(keep)
	default:
		// this check happens as a validation step in cobra as well
		// added it for additional verbosity
		return fmt.Errorf("unsupported output format: %s. Valid values: %s", opts.Output, SupportedOutputOpts)
	}
	return nil
}

func sliceToMap(s []string) map[string]bool {
	m := make(map[string]bool, len(s))
	for _, v := range s {
		m[v] = true
	}
	return m
}

func sliceContains(s []string, e string) bool {
	for _, k := range s {
		if k == e {
			return true
		}
	}

	return false
}

// returns all strings in vals that match at least one
// regexp in regexps
func selectByRegexp(vals []string, regexps []string) ([]string, error) {
	var matches []string
	for _, s := range vals {
		for _, r := range regexps {
			found, err := regexp.MatchString(r, s)
			if err != nil {
				return matches, err
			}
			if found {
				matches = append(matches, s)
				break
			}
		}
	}
	return matches, nil
}

// returns true if at least one of vals is in the allowed map
func atLeastOne(vals []string, allowed map[string]bool) bool {
	for _, v := range vals {
		if allowed[v] {
			return true
		}
	}

	return false
}
<|endoftext|>


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

// This test is flaky AF under the race detector.

//go:build !race
// +build !race

package pty

import (
	"encoding/json"
	"errors"
	"os"
	"reflect"
	"syscall"
	"testing"
)

func TestNew(t *testing.T) {
	if _, err := New(); os.IsNotExist(err) {
		t.Skipf("Failed allocate /dev/pts device")
	} else if err != nil {
		t.Errorf("New pty: want nil, got %v", err)
	}
}

func TestRunRestoreTTYMode(t *testing.T) {
	p, err := New()
	if os.IsNotExist(err) || errors.Is(err, syscall.ENXIO) {
		t.Skipf("Failed to allocate /dev/pts device")
	} else if err != nil {
		t.Fatalf("TestStart New pty: want nil, got %v", err)
	}

	p.Command("echo", "hi")
	if err := p.Start(); err != nil {
		t.Fatalf("TestStart Start: want nil, got %v", err)
	}
	if err := p.Wait(); err != nil {
		t.Error(err)
	}
	ti, err := p.TTY.Get()
	if err != nil {
		t.Fatalf("TestStart Get: want nil, got %v", err)
	}
	if !reflect.DeepEqual(ti, p.Restorer) {
		tt, err := json.Marshal(ti)
		if err != nil {
			t.Fatalf("Can't marshall %v: %v", ti, err)
		}
		r, err := json.Marshal(p.Restorer)
		if err != nil {
			t.Fatalf("Can't marshall %v: %v", p.Restorer, err)
		}
		t.Errorf("TestStart: want termios from Get %s to be the same as termios from Start (%s) to be the same, they differ", tt, r)
	}
	b := make([]byte, 1024)
	n, err := p.Ptm.Read(b)
	t.Logf("ptm read is %d bytes, b is %q", n, b[:n])
	if err != nil {
		t.Fatalf("Error reading from process: %v", err)
	}
	if n != 4 {
		t.Errorf("Bogus returned amount: got %d, want 4", n)
	}
	if string(b[:n]) != "hi\r\n" {
		t.Errorf("bogus returned data: got %q, want %q", string(b[:n]), "hi\r\n")
	}
}
<commit_msg>pkg/pty/pty_test:Skip test if /dev/tty does not exist<commit_after>// Copyright 2015-2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This test is flaky AF under the race detector.

//go:build !race
// +build !race

package pty

import (
	"encoding/json"
	"errors"
	"os"
	"reflect"
	"syscall"
	"testing"
)

func TestNew(t *testing.T) {
	if _, err := New(); os.IsNotExist(err) || errors.Is(err, syscall.ENXIO) {
		t.Skipf("Failed allocate /dev/pts device")
	} else if err != nil {
		t.Errorf("New pty: want nil, got %v", err)
	}
}

func TestRunRestoreTTYMode(t *testing.T) {
	p, err := New()
	if os.IsNotExist(err) || errors.Is(err, syscall.ENXIO) {
		t.Skipf("Failed to allocate /dev/pts device")
	} else if err != nil {
		t.Fatalf("TestStart New pty: want nil, got %v", err)
	}

	p.Command("echo", "hi")
	if err := p.Start(); err != nil {
		t.Fatalf("TestStart Start: want nil, got %v", err)
	}
	if err := p.Wait(); err != nil {
		t.Error(err)
	}
	ti, err := p.TTY.Get()
	if err != nil {
		t.Fatalf("TestStart Get: want nil, got %v", err)
	}
	if !reflect.DeepEqual(ti, p.Restorer) {
		tt, err := json.Marshal(ti)
		if err != nil {
			t.Fatalf("Can't marshall %v: %v", ti, err)
		}
		r, err := json.Marshal(p.Restorer)
		if err != nil {
			t.Fatalf("Can't marshall %v: %v", p.Restorer, err)
		}
		t.Errorf("TestStart: want termios from Get %s to be the same as termios from Start (%s) to be the same, they differ", tt, r)
	}
	b := make([]byte, 1024)
	n, err := p.Ptm.Read(b)
	t.Logf("ptm read is %d bytes, b is %q", n, b[:n])
	if err != nil {
		t.Fatalf("Error reading from process: %v", err)
	}
	if n != 4 {
		t.Errorf("Bogus returned amount: got %d, want 4", n)
	}
	if string(b[:n]) != "hi\r\n" {
		t.Errorf("bogus returned data: got %q, want %q", string(b[:n]), "hi\r\n")
	}
}
<|endoftext|>


<commit_before>package r53

//-----------------------------------------------------------------------------
// Package factored import statement:
//-----------------------------------------------------------------------------

import (

	// Stdlib:
	"os"
	"os/exec"
	"strings"
	"time"

	// AWS SDK:
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"

	// Community:
	log "github.com/Sirupsen/logrus"
)

//-----------------------------------------------------------------------------
// Typedefs:
//-----------------------------------------------------------------------------

type zoneData struct {
	route53.HostedZone
	route53.ResourceRecordSet
}

// Data struct for Route53.
type Data struct {
	r53     *route53.Route53
	command string
	APIKey  string
	Zone    zoneData
	Records []string
	Zones   []string
}

//-----------------------------------------------------------------------------
// func: AddRecords
//-----------------------------------------------------------------------------

// AddRecords adds one or more records to a Route 53 zone.
func (d *Data) AddRecords() {

	// Set the current command:
	d.command = "record:add"

	// Create the service handler:
	d.r53 = route53.New(session.Must(session.NewSession()))

	// Get the zone data:
	zone := normalizeZoneName(*d.Zone.HostedZone.Name)
	*d.Zone.HostedZone.Name = zone
	if _, err := d.getZone(zone); err != nil {
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Fatal(err)
	}

	// Return if zone is missing:
	if d.Zone.Id == nil || *d.Zone.Id == "" {
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Fatal("Ops! This zone does not exist")
	}

	// For each requested record:
	for _, record := range d.Records {
		if err := d.addRecord(record); err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": record}).
				Fatal(err)
		}
	}
}

//-----------------------------------------------------------------------------
// func: AddZones
//-----------------------------------------------------------------------------

// AddZones adds one or more zones to Route 53.
func (d *Data) AddZones() {

	// Set the current command:
	d.command = "zone:add"

	// Create the service handler:
	d.r53 = route53.New(session.Must(session.NewSession()))

	// For each requested zone:
	for _, zone := range d.Zones {

		// Normalize the zone name:
		zone = normalizeZoneName(zone)
		d.Zone.HostedZone.Name = &zone

		// Add the child zone:
		if err := d.addZone(); err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
				Fatal(err)
		}

		// Get the parent zone:
		pZone, err := d.getParentZone()
		if err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
				Fatal(err)
		}

		// If any:
		if pZone != "" {
			if err := d.delegateZone(pZone); err != nil {
				log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
					Fatal(err)
			}
		}

		// Clean zone data:
		d.Zone.Id = nil
	}
}

//-----------------------------------------------------------------------------
// func: DelZones
//-----------------------------------------------------------------------------

// DelZones deletes one or more zones from Route 53.
func (d *Data) DelZones() {

	// Set the current command:
	d.command = "zone:del"

	// Create the service handler:
	d.r53 = route53.New(session.Must(session.NewSession()))

	// For each requested zone:
	for _, zone := range d.Zones {

		// Normalize the zone name:
		zone = normalizeZoneName(zone)
		d.Zone.HostedZone.Name = &zone

		// Delete the child zone:
		if err := d.delZone(); err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": d.Zone}).
				Fatal(err)
		}
	}
}

//-----------------------------------------------------------------------------
// func: addRecord
//-----------------------------------------------------------------------------

func (d *Data) addRecord(record string) error {

	// Split into data:type:name
	s := strings.Split(record, ":")
	resourceName := s[0]
	resourceType := s[1]
	resourceData := s[2]

	// Resource records (innermost matryoshka):
	resourceRecords := []*route53.ResourceRecord{}
	for _, resource := range strings.Split(resourceData, ",") {
		resourceRecords = append(resourceRecords, &route53.ResourceRecord{
			Value: aws.String(resource),
		})
	}

	// Changes (middle matryoshka):
	zone := *d.Zone.HostedZone.Name
	changes := []*route53.Change{{
		Action: aws.String("UPSERT"),
		ResourceRecordSet: &route53.ResourceRecordSet{
			Name:            aws.String(resourceName + "." + zone),
			Type:            aws.String(resourceType),
			TTL:             aws.Int64(300),
			ResourceRecords: resourceRecords,
		},
	}}

	// Forge the change request (outermost matryoshka):
	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: aws.String(*d.Zone.Id),
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	// Send the change request:
	if _, err := d.r53.ChangeResourceRecordSets(params); err != nil {
		return err
	}

	// Log record creation:
	log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": resourceName}).
		Info("DNS record created/updated")

	return nil
}

//-----------------------------------------------------------------------------
// func: addZone
//-----------------------------------------------------------------------------

func (d *Data) addZone() error {

	// Get the zone data:
	zone := *d.Zone.HostedZone.Name
	if _, err := d.getZone(zone); err != nil {
		return err
	}

	// If the zone is missing:
	if d.Zone.Id == nil || *d.Zone.Id == "" {

		// Forge the new zone request:
		params := &route53.CreateHostedZoneInput{
			CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)),
			Name:            aws.String(zone),
		}

		// Send the new zone request:
		if _, err := d.r53.CreateHostedZone(params); err != nil {
			return err
		}

		// Get the zone data:
		if _, err := d.getZone(zone); err != nil {
			return err
		}

		// Log the new zone creation:
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Info("New DNS zone created")

	} else {

		// Log zone already exists:
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Info("DNS zone already exists")
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: delZone
//-----------------------------------------------------------------------------

func (d *Data) delZone() error {

	// Get the zone data:
	zone := *d.Zone.HostedZone.Name
	if _, err := d.getZone(zone); err != nil {
		return err
	}

	// If the zone exists:
	if d.Zone.Id != nil && *d.Zone.Id != "" {

		// Forge the delete zone request:
		params := &route53.DeleteHostedZoneInput{
			Id: aws.String(*d.Zone.Id),
		}

		// Send the delete zone request:
		if _, err := d.r53.DeleteHostedZone(params); err != nil {
			return err
		}

		// Log zone deletion:
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Info("DNS zone deleted")

		return nil
	}

	// Log zone already gone:
	log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
		Info("Ops! this zone does not exist")

	return nil
}

//-----------------------------------------------------------------------------
// func: getParentZone
//-----------------------------------------------------------------------------

func (d *Data) getParentZone() (string, error) {

	var err error
	var id, parent string

	// Split the given zone:
	zone := *d.Zone.HostedZone.Name
	split := strings.Split(zone, ".")

	// Iterate over parent zones:
	for i := len(split) - 1; i > 1; i-- {

		// Get the parent zone ID (if any):
		parent = strings.Join(split[len(split)-i:], ".")
		id, err = d.getZone(parent)
		if err != nil {
			return "", err
		}

		// Break if found:
		if id != "" {
			break
		}
	}

	// Not found:
	if id == "" {
		return "", nil
	}

	return parent, nil
}

//-----------------------------------------------------------------------------
// func: getZone
//-----------------------------------------------------------------------------

func (d *Data) getZone(zone string) (string, error) {

	// Forge the zone list request:
	pZone := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(zone),
		MaxItems: aws.String("1"),
	}

	// Send the zone list request:
	rZone, err := d.r53.ListHostedZonesByName(pZone)
	if err != nil {
		return "", err
	}

	// Zone does not exist:
	if len(rZone.HostedZones) < 1 || *rZone.HostedZones[0].Name != zone {
		return "", nil
	}

	// If adding a zone:
	if zone == *d.Zone.HostedZone.Name {

		// Forge the NS record list request:
		pRsrc := &route53.ListResourceRecordSetsInput{
			HostedZoneId:    aws.String(*rZone.HostedZones[0].Id),
			MaxItems:        aws.String("1"),
			StartRecordName: aws.String(zone),
			StartRecordType: aws.String("NS"),
		}

		// Send the NS record list request:
		rRsrc, err := d.r53.ListResourceRecordSets(pRsrc)
		if err != nil {
			return "", err
		}

		// Save the data:
		d.Zone.HostedZone = *rZone.HostedZones[0]
		d.Zone.ResourceRecordSet = *rRsrc.ResourceRecordSets[0]
	}

	return *rZone.HostedZones[0].Id, nil
}

//-----------------------------------------------------------------------------
// func: delegateZone
//-----------------------------------------------------------------------------

func (d *Data) delegateZone(pZone string) error {

	// Extract name servers:
	var ns []string
	for _, record := range d.Zone.ResourceRecords {
		ns = append(ns, *record.Value)
	}

	// Forge the 'record add' command:
	zone := strings.Replace(*d.Zone.HostedZone.Name, "."+pZone, "", 1)
	cmd := exec.Command("katoctl", "r53", "record", "add",
		"--zone", pZone, zone+":NS:"+strings.Join(ns, ","))

	// Execute the 'record add' command:
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: normalizeZoneName
//-----------------------------------------------------------------------------

func normalizeZoneName(zone string) string {
	if zone[len(zone)-1] != []byte(".")[0] {
		zone = zone + "."
	}
	return zone
}
<commit_msg>Be more verbose<commit_after>package r53

//-----------------------------------------------------------------------------
// Package factored import statement:
//-----------------------------------------------------------------------------

import (

	// Stdlib:
	"os"
	"os/exec"
	"strings"
	"time"

	// AWS SDK:
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/route53"

	// Community:
	log "github.com/Sirupsen/logrus"
)

//-----------------------------------------------------------------------------
// Typedefs:
//-----------------------------------------------------------------------------

type zoneData struct {
	route53.HostedZone
	route53.ResourceRecordSet
}

// Data struct for Route53.
type Data struct {
	r53     *route53.Route53
	command string
	APIKey  string
	Zone    zoneData
	Records []string
	Zones   []string
}

//-----------------------------------------------------------------------------
// func: AddRecords
//-----------------------------------------------------------------------------

// AddRecords adds one or more records to a Route 53 zone.
func (d *Data) AddRecords() {

	// Set the current command:
	d.command = "record:add"

	// Create the service handler:
	d.r53 = route53.New(session.Must(session.NewSession()))

	// Get the zone data:
	zone := normalizeZoneName(*d.Zone.HostedZone.Name)
	*d.Zone.HostedZone.Name = zone
	if _, err := d.getZone(zone); err != nil {
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Fatal(err)
	}

	// Return if zone is missing:
	if d.Zone.Id == nil || *d.Zone.Id == "" {
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Fatal("Ops! This zone does not exist")
	}

	// For each requested record:
	for _, record := range d.Records {
		if err := d.addRecord(record); err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": record}).
				Fatal(err)
		}
	}
}

//-----------------------------------------------------------------------------
// func: AddZones
//-----------------------------------------------------------------------------

// AddZones adds one or more zones to Route 53.
func (d *Data) AddZones() {

	// Set the current command:
	d.command = "zone:add"

	// Create the service handler:
	d.r53 = route53.New(session.Must(session.NewSession()))

	// For each requested zone:
	for _, zone := range d.Zones {

		// Normalize the zone name:
		zone = normalizeZoneName(zone)
		d.Zone.HostedZone.Name = &zone

		// Add the child zone:
		if err := d.addZone(); err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
				Fatal(err)
		}

		// Get the parent zone:
		pZone, err := d.getParentZone()
		if err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
				Fatal(err)
		}

		// If any:
		if pZone != "" {
			if err := d.delegateZone(pZone); err != nil {
				log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
					Fatal(err)
			}
		}

		// Clean zone data:
		d.Zone.Id = nil
	}
}

//-----------------------------------------------------------------------------
// func: DelZones
//-----------------------------------------------------------------------------

// DelZones deletes one or more zones from Route 53.
func (d *Data) DelZones() {

	// Set the current command:
	d.command = "zone:del"

	// Create the service handler:
	d.r53 = route53.New(session.Must(session.NewSession()))

	// For each requested zone:
	for _, zone := range d.Zones {

		// Normalize the zone name:
		zone = normalizeZoneName(zone)
		d.Zone.HostedZone.Name = &zone

		// Delete the child zone:
		if err := d.delZone(); err != nil {
			log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": d.Zone}).
				Fatal(err)
		}
	}
}

//-----------------------------------------------------------------------------
// func: addRecord
//-----------------------------------------------------------------------------

func (d *Data) addRecord(record string) error {

	// Split into data:type:name
	s := strings.Split(record, ":")
	resourceName := s[0]
	resourceType := s[1]
	resourceData := s[2]

	// Resource records (innermost matryoshka):
	resourceRecords := []*route53.ResourceRecord{}
	for _, resource := range strings.Split(resourceData, ",") {
		resourceRecords = append(resourceRecords, &route53.ResourceRecord{
			Value: aws.String(resource),
		})
	}

	// Changes (middle matryoshka):
	zone := *d.Zone.HostedZone.Name
	changes := []*route53.Change{{
		Action: aws.String("UPSERT"),
		ResourceRecordSet: &route53.ResourceRecordSet{
			Name:            aws.String(resourceName + "." + zone),
			Type:            aws.String(resourceType),
			TTL:             aws.Int64(300),
			ResourceRecords: resourceRecords,
		},
	}}

	// Forge the change request (outermost matryoshka):
	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: aws.String(*d.Zone.Id),
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	// Send the change request:
	if _, err := d.r53.ChangeResourceRecordSets(params); err != nil {
		return err
	}

	// Log record creation:
	log.WithFields(log.Fields{"cmd": "r53:" + d.command,
		"id": resourceName + "." + zone}).Info("DNS record created/updated")

	return nil
}

//-----------------------------------------------------------------------------
// func: addZone
//-----------------------------------------------------------------------------

func (d *Data) addZone() error {

	// Get the zone data:
	zone := *d.Zone.HostedZone.Name
	if _, err := d.getZone(zone); err != nil {
		return err
	}

	// If the zone is missing:
	if d.Zone.Id == nil || *d.Zone.Id == "" {

		// Forge the new zone request:
		params := &route53.CreateHostedZoneInput{
			CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)),
			Name:            aws.String(zone),
		}

		// Send the new zone request:
		if _, err := d.r53.CreateHostedZone(params); err != nil {
			return err
		}

		// Get the zone data:
		if _, err := d.getZone(zone); err != nil {
			return err
		}

		// Log the new zone creation:
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Info("New DNS zone created")

	} else {

		// Log zone already exists:
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Info("DNS zone already exists")
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: delZone
//-----------------------------------------------------------------------------

func (d *Data) delZone() error {

	// Get the zone data:
	zone := *d.Zone.HostedZone.Name
	if _, err := d.getZone(zone); err != nil {
		return err
	}

	// If the zone exists:
	if d.Zone.Id != nil && *d.Zone.Id != "" {

		// Forge the delete zone request:
		params := &route53.DeleteHostedZoneInput{
			Id: aws.String(*d.Zone.Id),
		}

		// Send the delete zone request:
		if _, err := d.r53.DeleteHostedZone(params); err != nil {
			return err
		}

		// Log zone deletion:
		log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
			Info("DNS zone deleted")

		return nil
	}

	// Log zone already gone:
	log.WithFields(log.Fields{"cmd": "r53:" + d.command, "id": zone}).
		Info("Ops! this zone does not exist")

	return nil
}

//-----------------------------------------------------------------------------
// func: getParentZone
//-----------------------------------------------------------------------------

func (d *Data) getParentZone() (string, error) {

	var err error
	var id, parent string

	// Split the given zone:
	zone := *d.Zone.HostedZone.Name
	split := strings.Split(zone, ".")

	// Iterate over parent zones:
	for i := len(split) - 1; i > 1; i-- {

		// Get the parent zone ID (if any):
		parent = strings.Join(split[len(split)-i:], ".")
		id, err = d.getZone(parent)
		if err != nil {
			return "", err
		}

		// Break if found:
		if id != "" {
			break
		}
	}

	// Not found:
	if id == "" {
		return "", nil
	}

	return parent, nil
}

//-----------------------------------------------------------------------------
// func: getZone
//-----------------------------------------------------------------------------

func (d *Data) getZone(zone string) (string, error) {

	// Forge the zone list request:
	pZone := &route53.ListHostedZonesByNameInput{
		DNSName:  aws.String(zone),
		MaxItems: aws.String("1"),
	}

	// Send the zone list request:
	rZone, err := d.r53.ListHostedZonesByName(pZone)
	if err != nil {
		return "", err
	}

	// Zone does not exist:
	if len(rZone.HostedZones) < 1 || *rZone.HostedZones[0].Name != zone {
		return "", nil
	}

	// If adding a zone:
	if zone == *d.Zone.HostedZone.Name {

		// Forge the NS record list request:
		pRsrc := &route53.ListResourceRecordSetsInput{
			HostedZoneId:    aws.String(*rZone.HostedZones[0].Id),
			MaxItems:        aws.String("1"),
			StartRecordName: aws.String(zone),
			StartRecordType: aws.String("NS"),
		}

		// Send the NS record list request:
		rRsrc, err := d.r53.ListResourceRecordSets(pRsrc)
		if err != nil {
			return "", err
		}

		// Save the data:
		d.Zone.HostedZone = *rZone.HostedZones[0]
		d.Zone.ResourceRecordSet = *rRsrc.ResourceRecordSets[0]
	}

	return *rZone.HostedZones[0].Id, nil
}

//-----------------------------------------------------------------------------
// func: delegateZone
//-----------------------------------------------------------------------------

func (d *Data) delegateZone(pZone string) error {

	// Extract name servers:
	var ns []string
	for _, record := range d.Zone.ResourceRecords {
		ns = append(ns, *record.Value)
	}

	// Forge the 'record add' command:
	zone := strings.Replace(*d.Zone.HostedZone.Name, "."+pZone, "", 1)
	cmd := exec.Command("katoctl", "r53", "record", "add",
		"--zone", pZone, zone+":NS:"+strings.Join(ns, ","))

	// Execute the 'record add' command:
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}

//-----------------------------------------------------------------------------
// func: normalizeZoneName
//-----------------------------------------------------------------------------

func normalizeZoneName(zone string) string {
	if zone[len(zone)-1] != []byte(".")[0] {
		zone = zone + "."
	}
	return zone
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"io"
	"text/tabwriter"

	"github.com/fatih/color"
)

const (
	noType          = ""
	okType          = "[OK]"
	errType         = "[ERROR]"
	skippedType     = "[SKIPPED]"
	warnType        = "[WARNING]"
	unreachableType = "[UNREACHABLE]"
	errIgnoredType  = "[ERROR IGNORED]"
)

var Green = color.New(color.FgGreen)
var Red = color.New(color.FgRed)
var Orange = color.New(color.FgRed, color.FgYellow)
var Blue = color.New(color.FgCyan)

// PrettyPrintOk [OK](Green) with formatted string
func PrettyPrintOk(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, okType, a...)
}

// PrettyPrintErr [ERROR](Red) with formatted string
func PrettyPrintErr(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, errType, a...)
}

// PrettyPrint no type will be displayed, used for just single line printing
func PrettyPrint(out io.Writer, msg string, a ...interface{}) {
	print(out, msg+"\t", noType, a...)
}

// PrettyPrintWarn [WARNING](Orange) with formatted string
func PrettyPrintWarn(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, warnType, a...)
}

// PrettyPrintErrorIgnored [ERROR IGNORED](Red) with formatted string
func PrettyPrintErrorIgnored(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, errIgnoredType, a...)
}

// PrettyPrintUnreachable [UNREACHABLE](Red) with formatted string
func PrettyPrintUnreachable(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, unreachableType, a...)
}

// PrettyPrintSkipped [SKIPPED](blue) with formatted string
func PrettyPrintSkipped(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, skippedType, a...)
}

// PrintOk print whole message in green(Red) format
func PrintOk(out io.Writer) {
	PrintColor(out, Green, okType)
}

// PrintOkln print whole message in green(Red) format
func PrintOkln(out io.Writer) {
	PrintColor(out, Green, okType+"\n")
}

// PrintError print whole message in error(Red) format
func PrintError(out io.Writer) {
	PrintColor(out, Red, errType)
}

// PrintWarn print whole message in warn(Orange) format
func PrintWarn(out io.Writer) {
	PrintColor(out, Orange, warnType)
}

// PrintSkipped print whole message in green(Red) format
func PrintSkipped(out io.Writer) {
	PrintColor(out, Blue, skippedType)
}

// PrintHeader will print header with predifined width
func PrintHeader(out io.Writer, msg string) {
	w := tabwriter.NewWriter(out, 84, 0, 0, '=', 0)
	fmt.Fprintln(w, "")
	format := msg + "\t\n"
	fmt.Fprintf(w, format)
	w.Flush()
}

// PrintColor prints text in color
func PrintColor(out io.Writer, clr *color.Color, msg string, a ...interface{}) {
	// Remove any newline, results in only one \n
	line := fmt.Sprintf("%s", clr.SprintfFunc()(msg, a...))
	fmt.Fprint(out, line)
}

func print(out io.Writer, msg, status string, a ...interface{}) {
	w := tabwriter.NewWriter(out, 80, 0, 0, ' ', 0)
	// print message
	format := msg + "\t"
	fmt.Fprintf(w, format, a...)

	// print status
	if status != noType {
		// get correct color
		var clr *color.Color
		switch status {
		case okType:
			clr = Green
		case errType, unreachableType:
			clr = Red
		case warnType, ErrIgnoredType:
			clr = Orange
		case skippedType:
			clr = Blue
		}

		sformat := "%s\n"
		fmt.Fprintf(w, sformat, clr.SprintFunc()(status))

	}
	w.Flush()
}
<commit_msg>No Ticket: Fix previous commit<commit_after>package util

import (
	"fmt"
	"io"
	"text/tabwriter"

	"github.com/fatih/color"
)

const (
	noType          = ""
	okType          = "[OK]"
	errType         = "[ERROR]"
	skippedType     = "[SKIPPED]"
	warnType        = "[WARNING]"
	unreachableType = "[UNREACHABLE]"
	errIgnoredType  = "[ERROR IGNORED]"
)

var Green = color.New(color.FgGreen)
var Red = color.New(color.FgRed)
var Orange = color.New(color.FgRed, color.FgYellow)
var Blue = color.New(color.FgCyan)

// PrettyPrintOk [OK](Green) with formatted string
func PrettyPrintOk(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, okType, a...)
}

// PrettyPrintErr [ERROR](Red) with formatted string
func PrettyPrintErr(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, errType, a...)
}

// PrettyPrint no type will be displayed, used for just single line printing
func PrettyPrint(out io.Writer, msg string, a ...interface{}) {
	print(out, msg+"\t", noType, a...)
}

// PrettyPrintWarn [WARNING](Orange) with formatted string
func PrettyPrintWarn(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, warnType, a...)
}

// PrettyPrintErrorIgnored [ERROR IGNORED](Red) with formatted string
func PrettyPrintErrorIgnored(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, errIgnoredType, a...)
}

// PrettyPrintUnreachable [UNREACHABLE](Red) with formatted string
func PrettyPrintUnreachable(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, unreachableType, a...)
}

// PrettyPrintSkipped [SKIPPED](blue) with formatted string
func PrettyPrintSkipped(out io.Writer, msg string, a ...interface{}) {
	print(out, msg, skippedType, a...)
}

// PrintOk print whole message in green(Red) format
func PrintOk(out io.Writer) {
	PrintColor(out, Green, okType)
}

// PrintOkln print whole message in green(Red) format
func PrintOkln(out io.Writer) {
	PrintColor(out, Green, okType+"\n")
}

// PrintError print whole message in error(Red) format
func PrintError(out io.Writer) {
	PrintColor(out, Red, errType)
}

// PrintWarn print whole message in warn(Orange) format
func PrintWarn(out io.Writer) {
	PrintColor(out, Orange, warnType)
}

// PrintSkipped print whole message in green(Red) format
func PrintSkipped(out io.Writer) {
	PrintColor(out, Blue, skippedType)
}

// PrintHeader will print header with predifined width
func PrintHeader(out io.Writer, msg string) {
	w := tabwriter.NewWriter(out, 84, 0, 0, '=', 0)
	fmt.Fprintln(w, "")
	format := msg + "\t\n"
	fmt.Fprintf(w, format)
	w.Flush()
}

// PrintColor prints text in color
func PrintColor(out io.Writer, clr *color.Color, msg string, a ...interface{}) {
	// Remove any newline, results in only one \n
	line := fmt.Sprintf("%s", clr.SprintfFunc()(msg, a...))
	fmt.Fprint(out, line)
}

func print(out io.Writer, msg, status string, a ...interface{}) {
	w := tabwriter.NewWriter(out, 80, 0, 0, ' ', 0)
	// print message
	format := msg + "\t"
	fmt.Fprintf(w, format, a...)

	// print status
	if status != noType {
		// get correct color
		var clr *color.Color
		switch status {
		case okType:
			clr = Green
		case errType, unreachableType:
			clr = Red
		case warnType, errIgnoredType:
			clr = Orange
		case skippedType:
			clr = Blue
		}

		sformat := "%s\n"
		fmt.Fprintf(w, sformat, clr.SprintFunc()(status))

	}
	w.Flush()
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Jetstack cert-manager contributors.

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 util

import "fmt"

var (
	AppGitState  = ""
	AppGitCommit = ""
	AppVersion   = "canary"
)

func version() string {
	v := AppVersion
	if AppGitCommit != "" {
		v += "-" + AppGitCommit
	}
	if AppGitState != "" {
		v += fmt.Sprintf(" (%v)", AppGitState)
	}
	return v
}
<commit_msg>Update version string<commit_after>/*
Copyright 2018 The Jetstack cert-manager contributors.

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 util

import "fmt"

// This variable block holds information used to build up the version string
var (
	AppGitState  = ""
	AppGitCommit = ""
	AppVersion   = "canary"
)

func version() string {
	v := AppVersion
	if AppVersion == "canary" && AppGitCommit != "" {
		v += "-" + AppGitCommit
	}
	if AppGitState != "" {
		v += fmt.Sprintf(" (%v)", AppGitState)
	}
	return v
}
<|endoftext|>


<commit_before>package bigflake

import (
	"errors"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/mattheath/kala/util"
)

const (
	// default number of bits to use for the worker id
	defaultWorkerIdBits uint32 = 48

	// default number of bits to use for the sequence (per ms)
	defaultSequenceBits uint32 = 16
)

var (
	ErrInvalidWorkerId  error = errors.New("Invalid worker ID - worker ID out of range")
	ErrOverflow         error = errors.New("Timestamp overflow (past end of lifespan) - unable to generate any more IDs")
	ErrSequenceOverflow error = errors.New("Sequence overflow (too many IDs generated) - unable to generate IDs for 1 millisecond")
)

// New initialises a Bigflake minter, with a default configuration
// This can be configured using Options
func New(workerId uint64) (*Bigflake, error) {
	return &Bigflake{
		workerId:     int64(workerId),
		sequenceBits: defaultSequenceBits,
		workerIdBits: defaultWorkerIdBits,
		epoch:        0, // default unix epoch
	}, nil
}

type Bigflake struct {
	sync.Mutex

	// lastTimestamp is the most recent millisecond time window encountered
	lastTimestamp int64

	// workerId - 48 bits
	workerId int64
	// sequence number - 16 bits
	// we auto-increment for same-millisecond collisions
	sequence int64

	// Options set prior to first use
	// Time bits cannot be set, and are the remainder from our bit limit
	sequenceBits uint32
	workerIdBits uint32
	epoch        int64

	// Limits based on configured options
	maxSequence          int64
	maxWorkerId          int64
	maxAdjustedTimestamp int64

	// Once we have started minting IDs the options cannot be changed
	once        sync.Once
	initialised bool
}

// Mint a new 128bit ID based on the current time, worker id and sequence
func (bf *Bigflake) Mint() (*BigflakeId, error) {
	bf.Lock()
	defer bf.Unlock()

	// Setup locks in our configured options
	bf.once.Do(bf.setup)

	// Ensure we only mint IDs if correctly configured
	if bf.workerId > bf.maxWorkerId {
		return nil, ErrInvalidWorkerId
	}

	// Get the current timestamp in ms
	// @todo generalise to allow custom epoch
	t := util.TimeToMsInt64(time.Now())

	// Update bigflake with this, which Mawill increment sequence number if needed
	err := bf.update(t)
	if err != nil {
		return nil, err
	}

	// Mint a new ID
	id := bf.mintId(bf.lastTimestamp, bf.workerId, bf.sequence, 48, 16)
	bfId := &BigflakeId{
		id: id,
	}

	return bfId, nil
}

// setup is called the first time we mint an ID and locks in our configured options
func (bf *Bigflake) setup() {

	// Set up limits based on configured options
	bf.maxWorkerId = (1 << bf.workerIdBits) - 1 // worker id mask
	bf.maxSequence = (1 << bf.sequenceBits) - 1 // sequence mask

	// maxAdjustedTimestamp which we can generate IDs until
	// eg. with the default worker and sequence bits we are limited to 41 bits of time
	// maxAdjustedTimestamp + epoch => 2199023255551, 2081-09-06 15:47:35 +0000 UTC (69 year range)
	// bf.maxAdjustedTimestamp = -1 ^ (-1 << (64 - sf.workerIdBits - sf.sequenceBits))

	// Confirm we are initialised, so new options will be ignored
	bf.initialised = true
}

// update Bigflake with a new timestamp, causing sequence numbers to increment if necessary
func (bf *Bigflake) update(t int64) error {
	if t != bf.lastTimestamp {
		// fmt.Println("Time not equal")

		switch {
		case t < bf.lastTimestamp:
			return fmt.Errorf("Time moved backwards - unable to generate IDs for %v milliseconds", bf.lastTimestamp-t)
		case t < 0:
			return fmt.Errorf("Time is currently set before our epoch - unable to generate IDs for %v milliseconds", -1*t)
			// case t > bf.maxAdjustedTimestamp:
			// 	return ErrOverflow
		}

		// Reset sequence as we're in a new ms
		bf.sequence = 0
		bf.lastTimestamp = t
	}

	// fmt.Printf("Bigflake: %#v\n\n", bf)

	// Increment sequence for this ms
	bf.sequence = bf.sequence + 1
	if bf.sequence > bf.maxSequence {
		return ErrSequenceOverflow
	}

	return nil
}

// mintId mints new 128bit IDs from the timestamp, worker ID and sequence
func (bf *Bigflake) mintId(timestamp, workerid, sequence int64,
	workerIdBits, sequenceIdBits uint32) *big.Int {

	// Time is the most significant bits
	// Shift by the number of worker and sequence bits
	id := big.NewInt(timestamp)
	id = id.Lsh(id, uint(workerIdBits+sequenceIdBits))

	// Shift the worker ID by the number of sequence bits
	bigW := big.NewInt(workerid)
	bigW = bigW.Lsh(bigW, uint(sequenceIdBits))

	// Sequence doesn't need shifting
	bigS := big.NewInt(sequence)

	// Combine components with bitwise OR
	id = id.Or(id, bigW)
	id = id.Or(id, bigS)

	return id
}
<commit_msg>Exported MintId<commit_after>package bigflake

import (
	"errors"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/mattheath/kala/util"
)

const (
	// default number of bits to use for the worker id
	defaultWorkerIdBits uint32 = 48

	// default number of bits to use for the sequence (per ms)
	defaultSequenceBits uint32 = 16
)

var (
	ErrInvalidWorkerId  error = errors.New("Invalid worker ID - worker ID out of range")
	ErrOverflow         error = errors.New("Timestamp overflow (past end of lifespan) - unable to generate any more IDs")
	ErrSequenceOverflow error = errors.New("Sequence overflow (too many IDs generated) - unable to generate IDs for 1 millisecond")
)

// New initialises a Bigflake minter, with a default configuration
// This can be configured using Options
func New(workerId uint64) (*Bigflake, error) {
	return &Bigflake{
		workerId:     int64(workerId),
		sequenceBits: defaultSequenceBits,
		workerIdBits: defaultWorkerIdBits,
		epoch:        0, // default unix epoch
	}, nil
}

type Bigflake struct {
	sync.Mutex

	// lastTimestamp is the most recent millisecond time window encountered
	lastTimestamp int64

	// workerId - 48 bits
	workerId int64
	// sequence number - 16 bits
	// we auto-increment for same-millisecond collisions
	sequence int64

	// Options set prior to first use
	// Time bits cannot be set, and are the remainder from our bit limit
	sequenceBits uint32
	workerIdBits uint32
	epoch        int64

	// Limits based on configured options
	maxSequence          int64
	maxWorkerId          int64
	maxAdjustedTimestamp int64

	// Once we have started minting IDs the options cannot be changed
	once        sync.Once
	initialised bool
}

// Mint a new 128bit ID based on the current time, worker id and sequence
func (bf *Bigflake) Mint() (*BigflakeId, error) {
	bf.Lock()
	defer bf.Unlock()

	// Setup locks in our configured options
	bf.once.Do(bf.setup)

	// Ensure we only mint IDs if correctly configured
	if bf.workerId > bf.maxWorkerId {
		return nil, ErrInvalidWorkerId
	}

	// Get the current timestamp in ms
	// @todo generalise to allow custom epoch
	t := util.TimeToMsInt64(time.Now())

	// Update bigflake with this, which Mawill increment sequence number if needed
	err := bf.update(t)
	if err != nil {
		return nil, err
	}

	// Mint a new ID
	id := bf.MintId(bf.lastTimestamp, bf.workerId, bf.sequence, 48, 16)
	bfId := &BigflakeId{
		id: id,
	}

	return bfId, nil
}

// setup is called the first time we mint an ID and locks in our configured options
func (bf *Bigflake) setup() {

	// Set up limits based on configured options
	bf.maxWorkerId = (1 << bf.workerIdBits) - 1 // worker id mask
	bf.maxSequence = (1 << bf.sequenceBits) - 1 // sequence mask

	// maxAdjustedTimestamp which we can generate IDs until
	// eg. with the default worker and sequence bits we are limited to 41 bits of time
	// maxAdjustedTimestamp + epoch => 2199023255551, 2081-09-06 15:47:35 +0000 UTC (69 year range)
	// bf.maxAdjustedTimestamp = -1 ^ (-1 << (64 - sf.workerIdBits - sf.sequenceBits))

	// Confirm we are initialised, so new options will be ignored
	bf.initialised = true
}

// update Bigflake with a new timestamp, causing sequence numbers to increment if necessary
func (bf *Bigflake) update(t int64) error {
	if t != bf.lastTimestamp {
		// fmt.Println("Time not equal")

		switch {
		case t < bf.lastTimestamp:
			return fmt.Errorf("Time moved backwards - unable to generate IDs for %v milliseconds", bf.lastTimestamp-t)
		case t < 0:
			return fmt.Errorf("Time is currently set before our epoch - unable to generate IDs for %v milliseconds", -1*t)
			// case t > bf.maxAdjustedTimestamp:
			// 	return ErrOverflow
		}

		// Reset sequence as we're in a new ms
		bf.sequence = 0
		bf.lastTimestamp = t
	}

	// fmt.Printf("Bigflake: %#v\n\n", bf)

	// Increment sequence for this ms
	bf.sequence = bf.sequence + 1
	if bf.sequence > bf.maxSequence {
		return ErrSequenceOverflow
	}

	return nil
}

// MintId mints new 128bit IDs from the timestamp, worker ID and sequence,
// this should only be used directly for testing
func (bf *Bigflake) MintId(timestamp, workerid, sequence int64,
	workerIdBits, sequenceIdBits uint32) *big.Int {

	// Time is the most significant bits
	// Shift by the number of worker and sequence bits
	id := big.NewInt(timestamp)
	id = id.Lsh(id, uint(workerIdBits+sequenceIdBits))

	// Shift the worker ID by the number of sequence bits
	bigW := big.NewInt(workerid)
	bigW = bigW.Lsh(bigW, uint(sequenceIdBits))

	// Sequence doesn't need shifting
	bigS := big.NewInt(sequence)

	// Combine components with bitwise OR
	id = id.Or(id, bigW)
	id = id.Or(id, bigS)

	return id
}
<|endoftext|>


<commit_before>package discrete

import (
	"math"
	"sort"
)

// A Graph implements all methods necessary to run graph-specific algorithms on it. Strictly speaking EdgeList() and NodeList() would be sufficient, but the others (such as Successors)
// are required to make the algorithms more efficient and/or easier to write and maintain.
type Graph interface {
	Successors(node int) []int                // Gives the nodes connected by OUTBOUND edges, if the graph is an undirected graph, this set is equal to Predecessors
	IsSuccessor(node, successor int) bool     // If successor shows up in the list returned by Successors(node), then it's a successor. If node doesn't exist, this should always return false
	Predecessors(node int) []int              // Gives the nodes connected by INBOUND edges, if the graph is an undirected graph, this set is equal to Successors
	IsPredecessor(node, predecessor int) bool // If predecessor shows up in the list returned by Predecessors(node), then it's a predecessor. If node doesn't exist, this should always return false
	IsAdjacent(node, neighbor int) bool       // IsSuccessor || IsPredecessor
	NodeExists(node int) bool                 // Returns whether a node with the given ID is currently in the graph
	Degree(node int) int                      // Degree is equivalent to len(Successors(node)) + len(Predecessors(node)); this means that reflexive edges are counted twice
	EdgeList() [][2]int                       // Returns a list of all edges in the graph. In the case of an directed graph edge[0] goes TO edge[1]. In an undirected graph, provide both directions as separate edges
	NodeList() []int                          // Returns a list of all node IDs in no particular order, useful for determining things like if a graph is fully connected. The caller is free to modify this list (so don't pass a reference to your own list)
	IsDirected() bool
}

// A Graph that implements Coster has an actual cost between adjacent nodes, also known as a weighted graph. If a graph implements coster and a function needs to read cost (e.g. A*), this function will
// take precedence over the Uniform Cost function (all weights are 1) if "nil" is passed in for the function argument
//
// Coster only need worry about the case when an edge from node 1 to node 2 exists (i.e. node2 is a successor to node1) -- asking for the weight in any other case is considered undefined behavior
type Coster interface {
	Cost(node1, node2 int) float64
}

// A graph that implements HeuristicCoster implements a heuristic between any two given nodes. Like Coster, if a graph implements this and a function needs a heuristic cost (e.g. A*), this function will
// take precedence over the Null Heuristic (always returns 0) if "nil" is passed in for the function argument
type HeuristicCoster interface {
	Coster
	HeuristicCost(node1, node2 int) float64 // If HeuristicCost is not intended to be used, it can be implemented as the null heuristic (always returns 0)
}

// A Mutable Graph is a graph that can be changed in an arbitrary way. It is useful for several algorithms; for instance, Johnson's Algorithm requires adding a temporary node and changing edge weights.
// Another case where this is used is computing minimum spanning trees. Since trees are graphs, a minimum spanning tree can be created using this interface.
//
// Note that just because a graph does not implement MutableGraph does not mean that this package expects it to be invariant (though even a MutableGraph should be treated as invariant while an algorithm
// is operating on it), it simply means that without this interface this package can not properly handle the graph in order to, say, fill it with a minimum spanning tree.
//
// In functions that take a MutableGraph as an argument, it should not be the same as the Graph argument as concurrent modification will likely cause problems in most cases.
type MutableGraph interface {
	Graph
	NewNode(successors []int) int               // Adds a node with an arbitrary ID, and returns the new, unique ID used
	AddNode(id int, successors []int)           // The graph itself is responsible for adding reciprocal edges if it's undirected. Likewise, the graph itself must add any non-existant edges listed in successors.
	AddEdge(node1, node2 int)                   // For a digraph, adds node1->node2; the graph is free to initialize this to any value it wishes. Node1 must exist, or it will result in undefined behavior, node2 must be created by the function if absent
	SetEdgeCost(node1, node2 int, cost float64) // The behavior is undefined if the edge has not been created with AddEdge (or the edge was removed before this function was called). For a directed graph only sets node1->node2
	RemoveNode(node int)                        // The graph is reponsible for removing edges to a node that is removed
	RemoveEdge(node1, node2 int)                // The graph is responsible for removing reciprocal edges if it's undirected
	EmptyGraph()                                // Clears the graph of all nodes and edges
	SetDirected(bool)                           // This package will only call SetDirected on an empty graph, so there's no need to worry about the case where a graph suddenly becomes (un)directed
}

// A package that contains an edge (as from EdgeList), and a Weight (as if Cost(Edge[0], Edge[1]) had been called)
type WeightedEdge struct {
	Edge   [2]int
	Weight float64
}

/* Basic Graph tests */

// An undirected graph is fully connected if Dijkstra's algorithm can find a cost to every node, so that's what we do. We use UniformCost instead of the graph's own weights to avoid the problems
// with negative edge weights. If there are no nodes, or only one, it's considered trivially connected.
func FullyConnectedUndirected(graph Graph) bool {
	nodes := graph.NodeList()
	if nodes == nil || len(nodes) <= 1 {
		return true
	}

	arbitraryNode := nodes[0]
	_, costs := Dijkstra(arbitraryNode, graph, UniformCost)

	return len(costs) == len(nodes)
}

func Tarjan(graph Graph) (sccs [][]int) {
	index := 0
	vStack := &Stack{}
	stackSet := NewSet()
	sccs = make([][]int, 0)

	nodes := graph.NodeList()
	lowlinks := make(map[int]int, len(nodes))
	indices := make(map[int]int, len(nodes))

	var strongconnect func(int) []int

	strongconnect = func(node int) []int {
		indices[node] = index
		lowlinks[node] = index
		index += 1

		vStack.Push(node)
		stackSet.Add(node)

		for _, succ := range graph.Successors(node) {
			if _, ok := indices[succ]; !ok {
				strongconnect(succ)
				lowlinks[node] = int(math.Min(float64(lowlinks[node]), float64(lowlinks[succ])))
			} else if stackSet.Contains(succ) {
				lowlinks[node] = int(math.Min(float64(lowlinks[node]), float64(lowlinks[succ])))
			}
		}

		if lowlinks[node] == indices[node] {
			scc := make([]int, 0)
			for {
				v, _ := vStack.Pop()
				stackSet.Remove(v.(int))
				scc = append(scc, v.(int))
				if v.(int) == node {
					return scc
				}
			}
		}

		return nil
	}

	for _, n := range nodes {
		if _, ok := indices[n]; !ok {
			sccs = append(sccs, strongconnect(n))
		}
	}

	return sccs
}

// Returns true if, starting at path[0] and ending at path[len(path)-1], all nodes between are valid neighbors. That is, for each element path[i], path[i+1] is a valid successor
//
// Special case: a nil or zero length path is considered valid (true), a path of length 1 (only one node) is the trivial case, but only if the node listed in path exists.
func IsPath(path []int, graph Graph) bool {
	if path == nil || len(path) == 0 {
		return true
	} else if len(path) == 1 {
		return graph.NodeExists(path[0])
	}

	for i := 0; i < len(path)-1; i++ {
		if !graph.IsSuccessor(path[i], path[i+1]) {
			return false
		}
	}

	return true
}

/* Implements minimum-spanning tree algorithms; puts the resulting minimum spanning tree in the dst graph */

// Generates a minimum spanning tree with sets.
//
// As with other algorithms that use Cost, the order of precedence is Argument > Interface > UniformCost
func Prim(dst MutableGraph, graph Graph, Cost func(int, int) float64) {
	if Cost == nil {
		if cgraph, ok := graph.(Coster); ok {
			Cost = cgraph.Cost
		} else {
			Cost = UniformCost
		}
	}
	dst.EmptyGraph()
	dst.SetDirected(false)

	nlist := graph.NodeList()

	if nlist == nil || len(nlist) == 0 {
		return
	}

	dst.AddNode(nlist[0], nil)
	remainingNodes := NewSet()
	for _, node := range nlist[1:] {
		remainingNodes.Add(node)
	}

	edgeList := graph.EdgeList()
	for remainingNodes.Cardinality() != 0 {
		edgeWeights := make(edgeSorter, 0)
		for _, edge := range edgeList {
			if dst.NodeExists(edge[0]) && remainingNodes.Contains(edge[1]) {
				edgeWeights = append(edgeWeights, WeightedEdge{edge, Cost(edge[0], edge[1])})
			}
		}

		sort.Sort(edgeWeights)
		myEdge := edgeWeights[0]

		if !dst.NodeExists(myEdge.Edge[0]) {
			dst.AddNode(myEdge.Edge[0], []int{myEdge.Edge[1]})
		} else {
			dst.AddEdge(myEdge.Edge[0], myEdge.Edge[1])
		}
		dst.SetEdgeCost(myEdge.Edge[0], myEdge.Edge[1], myEdge.Weight)

		remainingNodes.Remove(myEdge.Edge[1])
	}

}

// Generates a minimum spanning tree for a graph using discrete.DisjointSet
//
// As with other algorithms with Cost, the precedence goes Argument > Interface > UniformCost
func Kruskal(dst MutableGraph, graph Graph, Cost func(int, int) float64) {
	if Cost == nil {
		if cgraph, ok := graph.(Coster); ok {
			Cost = cgraph.Cost
		} else {
			Cost = UniformCost
		}
	}
	dst.EmptyGraph()
	dst.SetDirected(false)

	edgeList := graph.EdgeList()
	edgeWeights := make(edgeSorter, 0, len(edgeList))
	for _, edge := range edgeList {
		edgeWeights = append(edgeWeights, WeightedEdge{edge, Cost(edge[0], edge[1])})
	}

	sort.Sort(edgeWeights)

	ds := NewDisjointSet()
	for _, node := range graph.NodeList() {
		ds.MakeSet(node)
	}

	for _, edge := range edgeWeights {
		if s1, s2 := ds.Find(edge.Edge[0]), ds.Find(edge.Edge[1]); s1 != s2 {
			ds.Union(s1, s2)
			if !dst.NodeExists(edge.Edge[0]) {
				dst.AddNode(edge.Edge[0], []int{edge.Edge[1]})
			} else {
				dst.AddEdge(edge.Edge[0], edge.Edge[1])
			}
			dst.SetEdgeCost(edge.Edge[0], edge.Edge[1], edge.Weight)
		}
	}
}

/* Control flow graph stuff */

// A dominates B if and only if the only path through B travels through A
//
// This returns all possible dominators for all nodes, it does not prune for strict dominators, immediate dominators etc
func Dominators(start int, graph Graph) map[int]*Set {
	allNodes := NewSet()
	nlist := graph.NodeList()
	dominators := make(map[int]*Set, len(nlist))
	for _, node := range nlist {
		allNodes.Add(node)
	}

	for _, node := range nlist {
		dominators[node] = NewSet()
		if node == start {
			dominators[node].Add(start)
		} else {
			dominators[node].Copy(allNodes)
		}
	}

	for somethingChanged := true; somethingChanged; {
		somethingChanged = false
		for _, node := range nlist {
			if node == start {
				continue
			}
			preds := graph.Predecessors(node)
			if len(preds) == 0 {
				continue
			}
			tmp := NewSet().Copy(dominators[preds[0]])
			for _, pred := range preds[1:] {
				tmp.Intersection(tmp, dominators[pred])
			}

			dom := NewSet()
			dom.Add(node)

			dom.Union(dom, tmp)
			if !Equal(dom, dominators[node]) {
				dominators[node] = dom
				somethingChanged = true
			}
		}
	}

	return dominators
}

// A Postdominates B if and only if all paths from B travel through A
//
// This returns all possible post-dominators for all nodes, it does not prune for strict postdominators, immediate postdominators etc
func PostDominators(end int, graph Graph) map[int]*Set {
	allNodes := NewSet()
	nlist := graph.NodeList()
	dominators := make(map[int]*Set, len(nlist))
	for _, node := range nlist {
		allNodes.Add(node)
	}

	for _, node := range nlist {
		dominators[node] = NewSet()
		if node == end {
			dominators[node].Add(end)
		} else {
			dominators[node].Copy(allNodes)
		}
	}

	for somethingChanged := true; somethingChanged; {
		somethingChanged = false
		for _, node := range nlist {
			if node == end {
				continue
			}
			succs := graph.Successors(node)
			if len(succs) == 0 {
				continue
			}
			tmp := NewSet().Copy(dominators[succs[0]])
			for _, succ := range succs[1:] {
				tmp.Intersection(tmp, dominators[succ])
			}

			dom := NewSet()
			dom.Add(node)

			dom.Union(dom, tmp)
			if !Equal(dom, dominators[node]) {
				dominators[node] = dom
				somethingChanged = true
			}
		}
	}

	return dominators
}

/* Purely internal data structures and functions (mostly for sorting) */

/** Sorts a list of edges by weight, agnostic to repeated edges as well as direction **/

type edgeSorter []WeightedEdge

func (el edgeSorter) Len() int {
	return len(el)
}

func (el edgeSorter) Less(i, j int) bool {
	return el[i].Weight < el[j].Weight
}

func (el edgeSorter) Swap(i, j int) {
	el[i], el[j] = el[j], el[i]
}
<commit_msg>Comments<commit_after>package discrete

import (
	"math"
	"sort"
)

// A Graph implements all methods necessary to run graph-specific algorithms on it. Strictly speaking EdgeList() and NodeList() would be sufficient, but the others (such as Successors)
// are required to make the algorithms more efficient and/or easier to write and maintain.
type Graph interface {
	Successors(node int) []int                // Gives the nodes connected by OUTBOUND edges, if the graph is an undirected graph, this set is equal to Predecessors
	IsSuccessor(node, successor int) bool     // If successor shows up in the list returned by Successors(node), then it's a successor. If node doesn't exist, this should always return false
	Predecessors(node int) []int              // Gives the nodes connected by INBOUND edges, if the graph is an undirected graph, this set is equal to Successors
	IsPredecessor(node, predecessor int) bool // If predecessor shows up in the list returned by Predecessors(node), then it's a predecessor. If node doesn't exist, this should always return false
	IsAdjacent(node, neighbor int) bool       // IsSuccessor || IsPredecessor
	NodeExists(node int) bool                 // Returns whether a node with the given ID is currently in the graph
	Degree(node int) int                      // Degree is equivalent to len(Successors(node)) + len(Predecessors(node)); this means that reflexive edges are counted twice
	EdgeList() [][2]int                       // Returns a list of all edges in the graph. In the case of an directed graph edge[0] goes TO edge[1]. In an undirected graph, provide both directions as separate edges
	NodeList() []int                          // Returns a list of all node IDs in no particular order, useful for determining things like if a graph is fully connected. The caller is free to modify this list (so don't pass a reference to your own list)
	IsDirected() bool
}

// A Graph that implements Coster has an actual cost between adjacent nodes, also known as a weighted graph. If a graph implements coster and a function needs to read cost (e.g. A*), this function will
// take precedence over the Uniform Cost function (all weights are 1) if "nil" is passed in for the function argument
//
// Coster only need worry about the case when an edge from node 1 to node 2 exists (i.e. node2 is a successor to node1) -- asking for the weight in any other case is considered undefined behavior
type Coster interface {
	Cost(node1, node2 int) float64
}

// A graph that implements HeuristicCoster implements a heuristic between any two given nodes. Like Coster, if a graph implements this and a function needs a heuristic cost (e.g. A*), this function will
// take precedence over the Null Heuristic (always returns 0) if "nil" is passed in for the function argument
type HeuristicCoster interface {
	Coster
	HeuristicCost(node1, node2 int) float64 // If HeuristicCost is not intended to be used, it can be implemented as the null heuristic (always returns 0)
}

// A Mutable Graph is a graph that can be changed in an arbitrary way. It is useful for several algorithms; for instance, Johnson's Algorithm requires adding a temporary node and changing edge weights.
// Another case where this is used is computing minimum spanning trees. Since trees are graphs, a minimum spanning tree can be created using this interface.
//
// Note that just because a graph does not implement MutableGraph does not mean that this package expects it to be invariant (though even a MutableGraph should be treated as invariant while an algorithm
// is operating on it), it simply means that without this interface this package can not properly handle the graph in order to, say, fill it with a minimum spanning tree.
//
// In functions that take a MutableGraph as an argument, it should not be the same as the Graph argument as concurrent modification will likely cause problems in most cases.
type MutableGraph interface {
	Graph
	NewNode(successors []int) int               // Adds a node with an arbitrary ID, and returns the new, unique ID used
	AddNode(id int, successors []int)           // The graph itself is responsible for adding reciprocal edges if it's undirected. Likewise, the graph itself must add any non-existant nodes listed in successors.
	AddEdge(node1, node2 int)                   // For a digraph, adds node1->node2; the graph is free to initialize this to any value it wishes. Node1 must exist, or it will result in undefined behavior, node2 must be created by the function if absent
	SetEdgeCost(node1, node2 int, cost float64) // The behavior is undefined if the edge has not been created with AddEdge (or the edge was removed before this function was called). For a directed graph only sets node1->node2
	RemoveNode(node int)                        // The graph is reponsible for removing edges to a node that is removed
	RemoveEdge(node1, node2 int)                // The graph is responsible for removing reciprocal edges if it's undirected
	EmptyGraph()                                // Clears the graph of all nodes and edges
	SetDirected(bool)                           // This package will only call SetDirected on an empty graph, so there's no need to worry about the case where a graph suddenly becomes (un)directed
}

// A package that contains an edge (as from EdgeList), and a Weight (as if Cost(Edge[0], Edge[1]) had been called)
type WeightedEdge struct {
	Edge   [2]int
	Weight float64
}

/* Basic Graph tests */

// Also known as Tarjan's Strongly Connected Components Algorithm. This returns all the strongly connected components in the graph.
//
// A strongly connected component of a graph is a set of vertices where it's possible to reach any vertex in the set from any other (meaning there's a cycle between them)
//
// Generally speaking, a directed graph where the number of strongly connected components is equal to the number of nodes is acyclic, unless you count reflexive edges as a cycle (which requires only a little extra testing)
//
// An undirected graph should end up with as many SCCs as there are "islands" (or subgraphs) of connections, meaning having more than one strongly connected component implies that your graph is not fully connected.
func Tarjan(graph Graph) (sccs [][]int) {
	index := 0
	vStack := &Stack{}
	stackSet := NewSet()
	sccs = make([][]int, 0)

	nodes := graph.NodeList()
	lowlinks := make(map[int]int, len(nodes))
	indices := make(map[int]int, len(nodes))

	var strongconnect func(int) []int

	strongconnect = func(node int) []int {
		indices[node] = index
		lowlinks[node] = index
		index += 1

		vStack.Push(node)
		stackSet.Add(node)

		for _, succ := range graph.Successors(node) {
			if _, ok := indices[succ]; !ok {
				strongconnect(succ)
				lowlinks[node] = int(math.Min(float64(lowlinks[node]), float64(lowlinks[succ])))
			} else if stackSet.Contains(succ) {
				lowlinks[node] = int(math.Min(float64(lowlinks[node]), float64(lowlinks[succ])))
			}
		}

		if lowlinks[node] == indices[node] {
			scc := make([]int, 0)
			for {
				v, _ := vStack.Pop()
				stackSet.Remove(v.(int))
				scc = append(scc, v.(int))
				if v.(int) == node {
					return scc
				}
			}
		}

		return nil
	}

	for _, n := range nodes {
		if _, ok := indices[n]; !ok {
			sccs = append(sccs, strongconnect(n))
		}
	}

	return sccs
}

// Returns true if, starting at path[0] and ending at path[len(path)-1], all nodes between are valid neighbors. That is, for each element path[i], path[i+1] is a valid successor
//
// Special case: a nil or zero length path is considered valid (true), a path of length 1 (only one node) is the trivial case, but only if the node listed in path exists.
func IsPath(path []int, graph Graph) bool {
	if path == nil || len(path) == 0 {
		return true
	} else if len(path) == 1 {
		return graph.NodeExists(path[0])
	}

	for i := 0; i < len(path)-1; i++ {
		if !graph.IsSuccessor(path[i], path[i+1]) {
			return false
		}
	}

	return true
}

/* Implements minimum-spanning tree algorithms; puts the resulting minimum spanning tree in the dst graph */

// Generates a minimum spanning tree with sets.
//
// As with other algorithms that use Cost, the order of precedence is Argument > Interface > UniformCost
func Prim(dst MutableGraph, graph Graph, Cost func(int, int) float64) {
	if Cost == nil {
		if cgraph, ok := graph.(Coster); ok {
			Cost = cgraph.Cost
		} else {
			Cost = UniformCost
		}
	}
	dst.EmptyGraph()
	dst.SetDirected(false)

	nlist := graph.NodeList()

	if nlist == nil || len(nlist) == 0 {
		return
	}

	dst.AddNode(nlist[0], nil)
	remainingNodes := NewSet()
	for _, node := range nlist[1:] {
		remainingNodes.Add(node)
	}

	edgeList := graph.EdgeList()
	for remainingNodes.Cardinality() != 0 {
		edgeWeights := make(edgeSorter, 0)
		for _, edge := range edgeList {
			if dst.NodeExists(edge[0]) && remainingNodes.Contains(edge[1]) {
				edgeWeights = append(edgeWeights, WeightedEdge{edge, Cost(edge[0], edge[1])})
			}
		}

		sort.Sort(edgeWeights)
		myEdge := edgeWeights[0]

		if !dst.NodeExists(myEdge.Edge[0]) {
			dst.AddNode(myEdge.Edge[0], []int{myEdge.Edge[1]})
		} else {
			dst.AddEdge(myEdge.Edge[0], myEdge.Edge[1])
		}
		dst.SetEdgeCost(myEdge.Edge[0], myEdge.Edge[1], myEdge.Weight)

		remainingNodes.Remove(myEdge.Edge[1])
	}

}

// Generates a minimum spanning tree for a graph using discrete.DisjointSet
//
// As with other algorithms with Cost, the precedence goes Argument > Interface > UniformCost
func Kruskal(dst MutableGraph, graph Graph, Cost func(int, int) float64) {
	if Cost == nil {
		if cgraph, ok := graph.(Coster); ok {
			Cost = cgraph.Cost
		} else {
			Cost = UniformCost
		}
	}
	dst.EmptyGraph()
	dst.SetDirected(false)

	edgeList := graph.EdgeList()
	edgeWeights := make(edgeSorter, 0, len(edgeList))
	for _, edge := range edgeList {
		edgeWeights = append(edgeWeights, WeightedEdge{edge, Cost(edge[0], edge[1])})
	}

	sort.Sort(edgeWeights)

	ds := NewDisjointSet()
	for _, node := range graph.NodeList() {
		ds.MakeSet(node)
	}

	for _, edge := range edgeWeights {
		if s1, s2 := ds.Find(edge.Edge[0]), ds.Find(edge.Edge[1]); s1 != s2 {
			ds.Union(s1, s2)
			if !dst.NodeExists(edge.Edge[0]) {
				dst.AddNode(edge.Edge[0], []int{edge.Edge[1]})
			} else {
				dst.AddEdge(edge.Edge[0], edge.Edge[1])
			}
			dst.SetEdgeCost(edge.Edge[0], edge.Edge[1], edge.Weight)
		}
	}
}

/* Control flow graph stuff */

// A dominates B if and only if the only path through B travels through A
//
// This returns all possible dominators for all nodes, it does not prune for strict dominators, immediate dominators etc
func Dominators(start int, graph Graph) map[int]*Set {
	allNodes := NewSet()
	nlist := graph.NodeList()
	dominators := make(map[int]*Set, len(nlist))
	for _, node := range nlist {
		allNodes.Add(node)
	}

	for _, node := range nlist {
		dominators[node] = NewSet()
		if node == start {
			dominators[node].Add(start)
		} else {
			dominators[node].Copy(allNodes)
		}
	}

	for somethingChanged := true; somethingChanged; {
		somethingChanged = false
		for _, node := range nlist {
			if node == start {
				continue
			}
			preds := graph.Predecessors(node)
			if len(preds) == 0 {
				continue
			}
			tmp := NewSet().Copy(dominators[preds[0]])
			for _, pred := range preds[1:] {
				tmp.Intersection(tmp, dominators[pred])
			}

			dom := NewSet()
			dom.Add(node)

			dom.Union(dom, tmp)
			if !Equal(dom, dominators[node]) {
				dominators[node] = dom
				somethingChanged = true
			}
		}
	}

	return dominators
}

// A Postdominates B if and only if all paths from B travel through A
//
// This returns all possible post-dominators for all nodes, it does not prune for strict postdominators, immediate postdominators etc
func PostDominators(end int, graph Graph) map[int]*Set {
	allNodes := NewSet()
	nlist := graph.NodeList()
	dominators := make(map[int]*Set, len(nlist))
	for _, node := range nlist {
		allNodes.Add(node)
	}

	for _, node := range nlist {
		dominators[node] = NewSet()
		if node == end {
			dominators[node].Add(end)
		} else {
			dominators[node].Copy(allNodes)
		}
	}

	for somethingChanged := true; somethingChanged; {
		somethingChanged = false
		for _, node := range nlist {
			if node == end {
				continue
			}
			succs := graph.Successors(node)
			if len(succs) == 0 {
				continue
			}
			tmp := NewSet().Copy(dominators[succs[0]])
			for _, succ := range succs[1:] {
				tmp.Intersection(tmp, dominators[succ])
			}

			dom := NewSet()
			dom.Add(node)

			dom.Union(dom, tmp)
			if !Equal(dom, dominators[node]) {
				dominators[node] = dom
				somethingChanged = true
			}
		}
	}

	return dominators
}

/* Purely internal data structures and functions (mostly for sorting) */

/** Sorts a list of edges by weight, agnostic to repeated edges as well as direction **/

type edgeSorter []WeightedEdge

func (el edgeSorter) Len() int {
	return len(el)
}

func (el edgeSorter) Less(i, j int) bool {
	return el[i].Weight < el[j].Weight
}

func (el edgeSorter) Swap(i, j int) {
	el[i], el[j] = el[j], el[i]
}
<|endoftext|>


<commit_before>package opstocat

import (
	"fmt"
	"github.com/peterbourgon/g2s"
	"github.com/technoweenie/grohl"
	"log/syslog"
	"net/url"
	"os"
	"runtime"
	"time"
)

func SetupLogger(config ConfigWrapper) {
	innerconfig := config.OpstocatConfiguration()
	logch := make(chan grohl.Data, 100)
	chlogger, _ := grohl.NewChannelLogger(logch)
	grohl.SetLogger(chlogger)

	if len(innerconfig.StatsDAddress) > 0 {
		if innerconfig.StatsDAddress == "noop" {
			grohl.CurrentStatter = &NoOpStatter{}
		} else {
			statter, err := g2s.Dial("udp", innerconfig.StatsDAddress)
			if err != nil {
				grohl.Report(err, grohl.Data{"statsd_address": innerconfig.StatsDAddress})
				grohl.CurrentStatter = &NoOpStatter{}
			} else {
				grohl.CurrentStatter = statter
			}
		}
	}

	grohl.CurrentStatter = PrefixedStatter(innerconfig.App, grohl.CurrentStatter)

	if len(innerconfig.HaystackEndpoint) > 0 {
		reporter, err := NewHaystackReporter(innerconfig)
		if err != nil {
			grohl.Report(err, grohl.Data{"haystack_enpdoint": innerconfig.HaystackEndpoint})
		} else {
			grohl.SetErrorReporter(reporter)
		}
	}

	grohl.AddContext("app", innerconfig.App)
	grohl.AddContext("deploy", innerconfig.Env)
	grohl.AddContext("sha", innerconfig.Sha)

	var logger grohl.Logger
	if len(innerconfig.SyslogAddr) > 0 {
		writer, err := newSyslogWriter(innerconfig.SyslogAddr, innerconfig.App)
		if err == nil {
			logger = grohl.NewIoLogger(writer)
		}
	}

	if logger == nil {
		logger = grohl.NewIoLogger(nil)
	}

	go grohl.Watch(logger, logch)
}

func newSyslogWriter(configAddr, tag string) (*syslog.Writer, error) {
	net, addr, err := parseAddr(configAddr)
	if err != nil {
		return nil, err
	}
	writer, err := syslog.Dial(net, addr, syslog.LOG_INFO|syslog.LOG_LOCAL7, tag)
	if err != nil {
		grohl.Report(err, grohl.Data{"syslog_network": net, "syslog_addr": addr})
		fmt.Fprintf(os.Stderr, "Error opening syslog connection: %s\n", err)
	}
	return writer, err
}

func parseAddr(s string) (string, string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return "", "", err
	}

	if u.Host == "" {
		return u.Scheme, u.Path, nil
	}
	return u.Scheme, u.Host, nil
}

func SendPeriodicStats(duration string, config ConfigWrapper, callback func(keyprefix string)) error {
	innerconfig := config.OpstocatConfiguration()
	if !innerconfig.ShowPeriodicStats() {
		return nil
	}

	dur, err := time.ParseDuration(duration)
	if err != nil {
		return err
	}

	keyprefix := fmt.Sprintf("sys.%s.", innerconfig.Hostname)
	if callback == nil {
		callback = nopPeriodicCallback
	}

	go sendPeriodicStats(dur, keyprefix, callback)
	return nil
}

func sendPeriodicStats(dur time.Duration, keyprefix string, callback func(keyprefix string)) {
	for {
		time.Sleep(dur)
		grohl.Gauge(1.0, keyprefix+"goroutines", grohl.Format(runtime.NumGoroutine()))
		callback(keyprefix)
	}
}

func nopPeriodicCallback(keyprefix string) {}

func PrefixedStatter(prefix string, statter g2s.Statter) g2s.Statter {
	if prefix == "" {
		return statter
	}

	return &PrefixStatter{prefix, statter}
}

type PrefixStatter struct {
	Prefix  string
	Statter g2s.Statter
}

func (s *PrefixStatter) Counter(sampleRate float32, bucket string, n ...int) {
	s.Statter.Counter(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), n...)
}

func (s *PrefixStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {
	s.Statter.Timing(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), d...)
}

func (s *PrefixStatter) Gauge(sampleRate float32, bucket string, value ...string) {
	s.Statter.Gauge(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), value...)
}

type NoOpStatter struct{}

func (s *NoOpStatter) Counter(sampleRate float32, bucket string, n ...int)          {}
func (s *NoOpStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {}
func (s *NoOpStatter) Gauge(sampleRate float32, bucket string, value ...string)     {}
<commit_msg>Starts sending some memory allocation statistics<commit_after>package opstocat

import (
	"fmt"
	"github.com/peterbourgon/g2s"
	"github.com/technoweenie/grohl"
	"log/syslog"
	"net/url"
	"os"
	"runtime"
	"time"
)

func SetupLogger(config ConfigWrapper) {
	innerconfig := config.OpstocatConfiguration()
	logch := make(chan grohl.Data, 100)
	chlogger, _ := grohl.NewChannelLogger(logch)
	grohl.SetLogger(chlogger)

	if len(innerconfig.StatsDAddress) > 0 {
		if innerconfig.StatsDAddress == "noop" {
			grohl.CurrentStatter = &NoOpStatter{}
		} else {
			statter, err := g2s.Dial("udp", innerconfig.StatsDAddress)
			if err != nil {
				grohl.Report(err, grohl.Data{"statsd_address": innerconfig.StatsDAddress})
				grohl.CurrentStatter = &NoOpStatter{}
			} else {
				grohl.CurrentStatter = statter
			}
		}
	}

	grohl.CurrentStatter = PrefixedStatter(innerconfig.App, grohl.CurrentStatter)

	if len(innerconfig.HaystackEndpoint) > 0 {
		reporter, err := NewHaystackReporter(innerconfig)
		if err != nil {
			grohl.Report(err, grohl.Data{"haystack_enpdoint": innerconfig.HaystackEndpoint})
		} else {
			grohl.SetErrorReporter(reporter)
		}
	}

	grohl.AddContext("app", innerconfig.App)
	grohl.AddContext("deploy", innerconfig.Env)
	grohl.AddContext("sha", innerconfig.Sha)

	var logger grohl.Logger
	if len(innerconfig.SyslogAddr) > 0 {
		writer, err := newSyslogWriter(innerconfig.SyslogAddr, innerconfig.App)
		if err == nil {
			logger = grohl.NewIoLogger(writer)
		}
	}

	if logger == nil {
		logger = grohl.NewIoLogger(nil)
	}

	go grohl.Watch(logger, logch)
}

func newSyslogWriter(configAddr, tag string) (*syslog.Writer, error) {
	net, addr, err := parseAddr(configAddr)
	if err != nil {
		return nil, err
	}
	writer, err := syslog.Dial(net, addr, syslog.LOG_INFO|syslog.LOG_LOCAL7, tag)
	if err != nil {
		grohl.Report(err, grohl.Data{"syslog_network": net, "syslog_addr": addr})
		fmt.Fprintf(os.Stderr, "Error opening syslog connection: %s\n", err)
	}
	return writer, err
}

func parseAddr(s string) (string, string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return "", "", err
	}

	if u.Host == "" {
		return u.Scheme, u.Path, nil
	}
	return u.Scheme, u.Host, nil
}

func SendPeriodicStats(duration string, config ConfigWrapper, callback func(keyprefix string)) error {
	innerconfig := config.OpstocatConfiguration()
	if !innerconfig.ShowPeriodicStats() {
		return nil
	}

	dur, err := time.ParseDuration(duration)
	if err != nil {
		return err
	}

	keyprefix := fmt.Sprintf("sys.%s.", innerconfig.Hostname)
	if callback == nil {
		callback = nopPeriodicCallback
	}

	go sendPeriodicStats(dur, keyprefix, callback)
	return nil
}

func sendPeriodicStats(dur time.Duration, keyprefix string, callback func(keyprefix string)) {
	var memStats runtime.MemStats
	for {
		time.Sleep(dur)
		grohl.Gauge(1.0, keyprefix+"goroutines", grohl.Format(runtime.NumGoroutine()))

		runtime.ReadMemStats(&memStats)
		grohl.Gauge(1.0, keyprefix+"memory.alloc", grohl.Format(memStats.Alloc))
		grohl.Gauge(1.0, keyprefix+"memory.sys", grohl.Format(memStats.Sys))

		callback(keyprefix)
	}
}

func nopPeriodicCallback(keyprefix string) {}

func PrefixedStatter(prefix string, statter g2s.Statter) g2s.Statter {
	if prefix == "" {
		return statter
	}

	return &PrefixStatter{prefix, statter}
}

type PrefixStatter struct {
	Prefix  string
	Statter g2s.Statter
}

func (s *PrefixStatter) Counter(sampleRate float32, bucket string, n ...int) {
	s.Statter.Counter(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), n...)
}

func (s *PrefixStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {
	s.Statter.Timing(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), d...)
}

func (s *PrefixStatter) Gauge(sampleRate float32, bucket string, value ...string) {
	s.Statter.Gauge(sampleRate, fmt.Sprintf("%s.%s", s.Prefix, bucket), value...)
}

type NoOpStatter struct{}

func (s *NoOpStatter) Counter(sampleRate float32, bucket string, n ...int)          {}
func (s *NoOpStatter) Timing(sampleRate float32, bucket string, d ...time.Duration) {}
func (s *NoOpStatter) Gauge(sampleRate float32, bucket string, value ...string)     {}
<|endoftext|>