<commit_before>package myconfig

import (
	"context"
	"errors"
	"fmt"
	"log"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/coreos/etcd/clientv3"
)

const (
	dialTimeout    = 5 * time.Second
	requestTimeout = 1 * time.Second
)

// Config holds configuration data.
type Config struct {
	Endpoints []string
	Env       string
	Service   string
}

// Client is configuration client.
type Client struct {
	etcdClient    *clientv3.Client
	globalPrefix  string
	servicePrefix string
	storage       *data
}

// New creates configuration client.
func New(cfg Config) (*Client, error) {

	ec, err := clientv3.New(clientv3.Config{
		Endpoints:   cfg.Endpoints,
		DialTimeout: dialTimeout,
	})

	if err != nil {
		return nil, err
	}

	c := &Client{
		etcdClient: ec,
		storage:    newData(),
	}

	if err := c.setPrefixes(cfg.Env, cfg.Service); err != nil {
		return nil, fmt.Errorf("prefixes: %v", err)
	}

	var wg sync.WaitGroup
	wg.Add(2)

	go c.updateStorage(&wg, c.globalPrefix)

	go c.updateStorage(&wg, c.servicePrefix)

	wg.Wait()

	return c, nil

}

func (c *Client) String(key string) (string, bool) {
	v, ok := c.storage.get(key)
	return v, ok
}

func (c *Client) updateStorage(wg *sync.WaitGroup, prefix string) {
	defer wg.Done()
	dm, err := c.get(prefix)
	if err != nil {
		log.Printf("update client: %s: %v", prefix, err)
	}
	c.storage.update(dm)
}

const companyKey = "com"

func (c *Client) setPrefixes(env, service string) error {
	if env == "" {
		return errors.New("empty env")
	}
	if service == "" {
		return errors.New("empty service")
	}
	root := fmt.Sprintf("/%s/%s", companyKey, env)
	c.globalPrefix = fmt.Sprintf("%s/global", root)
	c.servicePrefix = fmt.Sprintf("%s/%s", root, service)
	return nil
}

func (c *Client) get(prefix string) (dataMap, error) {

	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	resp, err := c.etcdClient.Get(
		ctx,
		prefix,
		clientv3.WithPrefix(),
		clientv3.WithSerializable(),
	)

	defer cancel()

	dm := dataMap{}

	if err != nil {
		return dm, err
	}

	if len(resp.Kvs) < 1 {
		return dm, errors.New("not exists")
	}

	var k, v string

	for _, ev := range resp.Kvs {
		k = string(ev.Key)
		if strings.HasPrefix(k, c.globalPrefix) {
			k = "/global" + strings.TrimPrefix(k, c.globalPrefix)
		}
		k = strings.TrimPrefix(k, c.servicePrefix)

		v = string(ev.Value)

		dm[k] = v
	}
	return dm, nil
}

// Close closes client.
func Close(c *Client) {

	err := c.etcdClient.Close()
	if err != nil {
		// TODO(dvrkps): add better logging
		log.Print(err)
	}
}

func checkKey(key string) error {
	if key == "" || key == "/" {
		return errors.New("empty key")
	}
	if path.IsAbs(key) {
		return errors.New("relative key")
	}
	return nil
}
<commit_msg>etcdconfig/myconfig: fix checkKey<commit_after>package myconfig

import (
	"context"
	"errors"
	"fmt"
	"log"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/coreos/etcd/clientv3"
)

const (
	dialTimeout    = 5 * time.Second
	requestTimeout = 1 * time.Second
)

// Config holds configuration data.
type Config struct {
	Endpoints []string
	Env       string
	Service   string
}

// Client is configuration client.
type Client struct {
	etcdClient    *clientv3.Client
	globalPrefix  string
	servicePrefix string
	storage       *data
}

// New creates configuration client.
func New(cfg Config) (*Client, error) {

	ec, err := clientv3.New(clientv3.Config{
		Endpoints:   cfg.Endpoints,
		DialTimeout: dialTimeout,
	})

	if err != nil {
		return nil, err
	}

	c := &Client{
		etcdClient: ec,
		storage:    newData(),
	}

	if err := c.setPrefixes(cfg.Env, cfg.Service); err != nil {
		return nil, fmt.Errorf("prefixes: %v", err)
	}

	var wg sync.WaitGroup
	wg.Add(2)

	go c.updateStorage(&wg, c.globalPrefix)

	go c.updateStorage(&wg, c.servicePrefix)

	wg.Wait()

	return c, nil

}

func (c *Client) String(key string) (string, bool) {
	v, ok := c.storage.get(key)
	return v, ok
}

func (c *Client) updateStorage(wg *sync.WaitGroup, prefix string) {
	defer wg.Done()
	dm, err := c.get(prefix)
	if err != nil {
		log.Printf("update client: %s: %v", prefix, err)
	}
	c.storage.update(dm)
}

const companyKey = "com"

func (c *Client) setPrefixes(env, service string) error {
	if env == "" {
		return errors.New("empty env")
	}
	if service == "" {
		return errors.New("empty service")
	}
	root := fmt.Sprintf("/%s/%s", companyKey, env)
	c.globalPrefix = fmt.Sprintf("%s/global", root)
	c.servicePrefix = fmt.Sprintf("%s/%s", root, service)
	return nil
}

func (c *Client) get(prefix string) (dataMap, error) {

	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	resp, err := c.etcdClient.Get(
		ctx,
		prefix,
		clientv3.WithPrefix(),
		clientv3.WithSerializable(),
	)

	defer cancel()

	dm := dataMap{}

	if err != nil {
		return dm, err
	}

	if len(resp.Kvs) < 1 {
		return dm, errors.New("not exists")
	}

	var k, v string

	for _, ev := range resp.Kvs {
		k = string(ev.Key)
		if strings.HasPrefix(k, c.globalPrefix) {
			k = "/global" + strings.TrimPrefix(k, c.globalPrefix)
		}
		k = strings.TrimPrefix(k, c.servicePrefix)

		v = string(ev.Value)

		dm[k] = v
	}
	return dm, nil
}

// Close closes client.
func Close(c *Client) {

	err := c.etcdClient.Close()
	if err != nil {
		// TODO(dvrkps): add better logging
		log.Print(err)
	}
}

func checkKey(key string) error {
	if key == "" || key == "/" {
		return errors.New("empty key")
	}
	if !path.IsAbs(key) {
		return errors.New("relative key")
	}
	return nil
}
<|endoftext|>


<commit_before>package gordon

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	MaintainerFileName = "MAINTAINERS"
	NumWorkers         = 10
)

// GetMaintainersFromRepo returns the maintainers for a repo with the username
// as the key and the file's that they own as a slice in the value
func GetMaintainersFromRepo(repoPath string) (map[string][]string, error) {
	current := make(map[string][]string)

	if err := getMaintainersForDirectory(repoPath, repoPath, current); err != nil {
		return nil, err
	}
	return current, nil
}

func getMaintainersForDirectory(root, dir string, current map[string][]string) error {
	maintainersPerFile, err := getMaintainersFromFile(dir)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	for m, files := range maintainersPerFile {
		for _, f := range files {
			p, err := filepath.Rel(root, filepath.Join(dir, f))
			if err != nil {
				return err
			}
			current[m] = append(current[m], p)
		}
	}

	contents, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, fi := range contents {
		if fi.IsDir() && fi.Name() != ".git" {
			if err := getMaintainersForDirectory(root, filepath.Join(dir, fi.Name()), current); err != nil {
				return err
			}
		}
	}
	return nil
}

func getMaintainersFromFile(dir string) (map[string][]string, error) {
	maintainerFile := filepath.Join(dir, MaintainerFileName)
	f, err := os.Open(maintainerFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var (
		maintainer = make(map[string][]string)
		s          = bufio.NewScanner(f)
	)
	for s.Scan() {
		if err := s.Err(); err != nil {
			return nil, err
		}
		t := s.Text()
		if t == "" || t[0] == '#' {
			continue
		}
		m := parseMaintainer(t)
		if m.Email == "" || m.Username == "" {
			return nil, fmt.Errorf("invalid maintainer file format %s in %s", t, maintainerFile)
		}
		target := m.Target
		if target == "" {
			target = "*"
		}
		maintainer[m.Username] = append(maintainer[m.Username], target)
	}
	return maintainer, nil
}

// this function basically reverses the maintainers format so that file paths can be looked
// up by path and the maintainers are the value.  We have to parse the directories differently
// at first then lookup per path when we actually have the files so that it is much faster
// and cleaner than walking a fill dir tree looking at files and placing them into memeory.
//
// I swear I'm not crazy
func buildFileIndex(maintainers map[string][]string) map[string]map[string]bool {
	index := make(map[string]map[string]bool)

	for m, files := range maintainers {
		for _, f := range files {
			nm, exists := index[f]
			if !exists {
				nm = make(map[string]bool)
				index[f] = nm
			}
			nm[m] = true
		}
	}
	return index
}
<commit_msg>Use email and not github name<commit_after>package gordon

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	MaintainerFileName = "MAINTAINERS"
	NumWorkers         = 10
)

// GetMaintainersFromRepo returns the maintainers for a repo with the username
// as the key and the file's that they own as a slice in the value
func GetMaintainersFromRepo(repoPath string) (map[string][]string, error) {
	current := make(map[string][]string)

	if err := getMaintainersForDirectory(repoPath, repoPath, current); err != nil {
		return nil, err
	}
	return current, nil
}

func getMaintainersForDirectory(root, dir string, current map[string][]string) error {
	maintainersPerFile, err := getMaintainersFromFile(dir)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	for m, files := range maintainersPerFile {
		for _, f := range files {
			p, err := filepath.Rel(root, filepath.Join(dir, f))
			if err != nil {
				return err
			}
			current[m] = append(current[m], p)
		}
	}

	contents, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	for _, fi := range contents {
		if fi.IsDir() && fi.Name() != ".git" {
			if err := getMaintainersForDirectory(root, filepath.Join(dir, fi.Name()), current); err != nil {
				return err
			}
		}
	}
	return nil
}

func getMaintainersFromFile(dir string) (map[string][]string, error) {
	maintainerFile := filepath.Join(dir, MaintainerFileName)
	f, err := os.Open(maintainerFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var (
		maintainer = make(map[string][]string)
		s          = bufio.NewScanner(f)
	)
	for s.Scan() {
		if err := s.Err(); err != nil {
			return nil, err
		}
		t := s.Text()
		if t == "" || t[0] == '#' {
			continue
		}
		m := parseMaintainer(t)
		if m.Email == "" {
			return nil, fmt.Errorf("invalid maintainer file format %s in %s", t, maintainerFile)
		}
		target := m.Target
		if target == "" {
			target = "*"
		}
		maintainer[m.Email] = append(maintainer[m.Email], target)
	}
	return maintainer, nil
}

// this function basically reverses the maintainers format so that file paths can be looked
// up by path and the maintainers are the value.  We have to parse the directories differently
// at first then lookup per path when we actually have the files so that it is much faster
// and cleaner than walking a fill dir tree looking at files and placing them into memeory.
//
// I swear I'm not crazy
func buildFileIndex(maintainers map[string][]string) map[string]map[string]bool {
	index := make(map[string]map[string]bool)

	for m, files := range maintainers {
		for _, f := range files {
			nm, exists := index[f]
			if !exists {
				nm = make(map[string]bool)
				index[f] = nm
			}
			nm[m] = true
		}
	}
	return index
}
<|endoftext|>


<commit_before>// Copyright 2017, OpenCensus 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 zipkin contains an exporter for Zipkin.
//
// Example:
//
// 	import (
// 		openzipkin "github.com/openzipkin/zipkin-go"
// 		"github.com/openzipkin/zipkin-go/reporter/http"
// 		"go.opencensus.io/trace/adaptor/zipkin"
// 	)
//	...
//		localEndpoint, err := openzipkin.NewEndpoint("server", "server:5454")
// 		if err != nil {
// 			log.Print(err)
// 		}
// 		reporter := http.NewReporter("http://localhost:9411/api/v2/spans")
// 		exporter := zipkin.NewExporter(reporter, localEndpoint)
// 		trace.RegisterExporter(exporter)
package zipkin

import (
	"encoding/binary"
	"strconv"
	"strings"

	"github.com/openzipkin/zipkin-go/model"
	"github.com/openzipkin/zipkin-go/reporter"
	"go.opencensus.io/trace"
)

// Exporter is an implementation of trace.Exporter that uploads spans to a
// Zipkin server.
type Exporter struct {
	reporter      reporter.Reporter
	localEndpoint *model.Endpoint
}

// NewExporter returns an implementation of trace.Exporter that uploads spans
// to a Zipkin server.
//
// reporter is a Zipkin Reporter which will be used to send the spans.  These
// can be created with the openzipkin library, using one of the packages under
// github.com/openzipkin/zipkin-go/reporter.
//
// localEndpoint sets the local endpoint of exported spans.  It can be
// constructed with github.com/openzipkin/zipkin-go.NewEndpoint, e.g.:
// 	localEndpoint, err := NewEndpoint("my server", listener.Addr().String())
// localEndpoint can be nil.
func NewExporter(reporter reporter.Reporter, localEndpoint *model.Endpoint) *Exporter {
	return &Exporter{
		reporter:      reporter,
		localEndpoint: localEndpoint,
	}
}

// Export exports a span to a Zipkin server.
func (e *Exporter) Export(s *trace.SpanData) {
	e.reporter.Send(zipkinSpan(s, e.localEndpoint))
}

const (
	statusCodeTagKey        = "error"
	statusDescriptionTagKey = "opencensus.status_description"
)

var (
	sampledTrue    = true
	canonicalCodes = [...]string{
		"OK",
		"CANCELLED",
		"UNKNOWN",
		"INVALID_ARGUMENT",
		"DEADLINE_EXCEEDED",
		"NOT_FOUND",
		"ALREADY_EXISTS",
		"PERMISSION_DENIED",
		"RESOURCE_EXHAUSTED",
		"FAILED_PRECONDITION",
		"ABORTED",
		"OUT_OF_RANGE",
		"UNIMPLEMENTED",
		"INTERNAL",
		"UNAVAILABLE",
		"DATA_LOSS",
		"UNAUTHENTICATED",
	}
)

func canonicalCodeString(code int32) string {
	if code < 0 || int(code) >= len(canonicalCodes) {
		return "error code " + strconv.FormatInt(int64(code), 10)
	}
	return canonicalCodes[code]
}

func convertTraceID(t trace.TraceID) model.TraceID {
	return model.TraceID{
		High: binary.BigEndian.Uint64(t[:8]),
		Low:  binary.BigEndian.Uint64(t[8:]),
	}
}

func convertSpanID(s trace.SpanID) model.ID {
	return model.ID(binary.BigEndian.Uint64(s[:]))
}

func spanKind(s *trace.SpanData) model.Kind {
	if s.HasRemoteParent {
		return model.Server
	}
	if strings.HasPrefix(s.Name, "Sent.") {
		return model.Client
	}
	if strings.HasPrefix(s.Name, "Recv.") {
		return model.Server
	}
	if len(s.MessageEvents) > 0 {
		switch s.MessageEvents[0].EventType {
		case trace.MessageEventTypeSent:
			return model.Client
		case trace.MessageEventTypeRecv:
			return model.Server
		}
	}
	return model.Undetermined
}

func zipkinSpan(s *trace.SpanData, localEndpoint *model.Endpoint) model.SpanModel {
	sc := s.SpanContext
	z := model.SpanModel{
		SpanContext: model.SpanContext{
			TraceID: convertTraceID(sc.TraceID),
			ID:      convertSpanID(sc.SpanID),
			Sampled: &sampledTrue,
		},
		Kind:          spanKind(s),
		Name:          s.Name,
		Timestamp:     s.StartTime,
		Shared:        false,
		LocalEndpoint: localEndpoint,
	}

	if s.ParentSpanID != (trace.SpanID{}) {
		id := convertSpanID(s.ParentSpanID)
		z.ParentID = &id
	}

	if s, e := s.StartTime, s.EndTime; !s.IsZero() && !e.IsZero() {
		z.Duration = e.Sub(s)
	}

	// construct Tags from s.Attributes and s.Status.
	if len(s.Attributes) != 0 {
		m := make(map[string]string, len(s.Attributes)+2)
		for key, value := range s.Attributes {
			switch v := value.(type) {
			case string:
				m[key] = v
			case bool:
				if v {
					m[key] = "true"
				} else {
					m[key] = "false"
				}
			case int64:
				m[key] = strconv.FormatInt(v, 10)
			}
		}
		z.Tags = m
	}
	if s.Status.Code != 0 || s.Status.Message != "" {
		if z.Tags == nil {
			z.Tags = make(map[string]string, 2)
		}
		if s.Status.Code != 0 {
			z.Tags[statusCodeTagKey] = canonicalCodeString(s.Status.Code)
		}
		if s.Status.Message != "" {
			z.Tags[statusDescriptionTagKey] = s.Status.Message
		}
	}

	// construct Annotations from s.Annotations and s.MessageEvents.
	if len(s.Annotations) != 0 || len(s.MessageEvents) != 0 {
		z.Annotations = make([]model.Annotation, 0, len(s.Annotations)+len(s.MessageEvents))
		for _, a := range s.Annotations {
			z.Annotations = append(z.Annotations, model.Annotation{
				Timestamp: a.Time,
				Value:     a.Message,
			})
		}
		for _, m := range s.MessageEvents {
			a := model.Annotation{
				Timestamp: m.Time,
			}
			switch m.EventType {
			case trace.MessageEventTypeSent:
				a.Value = "SENT"
			case trace.MessageEventTypeRecv:
				a.Value = "RECV"
			default:
				a.Value = "<?>"
			}
			z.Annotations = append(z.Annotations, a)
		}
	}

	return z
}
<commit_msg>Fix zipkin example (#274)<commit_after>// Copyright 2017, OpenCensus 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 zipkin contains an exporter for Zipkin.
//
// Example:
//
// 	import (
// 		openzipkin "github.com/openzipkin/zipkin-go"
// 		"github.com/openzipkin/zipkin-go/reporter/http"
// 		"go.opencensus.io/exporter/trace/zipkin"
// 	)
//	...
//		localEndpoint, err := openzipkin.NewEndpoint("server", "server:5454")
// 		if err != nil {
// 			log.Print(err)
// 		}
// 		reporter := http.NewReporter("http://localhost:9411/api/v2/spans")
// 		exporter := zipkin.NewExporter(reporter, localEndpoint)
// 		trace.RegisterExporter(exporter)
package zipkin

import (
	"encoding/binary"
	"strconv"
	"strings"

	"github.com/openzipkin/zipkin-go/model"
	"github.com/openzipkin/zipkin-go/reporter"
	"go.opencensus.io/trace"
)

// Exporter is an implementation of trace.Exporter that uploads spans to a
// Zipkin server.
type Exporter struct {
	reporter      reporter.Reporter
	localEndpoint *model.Endpoint
}

// NewExporter returns an implementation of trace.Exporter that uploads spans
// to a Zipkin server.
//
// reporter is a Zipkin Reporter which will be used to send the spans.  These
// can be created with the openzipkin library, using one of the packages under
// github.com/openzipkin/zipkin-go/reporter.
//
// localEndpoint sets the local endpoint of exported spans.  It can be
// constructed with github.com/openzipkin/zipkin-go.NewEndpoint, e.g.:
// 	localEndpoint, err := NewEndpoint("my server", listener.Addr().String())
// localEndpoint can be nil.
func NewExporter(reporter reporter.Reporter, localEndpoint *model.Endpoint) *Exporter {
	return &Exporter{
		reporter:      reporter,
		localEndpoint: localEndpoint,
	}
}

// Export exports a span to a Zipkin server.
func (e *Exporter) Export(s *trace.SpanData) {
	e.reporter.Send(zipkinSpan(s, e.localEndpoint))
}

const (
	statusCodeTagKey        = "error"
	statusDescriptionTagKey = "opencensus.status_description"
)

var (
	sampledTrue    = true
	canonicalCodes = [...]string{
		"OK",
		"CANCELLED",
		"UNKNOWN",
		"INVALID_ARGUMENT",
		"DEADLINE_EXCEEDED",
		"NOT_FOUND",
		"ALREADY_EXISTS",
		"PERMISSION_DENIED",
		"RESOURCE_EXHAUSTED",
		"FAILED_PRECONDITION",
		"ABORTED",
		"OUT_OF_RANGE",
		"UNIMPLEMENTED",
		"INTERNAL",
		"UNAVAILABLE",
		"DATA_LOSS",
		"UNAUTHENTICATED",
	}
)

func canonicalCodeString(code int32) string {
	if code < 0 || int(code) >= len(canonicalCodes) {
		return "error code " + strconv.FormatInt(int64(code), 10)
	}
	return canonicalCodes[code]
}

func convertTraceID(t trace.TraceID) model.TraceID {
	return model.TraceID{
		High: binary.BigEndian.Uint64(t[:8]),
		Low:  binary.BigEndian.Uint64(t[8:]),
	}
}

func convertSpanID(s trace.SpanID) model.ID {
	return model.ID(binary.BigEndian.Uint64(s[:]))
}

func spanKind(s *trace.SpanData) model.Kind {
	if s.HasRemoteParent {
		return model.Server
	}
	if strings.HasPrefix(s.Name, "Sent.") {
		return model.Client
	}
	if strings.HasPrefix(s.Name, "Recv.") {
		return model.Server
	}
	if len(s.MessageEvents) > 0 {
		switch s.MessageEvents[0].EventType {
		case trace.MessageEventTypeSent:
			return model.Client
		case trace.MessageEventTypeRecv:
			return model.Server
		}
	}
	return model.Undetermined
}

func zipkinSpan(s *trace.SpanData, localEndpoint *model.Endpoint) model.SpanModel {
	sc := s.SpanContext
	z := model.SpanModel{
		SpanContext: model.SpanContext{
			TraceID: convertTraceID(sc.TraceID),
			ID:      convertSpanID(sc.SpanID),
			Sampled: &sampledTrue,
		},
		Kind:          spanKind(s),
		Name:          s.Name,
		Timestamp:     s.StartTime,
		Shared:        false,
		LocalEndpoint: localEndpoint,
	}

	if s.ParentSpanID != (trace.SpanID{}) {
		id := convertSpanID(s.ParentSpanID)
		z.ParentID = &id
	}

	if s, e := s.StartTime, s.EndTime; !s.IsZero() && !e.IsZero() {
		z.Duration = e.Sub(s)
	}

	// construct Tags from s.Attributes and s.Status.
	if len(s.Attributes) != 0 {
		m := make(map[string]string, len(s.Attributes)+2)
		for key, value := range s.Attributes {
			switch v := value.(type) {
			case string:
				m[key] = v
			case bool:
				if v {
					m[key] = "true"
				} else {
					m[key] = "false"
				}
			case int64:
				m[key] = strconv.FormatInt(v, 10)
			}
		}
		z.Tags = m
	}
	if s.Status.Code != 0 || s.Status.Message != "" {
		if z.Tags == nil {
			z.Tags = make(map[string]string, 2)
		}
		if s.Status.Code != 0 {
			z.Tags[statusCodeTagKey] = canonicalCodeString(s.Status.Code)
		}
		if s.Status.Message != "" {
			z.Tags[statusDescriptionTagKey] = s.Status.Message
		}
	}

	// construct Annotations from s.Annotations and s.MessageEvents.
	if len(s.Annotations) != 0 || len(s.MessageEvents) != 0 {
		z.Annotations = make([]model.Annotation, 0, len(s.Annotations)+len(s.MessageEvents))
		for _, a := range s.Annotations {
			z.Annotations = append(z.Annotations, model.Annotation{
				Timestamp: a.Time,
				Value:     a.Message,
			})
		}
		for _, m := range s.MessageEvents {
			a := model.Annotation{
				Timestamp: m.Time,
			}
			switch m.EventType {
			case trace.MessageEventTypeSent:
				a.Value = "SENT"
			case trace.MessageEventTypeRecv:
				a.Value = "RECV"
			default:
				a.Value = "<?>"
			}
			z.Annotations = append(z.Annotations, a)
		}
	}

	return z
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
)

func main() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<commit_msg>Use log.Fatalf<commit_after>package main

import (
	"log"
)

func main() {
	if err := RootCmd.Execute(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before><commit_msg>remove sha1 field<commit_after><|endoftext|>


<commit_before>package api

import (
	"bytes"
	"database/sql"
	"image/png"
	"io"
	"log"
	"net/http"
	"strconv"

	"github.com/mdlayher/wavepipe/data"

	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/mdlayher/waveform"
	"github.com/unrolled/render"
)

// waveformCache stores encoded waveform images in-memory, for re-use
// through multiple HTTP calls
var waveformCache = map[int][]byte{}

// GetWaveform generates and returns a waveform image from wavepipe.  On success, this API will
// return a binary stream. On failure, it will return a JSON error.
func GetWaveform(w http.ResponseWriter, r *http.Request) {
	// Retrieve render
	ren := context.Get(r, CtxRender).(*render.Render)

	// Check API version
	if version, ok := mux.Vars(r)["version"]; ok {
		// Check if this API call is supported in the advertised version
		if !apiVersionSet.Has(version) {
			ren.JSON(w, 400, errRes(400, "unsupported API version: "+version))
			return
		}
	}

	// Check for an ID parameter
	pID, ok := mux.Vars(r)["id"]
	if !ok {
		ren.JSON(w, 400, errRes(400, "no integer song ID provided"))
		return
	}

	// Verify valid integer ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		ren.JSON(w, 400, errRes(400, "invalid integer song ID"))
		return
	}

	// Attempt to load the song with matching ID
	song := &data.Song{ID: id}
	if err := song.Load(); err != nil {
		// Check for invalid ID
		if err == sql.ErrNoRows {
			ren.JSON(w, 404, errRes(404, "song ID not found"))
			return
		}

		// All other errors
		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Check for a cached waveform
	if _, ok := waveformCache[id]; ok {
		// Send cached data to HTTP writer
		if _, err := io.Copy(w, bytes.NewReader(waveformCache[id])); err != nil {
			log.Println(err)
		}

		return
	}

	// Open song's backing stream
	stream, err := song.Stream()
	if err != nil {
		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Generate a waveform from this song
	img, err := waveform.New(stream, &waveform.Options{
		ScaleX:     2,
		ScaleY:     2,
		Resolution: 2,
		ScaleRMS:   true,
		Sharpness:  1,
	})
	if err != nil {
		// If unknown format, return JSON error
		if err == waveform.ErrFormat {
			ren.JSON(w, 501, errRes(501, "unsupported audio format"))
			return
		}

		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Encode as PNG into buffer
	buf := bytes.NewBuffer(nil)
	if err := png.Encode(buf, img); err != nil {
		log.Println(err)
	}

	// Store cached image
	waveformCache[id] = buf.Bytes()

	// Send over HTTP
	if _, err := io.Copy(w, buf); err != nil {
		log.Println(err)
	}
}
<commit_msg>api/waveform: limit waveform cache to 10 entries<commit_after>package api

import (
	"bytes"
	"database/sql"
	"image/png"
	"io"
	"log"
	"net/http"
	"strconv"

	"github.com/mdlayher/wavepipe/data"

	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/mdlayher/waveform"
	"github.com/unrolled/render"
)

// waveformCache stores encoded waveform images in-memory, for re-use
// through multiple HTTP calls
var waveformCache = map[int][]byte{}

// waveformList tracks insertion order for cached waveforms, and enables the removal
// of the oldest waveform once a threshold is reached
var waveformList = []int{}

// GetWaveform generates and returns a waveform image from wavepipe.  On success, this API will
// return a binary stream. On failure, it will return a JSON error.
func GetWaveform(w http.ResponseWriter, r *http.Request) {
	// Retrieve render
	ren := context.Get(r, CtxRender).(*render.Render)

	// Check API version
	if version, ok := mux.Vars(r)["version"]; ok {
		// Check if this API call is supported in the advertised version
		if !apiVersionSet.Has(version) {
			ren.JSON(w, 400, errRes(400, "unsupported API version: "+version))
			return
		}
	}

	// Check for an ID parameter
	pID, ok := mux.Vars(r)["id"]
	if !ok {
		ren.JSON(w, 400, errRes(400, "no integer song ID provided"))
		return
	}

	// Verify valid integer ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		ren.JSON(w, 400, errRes(400, "invalid integer song ID"))
		return
	}

	// Attempt to load the song with matching ID
	song := &data.Song{ID: id}
	if err := song.Load(); err != nil {
		// Check for invalid ID
		if err == sql.ErrNoRows {
			ren.JSON(w, 404, errRes(404, "song ID not found"))
			return
		}

		// All other errors
		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Check for a cached waveform
	if _, ok := waveformCache[id]; ok {
		// Send cached data to HTTP writer
		if _, err := io.Copy(w, bytes.NewReader(waveformCache[id])); err != nil {
			log.Println(err)
		}

		return
	}

	// Open song's backing stream
	stream, err := song.Stream()
	if err != nil {
		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Generate a waveform from this song
	img, err := waveform.New(stream, &waveform.Options{
		ScaleX:     2,
		ScaleY:     2,
		Resolution: 2,
		ScaleRMS:   true,
		Sharpness:  1,
	})
	if err != nil {
		// If unknown format, return JSON error
		if err == waveform.ErrFormat {
			ren.JSON(w, 501, errRes(501, "unsupported audio format"))
			return
		}

		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Encode as PNG into buffer
	buf := bytes.NewBuffer(nil)
	if err := png.Encode(buf, img); err != nil {
		log.Println(err)
	}

	// Store cached image, append to cache list
	waveformCache[id] = buf.Bytes()
	waveformList = append(waveformList, id)

	// If threshold reached, remove oldest waveform from cache
	if len(waveformList) > 2 {
		oldest := waveformList[0]
		waveformList = waveformList[1:]
		delete(waveformCache, oldest)
	}

	// Send over HTTP
	if _, err := io.Copy(w, buf); err != nil {
		log.Println(err)
	}
}
<|endoftext|>


<commit_before><commit_msg>getAllCategoriesExcept works<commit_after><|endoftext|>


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

package video

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/paths"
	"github.com/andreaskoch/allmark2/model"
	"github.com/andreaskoch/allmark2/services/converter/markdowntohtml/pattern"
	"github.com/andreaskoch/allmark2/services/converter/markdowntohtml/util"
	"mime"
	"regexp"
	"strings"
)

var (
	// video: [*description text*](*a link to a youtube video or to a video file*)
	markdownPattern = regexp.MustCompile(`video: \[([^\]]+)\]\(([^)]+)\)`)

	// youtube video link pattern
	youTubeVideoPattern = regexp.MustCompile(`http[s]?://www\.youtube\.com/watch\?v=([^&]+)`)

	// vimeo video link pattern
	vimeoVideoPattern = regexp.MustCompile(`http[s]?://vimeo\.com/([\d]+)`)
)

func New(pathProvider paths.Pather, files []*model.File) *VideoExtension {
	return &VideoExtension{
		pathProvider: pathProvider,
		files:        files,
	}
}

type VideoExtension struct {
	pathProvider paths.Pather
	files        []*model.File
}

func (converter *VideoExtension) Convert(markdown string) (convertedContent string, converterError error) {

	convertedContent = markdown

	for {

		found, matches := pattern.IsMatch(convertedContent, markdownPattern)
		if !found || (found && len(matches) != 3) {
			break
		}

		// parameters
		originalText := strings.TrimSpace(matches[0])
		title := strings.TrimSpace(matches[1])
		path := strings.TrimSpace(matches[2])

		// get the code
		renderedCode := converter.getVideoCode(title, path)

		// replace markdown
		convertedContent = strings.Replace(convertedContent, originalText, renderedCode, 1)

	}

	return convertedContent, nil
}

func (converter *VideoExtension) getMatchingFile(path string) *model.File {
	for _, file := range converter.files {
		if file.Route().IsMatch(path) && util.IsVideoFile(file) {
			return file
		}
	}

	return nil
}

func (converter *VideoExtension) getVideoCode(title, path string) string {

	fallback := util.GetHtmlLinkCode(title, path)

	// internal video file
	if util.IsInternalLink(path) {

		if videoFile := converter.getMatchingFile(path); videoFile != nil {

			if mimeType, err := util.GetMimeType(videoFile); err == nil {
				filepath := converter.pathProvider.Path(videoFile.Route().Value())
				return renderVideoFileLink(title, filepath, mimeType)
			}

		}

	} else {

		// external: youtube
		if isYouTube, videoId := isYouTubeLink(path); isYouTube {
			return renderYouTubeVideo(title, videoId)
		}

		// external: vimeo
		if isVimeo, videoId := isVimeoLink(path); isVimeo {
			return renderVimeoVideo(title, videoId)
		}

		// external: html5 video file
		if isVideoFile, mimeType := isVideoFileLink(path); isVideoFile {
			return renderVideoFileLink(title, path, mimeType)
		}

	}

	// return the fallback handler
	return fallback
}

func isYouTubeLink(link string) (isYouTubeLink bool, videoId string) {
	if found, matches := pattern.IsMatch(link, youTubeVideoPattern); found && len(matches) == 2 {
		return true, matches[1]
	}

	return false, ""
}

func renderYouTubeVideo(title, videoId string) string {
	return fmt.Sprintf(`

		%s

`, videoId, title, title, videoId)
}

func isVimeoLink(link string) (isVimeoLink bool, videoId string) {
	if found, matches := pattern.IsMatch(link, vimeoVideoPattern); found && len(matches) == 2 {
		return true, matches[1]
	}

	return false, ""
}

func renderVimeoVideo(title, videoId string) string {
	return fmt.Sprintf(`

		%s

`, videoId, title, title, videoId)
}

func isVideoFileLink(link string) (isVideoFile bool, mimeType string) {

	// abort if the link does not contain a dot
	if !strings.Contains(link, ".") {
		return false, ""
	}

	normalizedLink := strings.ToLower(link)
	fileExtension := normalizedLink[strings.LastIndex(normalizedLink, "."):]
	mimeType = mime.TypeByExtension(fileExtension)

	switch fileExtension {
	case ".mp4", ".ogg", ".ogv", ".webm", ".3gp":
		return true, mimeType
	default:
		return false, ""
	}

	panic("Unreachable")
}

func renderVideoFileLink(title, link, mimetype string) string {
	return fmt.Sprintf(`

		%s
		<video width="560" height="315" controls>
			<source src="%s" type="%s">
		</video>

`, link, title, title, link, mimetype)
}
<commit_msg>Always embed youtube and video links via https<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package video

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/paths"
	"github.com/andreaskoch/allmark2/model"
	"github.com/andreaskoch/allmark2/services/converter/markdowntohtml/pattern"
	"github.com/andreaskoch/allmark2/services/converter/markdowntohtml/util"
	"mime"
	"regexp"
	"strings"
)

var (
	// video: [*description text*](*a link to a youtube video or to a video file*)
	markdownPattern = regexp.MustCompile(`video: \[([^\]]+)\]\(([^)]+)\)`)

	// youtube video link pattern
	youTubeVideoPattern = regexp.MustCompile(`http[s]?://www\.youtube\.com/watch\?v=([^&]+)`)

	// vimeo video link pattern
	vimeoVideoPattern = regexp.MustCompile(`http[s]?://vimeo\.com/([\d]+)`)
)

func New(pathProvider paths.Pather, files []*model.File) *VideoExtension {
	return &VideoExtension{
		pathProvider: pathProvider,
		files:        files,
	}
}

type VideoExtension struct {
	pathProvider paths.Pather
	files        []*model.File
}

func (converter *VideoExtension) Convert(markdown string) (convertedContent string, converterError error) {

	convertedContent = markdown

	for {

		found, matches := pattern.IsMatch(convertedContent, markdownPattern)
		if !found || (found && len(matches) != 3) {
			break
		}

		// parameters
		originalText := strings.TrimSpace(matches[0])
		title := strings.TrimSpace(matches[1])
		path := strings.TrimSpace(matches[2])

		// get the code
		renderedCode := converter.getVideoCode(title, path)

		// replace markdown
		convertedContent = strings.Replace(convertedContent, originalText, renderedCode, 1)

	}

	return convertedContent, nil
}

func (converter *VideoExtension) getMatchingFile(path string) *model.File {
	for _, file := range converter.files {
		if file.Route().IsMatch(path) && util.IsVideoFile(file) {
			return file
		}
	}

	return nil
}

func (converter *VideoExtension) getVideoCode(title, path string) string {

	fallback := util.GetHtmlLinkCode(title, path)

	// internal video file
	if util.IsInternalLink(path) {

		if videoFile := converter.getMatchingFile(path); videoFile != nil {

			if mimeType, err := util.GetMimeType(videoFile); err == nil {
				filepath := converter.pathProvider.Path(videoFile.Route().Value())
				return renderVideoFileLink(title, filepath, mimeType)
			}

		}

	} else {

		// external: youtube
		if isYouTube, videoId := isYouTubeLink(path); isYouTube {
			return renderYouTubeVideo(title, videoId)
		}

		// external: vimeo
		if isVimeo, videoId := isVimeoLink(path); isVimeo {
			return renderVimeoVideo(title, videoId)
		}

		// external: html5 video file
		if isVideoFile, mimeType := isVideoFileLink(path); isVideoFile {
			return renderVideoFileLink(title, path, mimeType)
		}

	}

	// return the fallback handler
	return fallback
}

func isYouTubeLink(link string) (isYouTubeLink bool, videoId string) {
	if found, matches := pattern.IsMatch(link, youTubeVideoPattern); found && len(matches) == 2 {
		return true, matches[1]
	}

	return false, ""
}

func renderYouTubeVideo(title, videoId string) string {
	return fmt.Sprintf(`

		%s

`, videoId, title, title, videoId)
}

func isVimeoLink(link string) (isVimeoLink bool, videoId string) {
	if found, matches := pattern.IsMatch(link, vimeoVideoPattern); found && len(matches) == 2 {
		return true, matches[1]
	}

	return false, ""
}

func renderVimeoVideo(title, videoId string) string {
	return fmt.Sprintf(`

		%s

`, videoId, title, title, videoId)
}

func isVideoFileLink(link string) (isVideoFile bool, mimeType string) {

	// abort if the link does not contain a dot
	if !strings.Contains(link, ".") {
		return false, ""
	}

	normalizedLink := strings.ToLower(link)
	fileExtension := normalizedLink[strings.LastIndex(normalizedLink, "."):]
	mimeType = mime.TypeByExtension(fileExtension)

	switch fileExtension {
	case ".mp4", ".ogg", ".ogv", ".webm", ".3gp":
		return true, mimeType
	default:
		return false, ""
	}

	panic("Unreachable")
}

func renderVideoFileLink(title, link, mimetype string) string {
	return fmt.Sprintf(`

		%s
		<video width="560" height="315" controls>
			<source src="%s" type="%s">
		</video>

`, link, title, title, link, mimetype)
}
<|endoftext|>


<commit_before>package main

import "fmt"
func fib(n int)  int {
    if n == 0 {
        return 0
    }
    if n == 1 {
        return 1
    }
    return fib(n-1)+fib(n-2)
}
func main() {
    fmt.Print("Введите число: ")
    var n int
    fmt.Scanf("%d", &n)
    fmt.Println(fib(n))
    }
<commit_msg>телефонная книга<commit_after>package main

import "fmt"

func main() {
    i:= 0
    fmt.Print(
      "1. Показать книгу"
      "2. Добавить контакт"
      "3.Удалить контакт"
      "4. Найти контакт"
      )
    fmt.Scanf("%i",&i)
    switch i{
      case 1:
    }

    var name string
    fmt.Print("Введите имя: ")
    fmt.Scanf("%s", &name)
    var number string
    fmt.Print("Введите телефон: ")
    fmt.Scanf("%s", &number)
    entry := map[string]string{
        name: number,
    }

    for i, v:= range entry{
        fmt.Println( i,v)
    }

}<|endoftext|>


<commit_before>package maven

import (
	"fmt"
	"io/ioutil"
	"os/exec"
)

func Clean(localCache, projectDirectory string) ([]byte, error) {
	// fmt.Printf("in clean function.\n") // debug

	// run the clean command for the specified
	// project directory
	cleanCommand := exec.Command(
		"mvn",
		"clean",
		"-f",
		projectDirectory,
	)

	// add localcache option flag
	if localCache != "" {
		mavenOpts := fmt.Sprintf(
			"-Dmaven.repo.local=%s", localCache)
		// fmt.Printf(mavenOpts) // debug
		cleanCommand = exec.Command(
			"mvn",
			"clean",
			"-f",
			projectDirectory,
			mavenOpts,
		)
	}
	output, err := cleanCommand.StdoutPipe()
	if err != nil {
		return nil, err
	}

	err = cleanCommand.Start()
	if err != nil {
		return nil, err
	}
	outputBytes, err := ioutil.ReadAll(output)
	if err != nil {
		fmt.Printf("err:\n%v\n", err)
		return nil, err
	}

	cleanCommand.Wait()

	return outputBytes, nil
}
<commit_msg>remove clean command.<commit_after><|endoftext|>


<commit_before>package mcmap

import (
	"errors"
	"github.com/kch42/gonbt/nbt"
	"time"
)

func calcBlockOffset(x, y, z int) int {
	if (x < 0) || (y < 0) || (z < 0) || (x >= ChunkSizeXZ) || (y >= ChunkSizeY) || (z >= ChunkSizeXZ) {
		return -1
	}

	return x | (z << 4) | (y << 8)
}

func offsetToPos(off int) (x, y, z int) {
	x = off & 0xf
	z = (off >> 4) & 0xf
	y = (off >> 8) & 0xff
	return
}

// BlockToChunk calculates the chunk (cx, cz) and the block position in this chunk(rbx, rbz) of a block position given global coordinates.
func BlockToChunk(bx, bz int) (cx, cz, rbx, rbz int) {
	cx = bx << 4
	cz = bz << 4
	rbx = ((cx % ChunkSizeXZ) + ChunkSizeXZ) % ChunkSizeXZ
	rbz = ((cz % ChunkSizeXZ) + ChunkSizeXZ) % ChunkSizeXZ
	return
}

// ChunkToBlock calculates the global position of a block, given the chunk position (cx, cz) and the plock position in that chunk (rbx, rbz).
func ChunkToBlock(cx, cz, rbx, rbz int) (bx, bz int) {
	bx = cx*ChunkSizeXZ + rbx
	bz = cz*ChunkSizeXZ + rbz
	return
}

// Chunk represents a 16*16*256 Chunk of the region.
type Chunk struct {
	Entities []nbt.TagCompound

	x, z int32

	lastUpdate    int64
	populated     bool
	inhabitedTime int64
	ts            time.Time

	heightMap []int32 // Ordered ZX

	modified bool
	blocks   []Block // Ordered YZX
	biomes   []Biome // Ordered ZX

	reg *Region
}

// MarkModified needs to be called, if some data of the chunk was modified.
func (c *Chunk) MarkModified() { c.modified = true }

// Coords returns the Chunk's coordinates.
func (c *Chunk) Coords() (X, Z int32) { return c.x, c.z }

// Block gives you a reference to the Block located at x, y, z. If you modify the block data, you need to call the MarkModified() function of the chunk.
//
// x and z must be in [0, 15], y in [0, 255]. Otherwise a nil pointer is returned.
func (c *Chunk) Block(x, y, z int) *Block {
	off := calcBlockOffset(x, y, z)
	if off < 0 {
		return nil
	}

	return &(c.blocks[off])
}

// Height returns the height at x, z.
//
// x and z must be in [0, 15]. Height will panic, if this is violated!
func (c *Chunk) Height(x, z int) int {
	if (x < 0) || (x >= ChunkSizeXZ) || (z < 0) || (z >= ChunkSizeXZ) {
		panic(errors.New("x or z parameter was out of range"))
	}

	return int(c.heightMap[z*ChunkSizeXZ+x])
}

// Iter iterates ofer all blocks of this chunk and calls the function fx with the coords (x,y,z) and a pointer to the block.
func (c *Chunk) Iter(fx func(int, int, int, *Block)) {
	for x := 0; x < ChunkSizeXZ; x++ {
		for y := 0; y < ChunkSizeY; y++ {
			for z := 0; z < ChunkSizeXZ; z++ {
				fx(x, y, z, &(c.blocks[calcBlockOffset(x, y, z)]))
			}
		}
	}
}

// Biome gets the Biome at x,z.
func (c *Chunk) Biome(x, z int) Biome { return c.biomes[z*ChunkSizeXZ+x] }

// SetBiome sets the biome at x,z.
func (c *Chunk) SetBiome(x, z int, bio Biome) { c.biomes[z*ChunkSizeXZ+x] = bio }

// MarkUnused marks the chunk as unused. If all chunks of a superchunk are marked as unused, the superchunk will be unloaded and saved (if needed).
//
// You must not use the chunk any longer, after you called this function.
//
// If the chunk was modified, call MarkModified BEFORE.
func (c *Chunk) MarkUnused() error { return c.reg.unloadChunk(int(c.x), int(c.z)) }

// RecalcHeightMap recalculates the internal height map.
//
// You should use this function before marking the chunk as unused, if you modified the chunk
// (unless you know, your changes wouldn't affect the height map).
func (c *Chunk) RecalcHeightMap() {
	i := 0
	for z := 0; z < ChunkSizeXZ; z++ {
		for x := 0; x < ChunkSizeXZ; x++ {
			for y := ChunkSizeY; y >= 0; y-- {
				blkid := c.blocks[calcBlockOffset(x, y, z)].ID
				if (blkid != BlkAir) && (blkid != BlkGlass) && (blkid != BlkGlassPane) {
					c.heightMap[i] = int32(y)
					break
				}
			}
			i++
		}
	}
}
<commit_msg>Fixed BlockToChunk<commit_after>package mcmap

import (
	"errors"
	"github.com/kch42/gonbt/nbt"
	"time"
)

func calcBlockOffset(x, y, z int) int {
	if (x < 0) || (y < 0) || (z < 0) || (x >= ChunkSizeXZ) || (y >= ChunkSizeY) || (z >= ChunkSizeXZ) {
		return -1
	}

	return x | (z << 4) | (y << 8)
}

func offsetToPos(off int) (x, y, z int) {
	x = off & 0xf
	z = (off >> 4) & 0xf
	y = (off >> 8) & 0xff
	return
}

// BlockToChunk calculates the chunk (cx, cz) and the block position in this chunk(rbx, rbz) of a block position given global coordinates.
func BlockToChunk(bx, bz int) (cx, cz, rbx, rbz int) {
	cx = bx >> 4
	cz = bz >> 4
	rbx = ((cx % ChunkSizeXZ) + ChunkSizeXZ) % ChunkSizeXZ
	rbz = ((cz % ChunkSizeXZ) + ChunkSizeXZ) % ChunkSizeXZ
	return
}

// ChunkToBlock calculates the global position of a block, given the chunk position (cx, cz) and the plock position in that chunk (rbx, rbz).
func ChunkToBlock(cx, cz, rbx, rbz int) (bx, bz int) {
	bx = cx*ChunkSizeXZ + rbx
	bz = cz*ChunkSizeXZ + rbz
	return
}

// Chunk represents a 16*16*256 Chunk of the region.
type Chunk struct {
	Entities []nbt.TagCompound

	x, z int32

	lastUpdate    int64
	populated     bool
	inhabitedTime int64
	ts            time.Time

	heightMap []int32 // Ordered ZX

	modified bool
	blocks   []Block // Ordered YZX
	biomes   []Biome // Ordered ZX

	reg *Region
}

// MarkModified needs to be called, if some data of the chunk was modified.
func (c *Chunk) MarkModified() { c.modified = true }

// Coords returns the Chunk's coordinates.
func (c *Chunk) Coords() (X, Z int32) { return c.x, c.z }

// Block gives you a reference to the Block located at x, y, z. If you modify the block data, you need to call the MarkModified() function of the chunk.
//
// x and z must be in [0, 15], y in [0, 255]. Otherwise a nil pointer is returned.
func (c *Chunk) Block(x, y, z int) *Block {
	off := calcBlockOffset(x, y, z)
	if off < 0 {
		return nil
	}

	return &(c.blocks[off])
}

// Height returns the height at x, z.
//
// x and z must be in [0, 15]. Height will panic, if this is violated!
func (c *Chunk) Height(x, z int) int {
	if (x < 0) || (x >= ChunkSizeXZ) || (z < 0) || (z >= ChunkSizeXZ) {
		panic(errors.New("x or z parameter was out of range"))
	}

	return int(c.heightMap[z*ChunkSizeXZ+x])
}

// Iter iterates ofer all blocks of this chunk and calls the function fx with the coords (x,y,z) and a pointer to the block.
func (c *Chunk) Iter(fx func(int, int, int, *Block)) {
	for x := 0; x < ChunkSizeXZ; x++ {
		for y := 0; y < ChunkSizeY; y++ {
			for z := 0; z < ChunkSizeXZ; z++ {
				fx(x, y, z, &(c.blocks[calcBlockOffset(x, y, z)]))
			}
		}
	}
}

// Biome gets the Biome at x,z.
func (c *Chunk) Biome(x, z int) Biome { return c.biomes[z*ChunkSizeXZ+x] }

// SetBiome sets the biome at x,z.
func (c *Chunk) SetBiome(x, z int, bio Biome) { c.biomes[z*ChunkSizeXZ+x] = bio }

// MarkUnused marks the chunk as unused. If all chunks of a superchunk are marked as unused, the superchunk will be unloaded and saved (if needed).
//
// You must not use the chunk any longer, after you called this function.
//
// If the chunk was modified, call MarkModified BEFORE.
func (c *Chunk) MarkUnused() error { return c.reg.unloadChunk(int(c.x), int(c.z)) }

// RecalcHeightMap recalculates the internal height map.
//
// You should use this function before marking the chunk as unused, if you modified the chunk
// (unless you know, your changes wouldn't affect the height map).
func (c *Chunk) RecalcHeightMap() {
	i := 0
	for z := 0; z < ChunkSizeXZ; z++ {
		for x := 0; x < ChunkSizeXZ; x++ {
			for y := ChunkSizeY; y >= 0; y-- {
				blkid := c.blocks[calcBlockOffset(x, y, z)].ID
				if (blkid != BlkAir) && (blkid != BlkGlass) && (blkid != BlkGlassPane) {
					c.heightMap[i] = int32(y)
					break
				}
			}
			i++
		}
	}
}
<|endoftext|>


<commit_before>package veneur

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/Sirupsen/logrus"
	"github.com/stripe/veneur/samplers"
	"github.com/stripe/veneur/trace"
)

type metricSink interface {
	Name() string
	Flush(context.Context, []samplers.InterMetric) error
	// This one is temporary?
	FlushEventsChecks(ctx context.Context, events []samplers.UDPEvent, checks []samplers.UDPServiceCheck)
}

type datadogMetricSink struct {
	HTTPClient      *http.Client
	ddHostname      string
	hostname        string
	apiKey          string
	flushMaxPerBody int
	statsd          *statsd.Client
	tags            []string
	interval        float64
}

// NewDatadogMetricSink creates a new Datadog sink for trace spans.
func NewDatadogMetricSink(config *Config, interval float64, httpClient *http.Client, stats *statsd.Client) (*datadogMetricSink, error) {
	return &datadogMetricSink{
		HTTPClient:      httpClient,
		statsd:          stats,
		interval:        interval,
		flushMaxPerBody: config.FlushMaxPerBody,
		ddHostname:      config.DatadogAPIHostname,
		apiKey:          config.DatadogAPIKey,
	}, nil
}

// Name returns the name of this sink.
func (dd *datadogMetricSink) Name() string {
	return "datadog"
}

func (dd *datadogMetricSink) Flush(ctx context.Context, interMetrics []samplers.InterMetric) error {
	span, _ := trace.StartSpanFromContext(ctx, "")
	defer span.Finish()

	metrics := dd.finalizeMetrics(interMetrics)

	// break the metrics into chunks of approximately equal size, such that
	// each chunk is less than the limit
	// we compute the chunks using rounding-up integer division
	workers := ((len(metrics) - 1) / dd.flushMaxPerBody) + 1
	chunkSize := ((len(metrics) - 1) / workers) + 1
	log.WithField("workers", workers).Debug("Worker count chosen")
	log.WithField("chunkSize", chunkSize).Debug("Chunk size chosen")
	var wg sync.WaitGroup
	flushStart := time.Now()
	for i := 0; i < workers; i++ {
		chunk := metrics[i*chunkSize:]
		if i < workers-1 {
			// trim to chunk size unless this is the last one
			chunk = chunk[:chunkSize]
		}
		wg.Add(1)
		go dd.flushPart(span.Attach(ctx), chunk, &wg)
	}
	wg.Wait()
	dd.statsd.TimeInMilliseconds("flush.total_duration_ns", float64(time.Since(flushStart).Nanoseconds()), []string{"part:post"}, 1.0)

	log.WithField("metrics", len(metrics)).Info("Completed flush to Datadog")
	return nil
}

func (dd *datadogMetricSink) FlushEventsChecks(ctx context.Context, events []samplers.UDPEvent, checks []samplers.UDPServiceCheck) {
	span, _ := trace.StartSpanFromContext(ctx, "")
	defer span.Finish()

	// fill in the default hostname for packets that didn't set it
	for i := range events {
		if events[i].Hostname == "" {
			events[i].Hostname = dd.hostname
		}
		events[i].Tags = append(events[i].Tags, dd.tags...)
	}
	for i := range checks {
		if checks[i].Hostname == "" {
			checks[i].Hostname = dd.hostname
		}
		checks[i].Tags = append(checks[i].Tags, dd.tags...)
	}

	if len(events) != 0 {
		// this endpoint is not documented at all, its existence is only known from
		// the official dd-agent
		// we don't actually pass all the body keys that dd-agent passes here... but
		// it still works
		err := postHelper(context.TODO(), dd.HTTPClient, dd.statsd, fmt.Sprintf("%s/intake?api_key=%s"), map[string]map[string][]samplers.UDPEvent{
			"events": {
				"api": events,
			},
		}, "flush_events", true)
		if err == nil {
			log.WithField("events", len(events)).Info("Completed flushing events to Datadog")
		} else {
			log.WithFields(logrus.Fields{
				"events":        len(events),
				logrus.ErrorKey: err}).Warn("Error flushing events to Datadog")
		}
	}

	if len(checks) != 0 {
		// this endpoint is not documented to take an array... but it does
		// another curious constraint of this endpoint is that it does not
		// support "Content-Encoding: deflate"
		err := postHelper(context.TODO(), dd.HTTPClient, dd.statsd, fmt.Sprintf("%s/api/v1/check_run?api_key=%s"), checks, "flush_checks", false)
		if err == nil {
			log.WithField("checks", len(checks)).Info("Completed flushing service checks to Datadog")
		} else {
			log.WithFields(logrus.Fields{
				"checks":        len(checks),
				logrus.ErrorKey: err}).Warn("Error flushing checks to Datadog")
		}
	}
}

func (dd *datadogMetricSink) finalizeMetrics(metrics []samplers.InterMetric) []samplers.DDMetric {
	ddMetrics := make([]samplers.DDMetric, len(metrics))
	for i, m := range metrics {
		// Defensively copy tags since we're gonna mutate it
		tags := make([]string, len(dd.tags))
		copy(tags, dd.tags)
		metricType := m.MetricType
		value := m.Value
		// We convert Datadog counters into rates
		if m.MetricType == "counter" {
			metricType = "rate"
			value = m.Value / dd.interval
		}
		ddMetric := samplers.DDMetric{
			Name: m.Name,
			Value: [1][2]float64{
				[2]float64{
					float64(m.Timestamp), value,
				},
			},
			Tags:       tags,
			MetricType: metricType,
			Interval:   int32(dd.interval),
		}

		// Let's look for "magic tags" that override metric fields host and device.
		for _, tag := range m.Tags {
			// This overrides hostname
			if strings.HasPrefix(tag, "host:") {
				// Override the hostname with the tag, trimming off the prefix.
				ddMetric.Hostname = tag[5:]
			} else if strings.HasPrefix(tag, "device:") {
				// Same as above, but device this time
				ddMetric.DeviceName = tag[7:]
			} else {
				// Add it, no reason to exclude it.
				ddMetric.Tags = append(ddMetric.Tags, tag)
			}
		}
		if ddMetric.Hostname == "" {
			// No magic tag, set the hostname
			ddMetric.Hostname = dd.hostname
		}
		ddMetrics[i] = ddMetric
	}

	return ddMetrics
}

func (dd *datadogMetricSink) flushPart(ctx context.Context, metricSlice []samplers.DDMetric, wg *sync.WaitGroup) {
	defer wg.Done()
	postHelper(ctx, dd.HTTPClient, dd.statsd, fmt.Sprintf("%s/api/v1/series?api_key=%s", dd.ddHostname, dd.apiKey), map[string][]samplers.DDMetric{
		"series": metricSlice,
	}, "flush", true)
}
<commit_msg>Fix small field name change difference.<commit_after>package veneur

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/DataDog/datadog-go/statsd"
	"github.com/Sirupsen/logrus"
	"github.com/stripe/veneur/samplers"
	"github.com/stripe/veneur/trace"
)

type metricSink interface {
	Name() string
	Flush(context.Context, []samplers.InterMetric) error
	// This one is temporary?
	FlushEventsChecks(ctx context.Context, events []samplers.UDPEvent, checks []samplers.UDPServiceCheck)
}

type datadogMetricSink struct {
	HTTPClient      *http.Client
	ddHostname      string
	hostname        string
	apiKey          string
	flushMaxPerBody int
	statsd          *statsd.Client
	tags            []string
	interval        float64
}

// NewDatadogMetricSink creates a new Datadog sink for trace spans.
func NewDatadogMetricSink(config *Config, interval float64, httpClient *http.Client, stats *statsd.Client) (*datadogMetricSink, error) {
	return &datadogMetricSink{
		HTTPClient:      httpClient,
		statsd:          stats,
		interval:        interval,
		flushMaxPerBody: config.FlushMaxPerBody,
		ddHostname:      config.DatadogAPIHostname,
		apiKey:          config.DatadogAPIKey,
	}, nil
}

// Name returns the name of this sink.
func (dd *datadogMetricSink) Name() string {
	return "datadog"
}

func (dd *datadogMetricSink) Flush(ctx context.Context, interMetrics []samplers.InterMetric) error {
	span, _ := trace.StartSpanFromContext(ctx, "")
	defer span.Finish()

	metrics := dd.finalizeMetrics(interMetrics)

	// break the metrics into chunks of approximately equal size, such that
	// each chunk is less than the limit
	// we compute the chunks using rounding-up integer division
	workers := ((len(metrics) - 1) / dd.flushMaxPerBody) + 1
	chunkSize := ((len(metrics) - 1) / workers) + 1
	log.WithField("workers", workers).Debug("Worker count chosen")
	log.WithField("chunkSize", chunkSize).Debug("Chunk size chosen")
	var wg sync.WaitGroup
	flushStart := time.Now()
	for i := 0; i < workers; i++ {
		chunk := metrics[i*chunkSize:]
		if i < workers-1 {
			// trim to chunk size unless this is the last one
			chunk = chunk[:chunkSize]
		}
		wg.Add(1)
		go dd.flushPart(span.Attach(ctx), chunk, &wg)
	}
	wg.Wait()
	dd.statsd.TimeInMilliseconds("flush.total_duration_ns", float64(time.Since(flushStart).Nanoseconds()), []string{"part:post"}, 1.0)

	log.WithField("metrics", len(metrics)).Info("Completed flush to Datadog")
	return nil
}

func (dd *datadogMetricSink) FlushEventsChecks(ctx context.Context, events []samplers.UDPEvent, checks []samplers.UDPServiceCheck) {
	span, _ := trace.StartSpanFromContext(ctx, "")
	defer span.Finish()

	// fill in the default hostname for packets that didn't set it
	for i := range events {
		if events[i].Hostname == "" {
			events[i].Hostname = dd.hostname
		}
		events[i].Tags = append(events[i].Tags, dd.tags...)
	}
	for i := range checks {
		if checks[i].Hostname == "" {
			checks[i].Hostname = dd.hostname
		}
		checks[i].Tags = append(checks[i].Tags, dd.tags...)
	}

	if len(events) != 0 {
		// this endpoint is not documented at all, its existence is only known from
		// the official dd-agent
		// we don't actually pass all the body keys that dd-agent passes here... but
		// it still works
		err := postHelper(context.TODO(), dd.HTTPClient, dd.statsd, fmt.Sprintf("%s/intake?api_key=%s"), map[string]map[string][]samplers.UDPEvent{
			"events": {
				"api": events,
			},
		}, "flush_events", true)
		if err == nil {
			log.WithField("events", len(events)).Info("Completed flushing events to Datadog")
		} else {
			log.WithFields(logrus.Fields{
				"events":        len(events),
				logrus.ErrorKey: err}).Warn("Error flushing events to Datadog")
		}
	}

	if len(checks) != 0 {
		// this endpoint is not documented to take an array... but it does
		// another curious constraint of this endpoint is that it does not
		// support "Content-Encoding: deflate"
		err := postHelper(context.TODO(), dd.HTTPClient, dd.statsd, fmt.Sprintf("%s/api/v1/check_run?api_key=%s"), checks, "flush_checks", false)
		if err == nil {
			log.WithField("checks", len(checks)).Info("Completed flushing service checks to Datadog")
		} else {
			log.WithFields(logrus.Fields{
				"checks":        len(checks),
				logrus.ErrorKey: err}).Warn("Error flushing checks to Datadog")
		}
	}
}

func (dd *datadogMetricSink) finalizeMetrics(metrics []samplers.InterMetric) []samplers.DDMetric {
	ddMetrics := make([]samplers.DDMetric, len(metrics))
	for i, m := range metrics {
		// Defensively copy tags since we're gonna mutate it
		tags := make([]string, len(dd.tags))
		copy(tags, dd.tags)
		metricType := m.Type.String()
		value := m.Value
		// We convert Datadog counters into rates
		if metricType == "counter" {
			metricType = "rate"
			value = m.Value / dd.interval
		}
		ddMetric := samplers.DDMetric{
			Name: m.Name,
			Value: [1][2]float64{
				[2]float64{
					float64(m.Timestamp), value,
				},
			},
			Tags:       tags,
			MetricType: metricType,
			Interval:   int32(dd.interval),
		}

		// Let's look for "magic tags" that override metric fields host and device.
		for _, tag := range m.Tags {
			// This overrides hostname
			if strings.HasPrefix(tag, "host:") {
				// Override the hostname with the tag, trimming off the prefix.
				ddMetric.Hostname = tag[5:]
			} else if strings.HasPrefix(tag, "device:") {
				// Same as above, but device this time
				ddMetric.DeviceName = tag[7:]
			} else {
				// Add it, no reason to exclude it.
				ddMetric.Tags = append(ddMetric.Tags, tag)
			}
		}
		if ddMetric.Hostname == "" {
			// No magic tag, set the hostname
			ddMetric.Hostname = dd.hostname
		}
		ddMetrics[i] = ddMetric
	}

	return ddMetrics
}

func (dd *datadogMetricSink) flushPart(ctx context.Context, metricSlice []samplers.DDMetric, wg *sync.WaitGroup) {
	defer wg.Done()
	postHelper(ctx, dd.HTTPClient, dd.statsd, fmt.Sprintf("%s/api/v1/series?api_key=%s", dd.ddHostname, dd.apiKey), map[string][]samplers.DDMetric{
		"series": metricSlice,
	}, "flush", true)
}
<|endoftext|>


<commit_before>package structs_test

import (
	"encoding/json"
	"fmt"
	"github.com/fatih/structs"
	"github.com/mitchellh/mapstructure"
	"github.com/stretchr/testify/assert"
	"os"
	"strings"
	"testing"
)

type Book struct {
	Title  string
	Author string
	Year   int
}

const jsonData string = `{
	"name": "Enrique Vila-Matas",
	"nationality": "Spain",
	"books": [{
		"title": "El mal de Montano",
		"year": 2002
	}, {
		"title": "Paris no se acaba nunca"
	}, {
		"title": "Doctor Pasavento",
		"year": 2005,
		"publisher": "Anagrama"
	}, {
		"title": "Dublinesca",
		"year": null
	}]
}`

func Test(t *testing.T) {
	assert.Nil(t, nil)

	book := Book{"El mal de Montano", "E. Vila-Matas", 2002}
	assert.Equal(t, "E. Vila-Matas", book.Author)

	bookUpdated1 := Book{}
	getUpdatedStruct(book, &bookUpdated1, `{"author": "Enrique Vila-Matas"}`)
	assert.Equal(t, "Enrique Vila-Matas", bookUpdated1.Author)

	bookUpdated2 := Book{}
	getUpdatedStruct(bookUpdated1, &bookUpdated2, `{"publisher": "Anagrama"}`)
	assert.Equal(t, bookUpdated2, bookUpdated1)

	bookUpdated3 := Book{}
	getUpdatedStruct(bookUpdated1, &bookUpdated3, `{"year": null}`)
	assert.Equal(t, bookUpdated3.Title, bookUpdated1.Title)
	assert.Equal(t, bookUpdated3.Author, bookUpdated1.Author)
	assert.Equal(t, bookUpdated3.Year, 0)

	bookUpdated4 := Book{}
	getUpdatedStruct(bookUpdated1, &bookUpdated4, `{"title": null}`)
	assert.Equal(t, bookUpdated4.Title, "")
	assert.Equal(t, bookUpdated4.Author, bookUpdated1.Author)
	assert.Equal(t, bookUpdated4.Year, bookUpdated1.Year)
}

func getUpdatedStruct(orig interface{}, dest interface{}, changeJson string) {
	origMap := structs.Map(orig)
	fmt.Println("---")
	json.NewEncoder(os.Stdout).Encode(origMap)

	var changeMap map[string]interface{}
	json.Unmarshal([]byte(changeJson), &changeMap)
	json.NewEncoder(os.Stdout).Encode(changeMap)

	destMap := make(map[string]interface{})
	for key, value := range origMap {
		key := strings.ToLower(key)
		destMap[key] = value
	}
	for key, value := range changeMap {
		key := strings.ToLower(key)
		destMap[key] = value
	}

	mapstructure.Decode(destMap, dest)
	json.NewEncoder(os.Stdout).Encode(dest)
}
<commit_msg>update<commit_after>package structs_test

import (
	"encoding/json"
	"fmt"
	"github.com/fatih/structs"
	"github.com/mitchellh/mapstructure"
	"github.com/stretchr/testify/assert"
	"os"
	"strings"
	"testing"
)

type Book struct {
	Title  string
	Author string
	Year   int
}

func Test(t *testing.T) {
	assert.Nil(t, nil)

	book := Book{"El mal de Montano", "E. Vila-Matas", 2002}
	assert.Equal(t, "E. Vila-Matas", book.Author)

	bookUpdated1 := Book{}
	getUpdatedStruct(book, &bookUpdated1, `{"author": "Enrique Vila-Matas"}`)
	assert.Equal(t, "Enrique Vila-Matas", bookUpdated1.Author)

	bookUpdated2 := Book{}
	getUpdatedStruct(bookUpdated1, &bookUpdated2, `{"publisher": "Anagrama"}`)
	assert.Equal(t, bookUpdated2, bookUpdated1)

	bookUpdated3 := Book{}
	getUpdatedStruct(bookUpdated1, &bookUpdated3, `{"year": null}`)
	assert.Equal(t, bookUpdated3.Title, bookUpdated1.Title)
	assert.Equal(t, bookUpdated3.Author, bookUpdated1.Author)
	assert.Equal(t, bookUpdated3.Year, 0)

	bookUpdated4 := Book{}
	getUpdatedStruct(bookUpdated1, &bookUpdated4, `{"title": null}`)
	assert.Equal(t, bookUpdated4.Title, "")
	assert.Equal(t, bookUpdated4.Author, bookUpdated1.Author)
	assert.Equal(t, bookUpdated4.Year, bookUpdated1.Year)
}

func getUpdatedStruct(orig interface{}, dest interface{}, changeJson string) {
	origMap := structs.Map(orig)
	fmt.Println("---")
	json.NewEncoder(os.Stdout).Encode(origMap)

	var changeMap map[string]interface{}
	json.Unmarshal([]byte(changeJson), &changeMap)
	json.NewEncoder(os.Stdout).Encode(changeMap)

	destMap := make(map[string]interface{})
	for key, value := range origMap {
		key := strings.ToLower(key)
		destMap[key] = value
	}
	for key, value := range changeMap {
		key := strings.ToLower(key)
		destMap[key] = value
	}

	mapstructure.Decode(destMap, dest)
	json.NewEncoder(os.Stdout).Encode(dest)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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 e2e_metric_only

import (
	"context"
	"flag"
	"fmt"
	"os"
	"path"
	"testing"
	"time"

	"k8s.io/node-problem-detector/pkg/util/tomb"
	"k8s.io/node-problem-detector/test/e2e/lib/gce"
	"k8s.io/test-infra/boskos/client"

	"github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/reporters"
	. "github.com/onsi/gomega"
	compute "google.golang.org/api/compute/v1"
)

var zone = flag.String("zone", "", "gce zone the hosts live in")
var project = flag.String("project", "", "gce project the hosts live in")
var image = flag.String("image", "", "image to test")
var imageFamily = flag.String("image-family", "", "image family to pick up the test image. Ignored when -image is set.")
var imageProject = flag.String("image-project", "", "gce project of the OS image")
var jobName = flag.String("job-name", "", "name of the Prow job running the test")
var sshKey = flag.String("ssh-key", "", "path to ssh private key.")
var sshUser = flag.String("ssh-user", "", "use predefined user for ssh.")
var npdBuildTar = flag.String("npd-build-tar", "", "tarball containing NPD to be tested.")
var artifactsDir = flag.String("artifacts-dir", "", "local directory to save test artifacts into.")
var boskosProjectType = flag.String("boskos-project-type", "gce-project",
	"specifies which project type to select from Boskos.")
var boskosServerURL = flag.String("boskos-server-url", "http://boskos.test-pods.svc.cluster.local",
	"specifies Boskos server URL.")
var boskosWaitDuration = flag.Duration("boskos-wait-duration", 2*time.Minute,
	"Duration to wait before quitting getting Boskos resource.")

var computeService *compute.Service

// boskosClient helps renting project from Boskos, and is only initialized on Ginkgo node 1.
var boskosClient *client.Client

// boskosRenewingTomb stops the goroutine keep renewing the Boskos resources.
var boskosRenewingTomb *tomb.Tomb

// SynchronizedBeforeSuite and SynchronizedAfterSuite help manages singleton resource (a Boskos project) across Ginkgo nodes.
var _ = ginkgo.SynchronizedBeforeSuite(rentBoskosProjectIfNeededOnNode1, acceptBoskosProjectIfNeededFromNode1)
var _ = ginkgo.SynchronizedAfterSuite(func() {}, releaseBoskosResourcesOnNode1)

func TestNPD(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	var err error
	computeService, err = gce.GetComputeClient()
	if err != nil {
		panic(fmt.Sprintf("Unable to create gcloud compute service using defaults. Make sure you are authenticated. %v", err))
	}

	if *artifactsDir != "" {
		_, err := os.Stat(*artifactsDir)
		if err != nil && os.IsNotExist(err) {
			os.MkdirAll(*artifactsDir, os.ModeDir|0755)
		}
	}

	// The junit formatted result output is for showing test results on testgrid.
	junitReporter := reporters.NewJUnitReporter(path.Join(*artifactsDir, fmt.Sprintf("junit-%02d.xml", config.GinkgoConfig.ParallelNode)))
	ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "NPD Metric-only Suite", []ginkgo.Reporter{junitReporter})
}

// rentBoskosProjectIfNeededOnNode1 rents a GCP project from Boskos if no GCP project is specified.
//
// rentBoskosProjectIfNeededOnNode1 returns a byte slice containing the project name.
// rentBoskosProjectIfNeededOnNode1 also initializes boskosClient if necessary.
// When the tests run in parallel mode in Ginkgo, this rentBoskosProjectIfNeededOnNode1 runs only on
// Ginkgo node 1. The output should be shared with all other Gingko nodes so that they all use the same
// GCP project.
func rentBoskosProjectIfNeededOnNode1() []byte {
	if *project != "" {
		return []byte{}
	}

	fmt.Printf("Renting project from Boskos\n")
	boskosClient = client.NewClient(*jobName, *boskosServerURL)
	boskosRenewingTomb = tomb.NewTomb()

	ctx, cancel := context.WithTimeout(context.Background(), *boskosWaitDuration)
	defer cancel()
	p, err := boskosClient.AcquireWait(ctx, *boskosProjectType, "free", "busy")
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unable to rent project from Boskos: %v\n", err))
	fmt.Printf("Rented project %q from Boskos\n", p.Name)

	go renewBoskosProject(boskosClient, p.Name, boskosRenewingTomb)

	return []byte(p.Name)
}

// acceptBoskosProjectIfNeededFromNode1 accepts a GCP project rented from Boskos by Ginkgo node 1.
//
// acceptBoskosProjectIfNeededFromNode1 takes the output of rentBoskosProjectIfNeededOnNode1.
// When the tests run in parallel mode in Ginkgo, this function runs on all Ginkgo nodes.
func acceptBoskosProjectIfNeededFromNode1(data []byte) {
	if *project != "" {
		return
	}

	boskosProject := string(data)
	fmt.Printf("Received Boskos project %q from Ginkgo node 1.\n", boskosProject)
	*project = boskosProject
}

func renewBoskosProject(boskosClient *client.Client, projectName string, boskosRenewingTomb *tomb.Tomb) {
	defer boskosRenewingTomb.Done()
	for {
		select {
		case <-time.Tick(5 * time.Minute):
			fmt.Printf("Renewing boskosProject %q\n", projectName)
			if err := boskosClient.UpdateOne(projectName, "busy", nil); err != nil {
				fmt.Printf("Failed to update status for project %q with Boskos: %v\n", projectName, err)
			}
		case <-boskosRenewingTomb.Stopping():
			return
		}
	}
}

// releaseBoskosResourcesOnNode1 releases all rented Boskos resources if there is any.
func releaseBoskosResourcesOnNode1() {
	if boskosClient == nil {
		return
	}
	boskosRenewingTomb.Stop()
	if !boskosClient.HasResource() {
		return
	}
	fmt.Printf("Releasing all Boskos resources.\n")
	err := boskosClient.ReleaseAll("dirty")
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Failed to release project to Boskos: %v", err))
}

func TestMain(m *testing.M) {
	RegisterFailHandler(ginkgo.Fail)
	flag.Parse()

	os.Exit(m.Run())
}
<commit_msg>Use Fatal instead of panic for go tests.<commit_after>/*
Copyright 2019 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 e2e_metric_only

import (
	"context"
	"flag"
	"fmt"
	"os"
	"path"
	"testing"
	"time"

	"k8s.io/node-problem-detector/pkg/util/tomb"
	"k8s.io/node-problem-detector/test/e2e/lib/gce"
	"k8s.io/test-infra/boskos/client"

	"github.com/onsi/ginkgo"
	"github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/reporters"
	. "github.com/onsi/gomega"
	compute "google.golang.org/api/compute/v1"
)

var zone = flag.String("zone", "", "gce zone the hosts live in")
var project = flag.String("project", "", "gce project the hosts live in")
var image = flag.String("image", "", "image to test")
var imageFamily = flag.String("image-family", "", "image family to pick up the test image. Ignored when -image is set.")
var imageProject = flag.String("image-project", "", "gce project of the OS image")
var jobName = flag.String("job-name", "", "name of the Prow job running the test")
var sshKey = flag.String("ssh-key", "", "path to ssh private key.")
var sshUser = flag.String("ssh-user", "", "use predefined user for ssh.")
var npdBuildTar = flag.String("npd-build-tar", "", "tarball containing NPD to be tested.")
var artifactsDir = flag.String("artifacts-dir", "", "local directory to save test artifacts into.")
var boskosProjectType = flag.String("boskos-project-type", "gce-project",
	"specifies which project type to select from Boskos.")
var boskosServerURL = flag.String("boskos-server-url", "http://boskos.test-pods.svc.cluster.local",
	"specifies Boskos server URL.")
var boskosWaitDuration = flag.Duration("boskos-wait-duration", 2*time.Minute,
	"Duration to wait before quitting getting Boskos resource.")

var computeService *compute.Service

// boskosClient helps renting project from Boskos, and is only initialized on Ginkgo node 1.
var boskosClient *client.Client

// boskosRenewingTomb stops the goroutine keep renewing the Boskos resources.
var boskosRenewingTomb *tomb.Tomb

// SynchronizedBeforeSuite and SynchronizedAfterSuite help manages singleton resource (a Boskos project) across Ginkgo nodes.
var _ = ginkgo.SynchronizedBeforeSuite(rentBoskosProjectIfNeededOnNode1, acceptBoskosProjectIfNeededFromNode1)
var _ = ginkgo.SynchronizedAfterSuite(func() {}, releaseBoskosResourcesOnNode1)

func TestNPD(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	var err error
	computeService, err = gce.GetComputeClient()
	if err != nil {
		t.Fatalf("Unable to create gcloud compute service using defaults. Make sure you are authenticated. %v", err)
	}

	if *artifactsDir != "" {
		_, err := os.Stat(*artifactsDir)
		if err != nil && os.IsNotExist(err) {
			os.MkdirAll(*artifactsDir, os.ModeDir|0755)
		}
	}

	// The junit formatted result output is for showing test results on testgrid.
	junitReporter := reporters.NewJUnitReporter(path.Join(*artifactsDir, fmt.Sprintf("junit-%02d.xml", config.GinkgoConfig.ParallelNode)))
	ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "NPD Metric-only Suite", []ginkgo.Reporter{junitReporter})
}

// rentBoskosProjectIfNeededOnNode1 rents a GCP project from Boskos if no GCP project is specified.
//
// rentBoskosProjectIfNeededOnNode1 returns a byte slice containing the project name.
// rentBoskosProjectIfNeededOnNode1 also initializes boskosClient if necessary.
// When the tests run in parallel mode in Ginkgo, this rentBoskosProjectIfNeededOnNode1 runs only on
// Ginkgo node 1. The output should be shared with all other Gingko nodes so that they all use the same
// GCP project.
func rentBoskosProjectIfNeededOnNode1() []byte {
	if *project != "" {
		return []byte{}
	}

	fmt.Printf("Renting project from Boskos\n")
	boskosClient = client.NewClient(*jobName, *boskosServerURL)
	boskosRenewingTomb = tomb.NewTomb()

	ctx, cancel := context.WithTimeout(context.Background(), *boskosWaitDuration)
	defer cancel()
	p, err := boskosClient.AcquireWait(ctx, *boskosProjectType, "free", "busy")
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unable to rent project from Boskos: %v\n", err))
	fmt.Printf("Rented project %q from Boskos\n", p.Name)

	go renewBoskosProject(boskosClient, p.Name, boskosRenewingTomb)

	return []byte(p.Name)
}

// acceptBoskosProjectIfNeededFromNode1 accepts a GCP project rented from Boskos by Ginkgo node 1.
//
// acceptBoskosProjectIfNeededFromNode1 takes the output of rentBoskosProjectIfNeededOnNode1.
// When the tests run in parallel mode in Ginkgo, this function runs on all Ginkgo nodes.
func acceptBoskosProjectIfNeededFromNode1(data []byte) {
	if *project != "" {
		return
	}

	boskosProject := string(data)
	fmt.Printf("Received Boskos project %q from Ginkgo node 1.\n", boskosProject)
	*project = boskosProject
}

func renewBoskosProject(boskosClient *client.Client, projectName string, boskosRenewingTomb *tomb.Tomb) {
	defer boskosRenewingTomb.Done()
	for {
		select {
		case <-time.Tick(5 * time.Minute):
			fmt.Printf("Renewing boskosProject %q\n", projectName)
			if err := boskosClient.UpdateOne(projectName, "busy", nil); err != nil {
				fmt.Printf("Failed to update status for project %q with Boskos: %v\n", projectName, err)
			}
		case <-boskosRenewingTomb.Stopping():
			return
		}
	}
}

// releaseBoskosResourcesOnNode1 releases all rented Boskos resources if there is any.
func releaseBoskosResourcesOnNode1() {
	if boskosClient == nil {
		return
	}
	boskosRenewingTomb.Stop()
	if !boskosClient.HasResource() {
		return
	}
	fmt.Printf("Releasing all Boskos resources.\n")
	err := boskosClient.ReleaseAll("dirty")
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Failed to release project to Boskos: %v", err))
}

func TestMain(m *testing.M) {
	RegisterFailHandler(ginkgo.Fail)
	flag.Parse()

	os.Exit(m.Run())
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The cert-manager 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 approval

import (
	"context"
	"crypto/x509"
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	apiutil "github.com/jetstack/cert-manager/pkg/api/util"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	clientset "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	testutil "github.com/jetstack/cert-manager/test/e2e/framework/util"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

// TODO
var _ = framework.CertManagerDescribe("Approval CertificateRequests", func() {
	f := framework.NewDefaultFramework("approval-certificaterequests")

	var (
		sa       *corev1.ServiceAccount
		saclient clientset.Interface
		request  *cmapi.CertificateRequest
	)

	JustBeforeEach(func() {
		var err error

		sa, err = f.KubeClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Create(context.TODO(), &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{
				GenerateName: "test-sa-",
				Namespace:    f.Namespace.Name,
			},
		}, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())

		role, err := f.KubeClientSet.RbacV1().Roles(f.Namespace.Name).Create(context.TODO(), &rbacv1.Role{
			ObjectMeta: metav1.ObjectMeta{
				GenerateName: "certificaterequest-creator-",
				Namespace:    f.Namespace.Name,
			},
			Rules: []rbacv1.PolicyRule{
				{
					Verbs:     []string{"create"},
					APIGroups: []string{"cert-manager.io"},
					Resources: []string{"certificaterequests"},
				},
				{
					Verbs:     []string{"update"},
					APIGroups: []string{"cert-manager.io"},
					Resources: []string{"certificaterequests/status"},
				},
			},
		}, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())

		By("Creating certificaterequest-creator rolebinding for ServiceAccount")
		_, err = f.KubeClientSet.RbacV1().RoleBindings(f.Namespace.Name).Create(context.TODO(), &rbacv1.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				GenerateName: "certificaterequest-creator-",
				Namespace:    f.Namespace.Name,
			},
			Subjects: []rbacv1.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      sa.Name,
					Namespace: f.Namespace.Name,
				},
			},
			RoleRef: rbacv1.RoleRef{
				APIGroup: "rbac.authorization.k8s.io",
				Kind:     "Role",
				Name:     role.Name,
			},
		}, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())

		err = wait.PollImmediate(time.Second, time.Second*10,
			func() (bool, error) {
				sa, err = f.KubeClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get(context.TODO(), sa.Name, metav1.GetOptions{})
				if err != nil {
					return false, err
				}

				if len(sa.Secrets) == 0 {
					return false, nil
				}

				return true, nil
			},
		)
		Expect(err).NotTo(HaveOccurred())

		By("Building ServiceAccount kubernetes clientset")
		sec, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), sa.Secrets[0].Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())

		kubeConfig, err := testutil.LoadConfig(f.Config.KubeConfig, f.Config.KubeContext)
		Expect(err).NotTo(HaveOccurred())

		kubeConfig.BearerToken = fmt.Sprintf("%s", sec.Data["token"])
		kubeConfig.CertData = nil
		kubeConfig.KeyData = nil

		saclient, err = clientset.NewForConfig(kubeConfig)
		Expect(err).NotTo(HaveOccurred())

		csr, _, err := gen.CSR(x509.RSA)
		Expect(err).NotTo(HaveOccurred())

		request = gen.CertificateRequest("",
			gen.SetCertificateRequestNamespace(f.Namespace.Name),
			gen.SetCertificateRequestCSR(csr),
			gen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
				Name:  "test-issuer",
				Kind:  "Issuer",
				Group: "example.io",
			}),
		)
		request.GenerateName = "test-request-"

		request, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), request, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())
	})

	JustAfterEach(func() {
		err := f.KubeClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Delete(context.TODO(), sa.Name, metav1.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
		err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Delete(context.TODO(), request.Name, metav1.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
	})

	It("attempting to approve a certificate request without the approve permission should error", func() {
		approvedCR := request.DeepCopy()
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionApproved, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err := saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).To(HaveOccurred())
	})

	It("attempting to deny a certificate request without the approve permission should error", func() {
		approvedCR := request.DeepCopy()
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionDenied, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err := saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).To(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/* should be able to approve requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/*")

		approvedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionApproved, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/* should be able to deny requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/*")

		deniedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(deniedCR, cmapi.CertificateRequestConditionDenied, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), deniedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/test-issuer should be able to approve requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/test-issuer")

		approvedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionApproved, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/test-issuer should be able to deny requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/test-issuer")

		deniedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(deniedCR, cmapi.CertificateRequestConditionDenied, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), deniedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})
})

func bindServiceAccountToApprove(f *framework.Framework, sa *corev1.ServiceAccount, resourceName string) {
	clusterrole, err := f.KubeClientSet.RbacV1().ClusterRoles().Create(context.TODO(), &rbacv1.ClusterRole{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "certificaterequest-approver-",
		},
		Rules: []rbacv1.PolicyRule{
			{
				APIGroups:     []string{"cert-manager.io"},
				Resources:     []string{"signers"},
				Verbs:         []string{"approve"},
				ResourceNames: []string{resourceName},
			},
		},
	}, metav1.CreateOptions{})
	Expect(err).NotTo(HaveOccurred())

	_, err = f.KubeClientSet.RbacV1().ClusterRoleBindings().Create(context.TODO(), &rbacv1.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "certificaterequest-approver-",
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				Name:      sa.Name,
				Namespace: sa.Namespace,
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "ClusterRole",
			Name:     clusterrole.Name,
		},
	}, metav1.CreateOptions{})
	Expect(err).NotTo(HaveOccurred())
}
<commit_msg>Adds test comment to approval e2e test<commit_after>/*
Copyright 2020 The cert-manager 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 approval

import (
	"context"
	"crypto/x509"
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	apiutil "github.com/jetstack/cert-manager/pkg/api/util"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	clientset "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	testutil "github.com/jetstack/cert-manager/test/e2e/framework/util"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

// This test ensures that the approval condition may only be set by users who
// have the correct RBAC permissions.
var _ = framework.CertManagerDescribe("Approval CertificateRequests", func() {
	f := framework.NewDefaultFramework("approval-certificaterequests")

	var (
		sa       *corev1.ServiceAccount
		saclient clientset.Interface
		request  *cmapi.CertificateRequest
	)

	JustBeforeEach(func() {
		var err error

		sa, err = f.KubeClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Create(context.TODO(), &corev1.ServiceAccount{
			ObjectMeta: metav1.ObjectMeta{
				GenerateName: "test-sa-",
				Namespace:    f.Namespace.Name,
			},
		}, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())

		role, err := f.KubeClientSet.RbacV1().Roles(f.Namespace.Name).Create(context.TODO(), &rbacv1.Role{
			ObjectMeta: metav1.ObjectMeta{
				GenerateName: "certificaterequest-creator-",
				Namespace:    f.Namespace.Name,
			},
			Rules: []rbacv1.PolicyRule{
				{
					Verbs:     []string{"create"},
					APIGroups: []string{"cert-manager.io"},
					Resources: []string{"certificaterequests"},
				},
				{
					Verbs:     []string{"update"},
					APIGroups: []string{"cert-manager.io"},
					Resources: []string{"certificaterequests/status"},
				},
			},
		}, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())

		By("Creating certificaterequest-creator rolebinding for ServiceAccount")
		_, err = f.KubeClientSet.RbacV1().RoleBindings(f.Namespace.Name).Create(context.TODO(), &rbacv1.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				GenerateName: "certificaterequest-creator-",
				Namespace:    f.Namespace.Name,
			},
			Subjects: []rbacv1.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      sa.Name,
					Namespace: f.Namespace.Name,
				},
			},
			RoleRef: rbacv1.RoleRef{
				APIGroup: "rbac.authorization.k8s.io",
				Kind:     "Role",
				Name:     role.Name,
			},
		}, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())

		err = wait.PollImmediate(time.Second, time.Second*10,
			func() (bool, error) {
				sa, err = f.KubeClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get(context.TODO(), sa.Name, metav1.GetOptions{})
				if err != nil {
					return false, err
				}

				if len(sa.Secrets) == 0 {
					return false, nil
				}

				return true, nil
			},
		)
		Expect(err).NotTo(HaveOccurred())

		By("Building ServiceAccount kubernetes clientset")
		sec, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Get(context.TODO(), sa.Secrets[0].Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())

		kubeConfig, err := testutil.LoadConfig(f.Config.KubeConfig, f.Config.KubeContext)
		Expect(err).NotTo(HaveOccurred())

		kubeConfig.BearerToken = fmt.Sprintf("%s", sec.Data["token"])
		kubeConfig.CertData = nil
		kubeConfig.KeyData = nil

		saclient, err = clientset.NewForConfig(kubeConfig)
		Expect(err).NotTo(HaveOccurred())

		csr, _, err := gen.CSR(x509.RSA)
		Expect(err).NotTo(HaveOccurred())

		request = gen.CertificateRequest("",
			gen.SetCertificateRequestNamespace(f.Namespace.Name),
			gen.SetCertificateRequestCSR(csr),
			gen.SetCertificateRequestIssuer(cmmeta.ObjectReference{
				Name:  "test-issuer",
				Kind:  "Issuer",
				Group: "example.io",
			}),
		)
		request.GenerateName = "test-request-"

		request, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), request, metav1.CreateOptions{})
		Expect(err).NotTo(HaveOccurred())
	})

	JustAfterEach(func() {
		err := f.KubeClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Delete(context.TODO(), sa.Name, metav1.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
		err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Delete(context.TODO(), request.Name, metav1.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
	})

	It("attempting to approve a certificate request without the approve permission should error", func() {
		approvedCR := request.DeepCopy()
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionApproved, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err := saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).To(HaveOccurred())
	})

	It("attempting to deny a certificate request without the approve permission should error", func() {
		approvedCR := request.DeepCopy()
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionDenied, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err := saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).To(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/* should be able to approve requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/*")

		approvedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionApproved, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/* should be able to deny requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/*")

		deniedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(deniedCR, cmapi.CertificateRequestConditionDenied, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), deniedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/test-issuer should be able to approve requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/test-issuer")

		approvedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(approvedCR, cmapi.CertificateRequestConditionApproved, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), approvedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})

	It("a service account with the approve permissions for issuer.example.io/test-issuer should be able to deny requests", func() {
		bindServiceAccountToApprove(f, sa, "issuer.example.io/test-issuer")

		deniedCR, err := f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{})
		Expect(err).NotTo(HaveOccurred())
		apiutil.SetCertificateRequestCondition(deniedCR, cmapi.CertificateRequestConditionDenied, cmmeta.ConditionTrue, "cert-manager.io", "integration")
		_, err = saclient.CertmanagerV1().CertificateRequests(f.Namespace.Name).UpdateStatus(context.TODO(), deniedCR, metav1.UpdateOptions{})
		Expect(err).ToNot(HaveOccurred())
	})
})

func bindServiceAccountToApprove(f *framework.Framework, sa *corev1.ServiceAccount, resourceName string) {
	clusterrole, err := f.KubeClientSet.RbacV1().ClusterRoles().Create(context.TODO(), &rbacv1.ClusterRole{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "certificaterequest-approver-",
		},
		Rules: []rbacv1.PolicyRule{
			{
				APIGroups:     []string{"cert-manager.io"},
				Resources:     []string{"signers"},
				Verbs:         []string{"approve"},
				ResourceNames: []string{resourceName},
			},
		},
	}, metav1.CreateOptions{})
	Expect(err).NotTo(HaveOccurred())

	_, err = f.KubeClientSet.RbacV1().ClusterRoleBindings().Create(context.TODO(), &rbacv1.ClusterRoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "certificaterequest-approver-",
		},
		Subjects: []rbacv1.Subject{
			{
				Kind:      "ServiceAccount",
				Name:      sa.Name,
				Namespace: sa.Namespace,
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: "rbac.authorization.k8s.io",
			Kind:     "ClusterRole",
			Name:     clusterrole.Name,
		},
	}, metav1.CreateOptions{})
	Expect(err).NotTo(HaveOccurred())
}
<|endoftext|>