<commit_before>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package robotgo

type uMap map[string]uint16

// MouseMap robotgo hook mouse's code map
var MouseMap = uMap{
	"left":       1,
	"right":      2,
	"center":     3,
	"wheelDown":  4,
	"wheelUp":    5,
	"wheelLeft":  6,
	"wheelRight": 7,
}

// Keycode robotgo hook key's code map
var Keycode = uMap{
	"`": 41,
	"1": 2,
	"2": 3,
	"3": 4,
	"4": 5,
	"5": 6,
	"6": 7,
	"7": 8,
	"8": 9,
	"9": 10,
	"0": 11,
	"-": 12,
	"+": 13,
	//
	"q":  16,
	"w":  17,
	"e":  18,
	"r":  19,
	"t":  20,
	"y":  21,
	"u":  22,
	"i":  23,
	"o":  24,
	"p":  25,
	"[":  26,
	"]":  27,
	"\\": 43,
	//
	"a": 30,
	"s": 31,
	"d": 32,
	"f": 33,
	"g": 34,
	"h": 35,
	"j": 36,
	"k": 37,
	"l": 38,
	";": 39,
	"'": 40,
	//
	"z": 44,
	"x": 45,
	"c": 46,
	"v": 47,
	"b": 48,
	"n": 49,
	"m": 50,
	",": 51,
	".": 52,
	"/": 53,
	//
	"f1":  59,
	"f2":  60,
	"f3":  61,
	"f4":  62,
	"f5":  63,
	"f6":  64,
	"f7":  65,
	"f8":  66,
	"f9":  67,
	"f10": 68,
	"f11": 69,
	"f12": 70,
	// more
	"esc":     1,
	"delete":  14,
	"tab":     15,
	"ctrl":    29,
	"control": 29,
	"alt":     56,
	"space":   57,
	"shift":   42,
	"rshift":  54,
	"enter":   28,
	"cmd":     3675,
	"command": 3675,
	"rcmd":    3676,
	"ralt":    3640,
	"up":      57416,
	"down":    57424,
	"left":    57419,
	"right":   57421,
}
<commit_msg>update keycode.go<commit_after>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package robotgo

type uMap map[string]uint16

// MouseMap robotgo hook mouse's code map
var MouseMap = uMap{
	"left":       1,
	"right":      2,
	"center":     3,
	"wheelDown":  4,
	"wheelUp":    5,
	"wheelLeft":  6,
	"wheelRight": 7,
}

// Keycode robotgo hook key's code map
var Keycode = uMap{
	"`": 41,
	"1": 2,
	"2": 3,
	"3": 4,
	"4": 5,
	"5": 6,
	"6": 7,
	"7": 8,
	"8": 9,
	"9": 10,
	"0": 11,
	"-": 12,
	"+": 13,
	//
	"q":  16,
	"w":  17,
	"e":  18,
	"r":  19,
	"t":  20,
	"y":  21,
	"u":  22,
	"i":  23,
	"o":  24,
	"p":  25,
	"[":  26,
	"]":  27,
	"\\": 43,
	//
	"a": 30,
	"s": 31,
	"d": 32,
	"f": 33,
	"g": 34,
	"h": 35,
	"j": 36,
	"k": 37,
	"l": 38,
	";": 39,
	"'": 40,
	//
	"z": 44,
	"x": 45,
	"c": 46,
	"v": 47,
	"b": 48,
	"n": 49,
	"m": 50,
	",": 51,
	".": 52,
	"/": 53,
	//
	"f1":  59,
	"f2":  60,
	"f3":  61,
	"f4":  62,
	"f5":  63,
	"f6":  64,
	"f7":  65,
	"f8":  66,
	"f9":  67,
	"f10": 68,
	"f11": 69,
	"f12": 70,
	// more
	"esc":     1,
	"delete":  14,
	"tab":     15,
	"ctrl":    29,
	"control": 29,
	"alt":     56,
	"space":   57,
	"shift":   42,
	"rshift":  54,
	"enter":   28,
	//
	"cmd":     3675,
	"command": 3675,
	"rcmd":    3676,
	"ralt":    3640,
	//
	"up":    57416,
	"down":  57424,
	"left":  57419,
	"right": 57421,
}
<|endoftext|>


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

// Modified 2016 by Steve Manuel, Boss Sauce Creative, LLC
// All modifications are relicensed under the same BSD license
// found in the LICENSE file.

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'devcerts/cert.pem' and 'devcerts/key.pem' and will overwrite existing files.

package tls

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"log"
	"math/big"
	"net"
	"os"
	"path/filepath"
	"time"

	"github.com/ponzu-cms/ponzu/system/db"
)

func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	case *ecdsa.PrivateKey:
		return &k.PublicKey
	default:
		return nil
	}
}

func pemBlockForKey(priv interface{}) *pem.Block {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
			os.Exit(2)
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
	default:
		return nil
	}
}

func setupDev() {
	var priv interface{}
	var err error

	priv, err = rsa.GenerateKey(rand.Reader, 2048)

	if err != nil {
		log.Fatalf("failed to generate private key: %s", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(time.Hour * 24 * 30) // valid for 30 days

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Ponzu Dev Server"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := []string{"localhost", "0.0.0.0"}
	domain := db.ConfigCache("domain")
	if domain != "" {
		hosts = append(hosts, domain)
	}

	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	// make all certs CA
	template.IsCA = true
	template.KeyUsage |= x509.KeyUsageCertSign

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		log.Fatalln("Failed to create certificate:", err)
	}

	// overwrite/create directory for devcerts
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatalln("Couldn't find working directory to locate or save dev certificates:", err)
	}

	vendorTLSPath := filepath.Join(pwd, "cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu", "system", "tls")
	devcertsPath := filepath.Join(vendorTLSPath, "devcerts")
	fmt.Println(devcertsPath)

	// clear all old certs if found
	err = os.RemoveAll(devcertsPath)
	if err != nil {
		log.Fatalln("Failed to remove old files from dev certificate directory:", err)
	}

	err = os.Mkdir(devcertsPath, os.ModePerm|os.ModePerm)
	if err != nil {
		log.Fatalln("Failed to create directory to locate or save dev certificates:", err)
	}

	certOut, err := os.Create(filepath.Join(devcertsPath, "cert.pem"))
	if err != nil {
		log.Fatalln("Failed to open devcerts/cert.pem for writing:", err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.OpenFile(filepath.Join(devcertsPath, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalln("Failed to open devcerts/key.pem for writing:", err)
		return
	}
	pem.Encode(keyOut, pemBlockForKey(priv))
	keyOut.Close()
}
<commit_msg>removing old fmt print for debug<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Modified 2016 by Steve Manuel, Boss Sauce Creative, LLC
// All modifications are relicensed under the same BSD license
// found in the LICENSE file.

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'devcerts/cert.pem' and 'devcerts/key.pem' and will overwrite existing files.

package tls

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"log"
	"math/big"
	"net"
	"os"
	"path/filepath"
	"time"

	"github.com/ponzu-cms/ponzu/system/db"
)

func publicKey(priv interface{}) interface{} {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &k.PublicKey
	case *ecdsa.PrivateKey:
		return &k.PublicKey
	default:
		return nil
	}
}

func pemBlockForKey(priv interface{}) *pem.Block {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
			os.Exit(2)
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
	default:
		return nil
	}
}

func setupDev() {
	var priv interface{}
	var err error

	priv, err = rsa.GenerateKey(rand.Reader, 2048)

	if err != nil {
		log.Fatalf("failed to generate private key: %s", err)
	}

	notBefore := time.Now()
	notAfter := notBefore.Add(time.Hour * 24 * 30) // valid for 30 days

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatalf("failed to generate serial number: %s", err)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Ponzu Dev Server"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	hosts := []string{"localhost", "0.0.0.0"}
	domain := db.ConfigCache("domain")
	if domain != "" {
		hosts = append(hosts, domain)
	}

	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}

	// make all certs CA
	template.IsCA = true
	template.KeyUsage |= x509.KeyUsageCertSign

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		log.Fatalln("Failed to create certificate:", err)
	}

	// overwrite/create directory for devcerts
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatalln("Couldn't find working directory to locate or save dev certificates:", err)
	}

	vendorTLSPath := filepath.Join(pwd, "cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu", "system", "tls")
	devcertsPath := filepath.Join(vendorTLSPath, "devcerts")

	// clear all old certs if found
	err = os.RemoveAll(devcertsPath)
	if err != nil {
		log.Fatalln("Failed to remove old files from dev certificate directory:", err)
	}

	err = os.Mkdir(devcertsPath, os.ModePerm|os.ModePerm)
	if err != nil {
		log.Fatalln("Failed to create directory to locate or save dev certificates:", err)
	}

	certOut, err := os.Create(filepath.Join(devcertsPath, "cert.pem"))
	if err != nil {
		log.Fatalln("Failed to open devcerts/cert.pem for writing:", err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	keyOut, err := os.OpenFile(filepath.Join(devcertsPath, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalln("Failed to open devcerts/key.pem for writing:", err)
		return
	}
	pem.Encode(keyOut, pemBlockForKey(priv))
	keyOut.Close()
}
<|endoftext|>


<commit_before>package retry

import (
	"time"

	"github.com/hashicorp/consul/lib"
)

const (
	defaultMinFailures = 0
	defaultMaxWait     = 2 * time.Minute
)

// Interface used for offloading jitter calculations from the RetryWaiter
type Jitter interface {
	AddJitter(baseTime time.Duration) time.Duration
}

// Calculates a random jitter between 0 and up to a specific percentage of the baseTime
type JitterRandomStagger struct {
	// int64 because we are going to be doing math against an int64 to represent nanoseconds
	percent int64
}

// Creates a new JitterRandomStagger
func NewJitterRandomStagger(percent int) *JitterRandomStagger {
	if percent < 0 {
		percent = 0
	}

	return &JitterRandomStagger{
		percent: int64(percent),
	}
}

// Implments the Jitter interface
func (j *JitterRandomStagger) AddJitter(baseTime time.Duration) time.Duration {
	if j.percent == 0 {
		return baseTime
	}

	// time.Duration is actually a type alias for int64 which is why casting
	// to the duration type and then dividing works
	return baseTime + lib.RandomStagger((baseTime*time.Duration(j.percent))/100)
}

// RetryWaiter will record failed and successful operations and provide
// a channel to wait on before a failed operation can be retried.
type Waiter struct {
	minFailures uint
	minWait     time.Duration
	maxWait     time.Duration
	jitter      Jitter
	failures    uint
}

// Creates a new RetryWaiter
func NewRetryWaiter(minFailures int, minWait, maxWait time.Duration, jitter Jitter) *Waiter {
	if minFailures < 0 {
		minFailures = defaultMinFailures
	}

	if maxWait <= 0 {
		maxWait = defaultMaxWait
	}

	if minWait <= 0 {
		minWait = 0 * time.Nanosecond
	}

	return &Waiter{
		minFailures: uint(minFailures),
		minWait:     minWait,
		maxWait:     maxWait,
		failures:    0,
		jitter:      jitter,
	}
}

// calculates the necessary wait time before the
// next operation should be allowed.
func (rw *Waiter) calculateWait() time.Duration {
	waitTime := rw.minWait
	if rw.failures > rw.minFailures {
		shift := rw.failures - rw.minFailures - 1
		waitTime = rw.maxWait
		if shift < 31 {
			waitTime = (1 << shift) * time.Second
		}
		if waitTime > rw.maxWait {
			waitTime = rw.maxWait
		}

		if rw.jitter != nil {
			waitTime = rw.jitter.AddJitter(waitTime)
		}
	}

	if waitTime < rw.minWait {
		waitTime = rw.minWait
	}

	return waitTime
}

// calculates the waitTime and returns a chan
// that will become selectable once that amount
// of time has elapsed.
func (rw *Waiter) wait() <-chan struct{} {
	waitTime := rw.calculateWait()
	ch := make(chan struct{})
	if waitTime > 0 {
		time.AfterFunc(waitTime, func() { close(ch) })
	} else {
		// if there should be 0 wait time then we ensure
		// that the chan will be immediately selectable
		close(ch)
	}
	return ch
}

// Marks that an operation is successful which resets the failure count.
// The chan that is returned will be immediately selectable
func (rw *Waiter) Success() <-chan struct{} {
	rw.Reset()
	return rw.wait()
}

// Marks that an operation failed. The chan returned will be selectable
// once the calculated retry wait amount of time has elapsed
func (rw *Waiter) Failed() <-chan struct{} {
	rw.failures += 1
	ch := rw.wait()
	return ch
}

// Resets the internal failure counter.
func (rw *Waiter) Reset() {
	rw.failures = 0
}

// Failures returns the current number of consecutive failures recorded.
func (rw *Waiter) Failures() int {
	return int(rw.failures)
}

// WaitIf is a convenice method to record whether the last
// operation was a success or failure and return a chan that
// will be selectablw when the next operation can be done.
func (rw *Waiter) WaitIf(failure bool) <-chan struct{} {
	if failure {
		return rw.Failed()
	}
	return rw.Success()
}

// WaitIfErr is a convenience method to record whether the last
// operation was a success or failure based on whether the err
// is nil and then return a chan that will be selectable when
// the next operation can be done.
func (rw *Waiter) WaitIfErr(err error) <-chan struct{} {
	return rw.WaitIf(err != nil)
}
<commit_msg>lib/retry: export fields<commit_after>package retry

import (
	"time"

	"github.com/hashicorp/consul/lib"
)

const (
	defaultMinFailures = 0
	defaultMaxWait     = 2 * time.Minute
)

// Interface used for offloading jitter calculations from the RetryWaiter
type Jitter interface {
	AddJitter(baseTime time.Duration) time.Duration
}

// Calculates a random jitter between 0 and up to a specific percentage of the baseTime
type JitterRandomStagger struct {
	// int64 because we are going to be doing math against an int64 to represent nanoseconds
	percent int64
}

// Creates a new JitterRandomStagger
func NewJitterRandomStagger(percent int) *JitterRandomStagger {
	if percent < 0 {
		percent = 0
	}

	return &JitterRandomStagger{
		percent: int64(percent),
	}
}

// Implments the Jitter interface
func (j *JitterRandomStagger) AddJitter(baseTime time.Duration) time.Duration {
	if j.percent == 0 {
		return baseTime
	}

	// time.Duration is actually a type alias for int64 which is why casting
	// to the duration type and then dividing works
	return baseTime + lib.RandomStagger((baseTime*time.Duration(j.percent))/100)
}

// RetryWaiter will record failed and successful operations and provide
// a channel to wait on before a failed operation can be retried.
type Waiter struct {
	MinFailures uint
	MinWait     time.Duration
	MaxWait     time.Duration
	Jitter      Jitter
	failures    uint
}

// Creates a new RetryWaiter
func NewRetryWaiter(minFailures int, minWait, maxWait time.Duration, jitter Jitter) *Waiter {
	if minFailures < 0 {
		minFailures = defaultMinFailures
	}

	if maxWait <= 0 {
		maxWait = defaultMaxWait
	}

	if minWait <= 0 {
		minWait = 0 * time.Nanosecond
	}

	return &Waiter{
		MinFailures: uint(minFailures),
		MinWait:     minWait,
		MaxWait:     maxWait,
		failures:    0,
		Jitter:      jitter,
	}
}

// calculates the necessary wait time before the
// next operation should be allowed.
func (rw *Waiter) calculateWait() time.Duration {
	waitTime := rw.MinWait
	if rw.failures > rw.MinFailures {
		shift := rw.failures - rw.MinFailures - 1
		waitTime = rw.MaxWait
		if shift < 31 {
			waitTime = (1 << shift) * time.Second
		}
		if waitTime > rw.MaxWait {
			waitTime = rw.MaxWait
		}

		if rw.Jitter != nil {
			waitTime = rw.Jitter.AddJitter(waitTime)
		}
	}

	if waitTime < rw.MinWait {
		waitTime = rw.MinWait
	}

	return waitTime
}

// calculates the waitTime and returns a chan
// that will become selectable once that amount
// of time has elapsed.
func (rw *Waiter) wait() <-chan struct{} {
	waitTime := rw.calculateWait()
	ch := make(chan struct{})
	if waitTime > 0 {
		time.AfterFunc(waitTime, func() { close(ch) })
	} else {
		// if there should be 0 wait time then we ensure
		// that the chan will be immediately selectable
		close(ch)
	}
	return ch
}

// Marks that an operation is successful which resets the failure count.
// The chan that is returned will be immediately selectable
func (rw *Waiter) Success() <-chan struct{} {
	rw.Reset()
	return rw.wait()
}

// Marks that an operation failed. The chan returned will be selectable
// once the calculated retry wait amount of time has elapsed
func (rw *Waiter) Failed() <-chan struct{} {
	rw.failures += 1
	ch := rw.wait()
	return ch
}

// Resets the internal failure counter.
func (rw *Waiter) Reset() {
	rw.failures = 0
}

// Failures returns the current number of consecutive failures recorded.
func (rw *Waiter) Failures() int {
	return int(rw.failures)
}

// WaitIf is a convenice method to record whether the last
// operation was a success or failure and return a chan that
// will be selectablw when the next operation can be done.
func (rw *Waiter) WaitIf(failure bool) <-chan struct{} {
	if failure {
		return rw.Failed()
	}
	return rw.Success()
}

// WaitIfErr is a convenience method to record whether the last
// operation was a success or failure based on whether the err
// is nil and then return a chan that will be selectable when
// the next operation can be done.
func (rw *Waiter) WaitIfErr(err error) <-chan struct{} {
	return rw.WaitIf(err != nil)
}
<|endoftext|>


<commit_before>package etcd

import (
	"github.com/coreos/go-log/log"
	"os"
)

var logger *log.Logger

func init() {
	setLogger(log.PriErr)
	OpenDebug()
}

func OpenDebug() {
	setLogger(log.PriDebug)
}

func CloseDebug() {
	setLogger(log.PriErr)
}

func setLogger(priority log.Priority) {
	logger = log.NewSimple(
		log.PriorityFilter(
			priority,
			log.WriterSink(os.Stdout, log.BasicFormat, log.BasicFields)))
}
<commit_msg>Uncomment OpenDebug<commit_after>package etcd

import (
	"github.com/coreos/go-log/log"
	"os"
)

var logger *log.Logger

func init() {
	setLogger(log.PriErr)
	// Uncomment the following line if you want to see lots of logs
	// OpenDebug()
}

func OpenDebug() {
	setLogger(log.PriDebug)
}

func CloseDebug() {
	setLogger(log.PriErr)
}

func setLogger(priority log.Priority) {
	logger = log.NewSimple(
		log.PriorityFilter(
			priority,
			log.WriterSink(os.Stdout, log.BasicFormat, log.BasicFields)))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/base64"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const (
	defaultPort  = 7171
	dataDir      = "notes/"
	templatesDir = "templates/"
	fileExt      = ".txt"
)

var (
	port = flag.Int("port", defaultPort, "Specify the listening port.")
)
var (
	templates        *template.Template
	linkPattern      = regexp.MustCompile(`(\b)([a-zA-Z0-9\-\._]+)\.txt`)
	linkTemplate     = `$1<a href="/view/$2">$2</a>`
	deadLinkTemplate = `$1<a href="/view/$2">$2</a> [no such file] `
	validPath        = regexp.MustCompile(`^/((view|edit|save|styles|error)/([a-zA-Z0-9\.\-_]*))?(favicon.ico)?$`)
)

type Model map[string]interface{}

type Page struct {
	Title string
	Body  string
}

func main() {
	flag.Parse()
	checkEnvironment()
	registerHandlers()
	fmt.Println("Running... ( port", *port, ")")
	http.ListenAndServe(":"+strconv.Itoa(*port), nil)
}

func checkEnvironment() {
	_, err := os.Stat(dataDir)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(dataDir, 0644)
			if err != nil {
				fmt.Println(`Unable to create "notes" directory:`, err)
				os.Exit(0)
			}
		} else {
			fmt.Println(`Unable to check existence of "notes" directory:`, err)
			os.Exit(0)
		}
	}
	_, err = os.Stat(templatesDir)
	if err != nil {
		if os.IsNotExist(err) {
			// Template dir does not exist
			fmt.Println("Using default templates.")
			templates = template.Must(template.New("index.html").Parse(defaultTemplateIndexHtml))
			template.Must(templates.New("view.html").Parse(defaultTemplateViewHtml))
			template.Must(templates.New("edit.html").Parse(defaultTemplateEditHtml))
			template.Must(templates.New("error.html").Parse(defaultTemplateErrorHtml))
		}
	} else {
		fmt.Println("Using file templates.")
		templates = template.Must(template.ParseFiles(
			templatesDir+"index.html",
			templatesDir+"view.html",
			templatesDir+"edit.html",
			templatesDir+"error.html"))
	}
}

func registerHandlers() {
	http.HandleFunc("/", makeHandler(indexHandler))
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.HandleFunc("/styles/", makeHandler(styleHandler))
	http.HandleFunc("/favicon.ico", makeHandler(faviconHandler))
	http.HandleFunc("/error/", makeHandler(errorHandler))
}

func (p *Page) save() error {
	filename := p.Title + fileExt
	return ioutil.WriteFile(dataDir+filename, []byte(p.Body), 0644)
}

func (p *Page) load(title string) (*Page, error) {
	filename := title + fileExt
	filename = strings.Replace(filename, "%20", " ", -1)
	body, err := ioutil.ReadFile(dataDir + filename)
	if err != nil {
		return nil, err
	}
	p.Title = title
	p.Body = string(body)
	return p, nil
}

func buildModel(p *Page, asHtml bool) Model {
	b := p.Body
	if asHtml {
		b = parseText(b)
	}
	m := Model{
		"Title": p.Title,
		"Body":  template.HTML(b),
	}
	return m
}

func parseText(body string) string {
	b := strings.Replace(body, " ", " ", -1)
	b = strings.Replace(b, "\t", "    ", -1)
	b = strings.Replace(b, "\n", "
", -1)
	b = linkPattern.ReplaceAllStringFunc(b, func(s string) string {
		_, err := os.Stat(dataDir + s)
		if err == nil {
			r := linkPattern.ReplaceAllString(s, linkTemplate)
			return r
		} else {
			r := linkPattern.ReplaceAllString(s, deadLinkTemplate)
			return r
		}
	})
	return b
}

func faviconHandler(w http.ResponseWriter, r *http.Request, title string) {
	body, err := base64.StdEncoding.DecodeString(faviconBase64)
	if err != nil {
		fmt.Println("favicon handler decoding error:", err)
		return
	}
	w.Header().Set("content-type", "image/x-icon")
	w.Write(body)
	return
}

func styleHandler(w http.ResponseWriter, r *http.Request, title string) {
	filename := "styles/" + title
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("Styles handler err:", err)
		return
	}
	w.Header().Set("content-type", "text/css")
	w.Write(body)
}

func indexHandler(w http.ResponseWriter, req *http.Request, title string) {
	title = "index"
	p, err := new(Page).load(title)
	if err != nil {
		http.Redirect(w, req, "/edit/"+title, http.StatusFound)
		return
	}
	model := buildModel(p, true)
	renderTemplate(w, "index", model)
}

func errorHandler(w http.ResponseWriter, req *http.Request, title string) {
	//title = "error"
	//p := &Page{Title: title}
	p := &Page{}
	model := buildModel(p, true)
	renderTemplate(w, "error", model)
}

func viewHandler(w http.ResponseWriter, req *http.Request, title string) {
	if title == "index" {
		http.Redirect(w, req, "/", http.StatusFound)
		return
	}
	p, err := new(Page).load(title)
	if err != nil {
		http.Redirect(w, req, "/edit/"+title, http.StatusFound)
		return
	}
	model := buildModel(p, true)
	renderTemplate(w, "view", model)
}

func editHandler(w http.ResponseWriter, req *http.Request, title string) {
	p, err := new(Page).load(title)
	if err != nil {
		p = &Page{Title: title}
	}
	model := buildModel(p, false)
	renderTemplate(w, "edit", model)
}

func saveHandler(w http.ResponseWriter, req *http.Request, title string) {
	body := req.FormValue("body")
	p := &Page{Title: title, Body: body}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, req, "/view/"+title, http.StatusFound)
}

func renderTemplate(w http.ResponseWriter, tmpl string, model Model) {
	err := templates.ExecuteTemplate(w, tmpl+".html", model)
	if err != nil {
		fmt.Println("Error in rendertemplate: ", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		pathParts := validPath.FindStringSubmatch(req.URL.Path)
		if pathParts == nil {
			http.Redirect(w, req, "/error/", http.StatusFound)
			return
		}
		fn(w, req, pathParts[3])
	}
}
<commit_msg>Documentation.<commit_after>// Kwikwik is web based text file interface, lighter than a wiki.
package main

import (
	"encoding/base64"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

const (
	defaultPort  = 7171
	dataDir      = "notes/"
	templatesDir = "templates/"
	fileExt      = ".txt"
)

var (
	port = flag.Int("port", defaultPort, "Specify the listening port.")
)
var (
	templates        *template.Template
	linkPattern      = regexp.MustCompile(`(\b)([a-zA-Z0-9\-\._]+)\.txt`)
	linkTemplate     = `$1<a href="/view/$2">$2</a>`
	deadLinkTemplate = `$1<a href="/view/$2">$2</a> [no such file] `
	validPath        = regexp.MustCompile(`^/((view|edit|save|styles|error)/([a-zA-Z0-9\.\-_]*))?(favicon.ico)?$`)
)

type Model map[string]interface{}

type Page struct {
	Title string
	Body  string
}

func main() {
	flag.Parse()
	checkEnvironment()
	registerHandlers()
	fmt.Println("Running... ( port", *port, ")")
	http.ListenAndServe(":"+strconv.Itoa(*port), nil)
}

// checkEnvironment checks if required dirs and files exist, uses defaults if not.
func checkEnvironment() {
	_, err := os.Stat(dataDir)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(dataDir, 0644)
			if err != nil {
				fmt.Println(`Unable to create "notes" directory:`, err)
				os.Exit(0)
			}
		} else {
			fmt.Println(`Unable to check existence of "notes" directory:`, err)
			os.Exit(0)
		}
	}
	_, err = os.Stat(templatesDir)
	if err != nil {
		if os.IsNotExist(err) {
			// Template dir does not exist
			fmt.Println("Using default templates.")
			templates = template.Must(template.New("index.html").Parse(defaultTemplateIndexHtml))
			template.Must(templates.New("view.html").Parse(defaultTemplateViewHtml))
			template.Must(templates.New("edit.html").Parse(defaultTemplateEditHtml))
			template.Must(templates.New("error.html").Parse(defaultTemplateErrorHtml))
		}
	} else {
		fmt.Println("Using file templates.")
		templates = template.Must(template.ParseFiles(
			templatesDir+"index.html",
			templatesDir+"view.html",
			templatesDir+"edit.html",
			templatesDir+"error.html"))
	}
}

// registerHandlers registers with the HTTP request multiplexer the request handlers and
// the associated paths they handle.
func registerHandlers() {
	http.HandleFunc("/", makeHandler(indexHandler))
	http.HandleFunc("/view/", makeHandler(viewHandler))
	http.HandleFunc("/edit/", makeHandler(editHandler))
	http.HandleFunc("/save/", makeHandler(saveHandler))
	http.HandleFunc("/styles/", makeHandler(styleHandler))
	http.HandleFunc("/favicon.ico", makeHandler(faviconHandler))
	http.HandleFunc("/error/", makeHandler(errorHandler))
}

// save saves an edited text file to disk.
func (p *Page) save() error {
	filename := p.Title + fileExt
	return ioutil.WriteFile(dataDir+filename, []byte(p.Body), 0644)
}

// load loads a text file from disk.
func (p *Page) load(title string) (*Page, error) {
	filename := title + fileExt
	filename = strings.Replace(filename, "%20", " ", -1)
	body, err := ioutil.ReadFile(dataDir + filename)
	if err != nil {
		return nil, err
	}
	p.Title = title
	p.Body = string(body)
	return p, nil
}

// buildModel builds the data model that is passed to the html template.
func buildModel(p *Page, asHtml bool) Model {
	b := p.Body
	if asHtml {
		b = parseText(b)
	}
	m := Model{
		"Title": p.Title,
		"Body":  template.HTML(b),
	}
	return m
}

// parseText replaces certain string patterns in a text file with HTML.
func parseText(body string) string {
	b := strings.Replace(body, " ", " ", -1)
	b = strings.Replace(b, "\t", "    ", -1)
	b = strings.Replace(b, "\n", "
", -1)
	b = linkPattern.ReplaceAllStringFunc(b, func(s string) string {
		_, err := os.Stat(dataDir + s)
		if err == nil {
			r := linkPattern.ReplaceAllString(s, linkTemplate)
			return r
		} else {
			r := linkPattern.ReplaceAllString(s, deadLinkTemplate)
			return r
		}
	})
	return b
}

func faviconHandler(w http.ResponseWriter, r *http.Request, title string) {
	body, err := base64.StdEncoding.DecodeString(faviconBase64)
	if err != nil {
		fmt.Println("favicon handler decoding error:", err)
		return
	}
	w.Header().Set("content-type", "image/x-icon")
	w.Write(body)
	return
}

// styleHandler handles css requests.
func styleHandler(w http.ResponseWriter, r *http.Request, title string) {
	filename := "styles/" + title
	body, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Println("Styles handler err:", err)
		return
	}
	w.Header().Set("content-type", "text/css")
	w.Write(body)
}

func indexHandler(w http.ResponseWriter, req *http.Request, title string) {
	title = "index"
	p, err := new(Page).load(title)
	if err != nil {
		http.Redirect(w, req, "/edit/"+title, http.StatusFound)
		return
	}
	model := buildModel(p, true)
	renderTemplate(w, "index", model)
}

func errorHandler(w http.ResponseWriter, req *http.Request, title string) {
	//title = "error"
	//p := &Page{Title: title}
	p := &Page{}
	model := buildModel(p, true)
	renderTemplate(w, "error", model)
}

func viewHandler(w http.ResponseWriter, req *http.Request, title string) {
	if title == "index" {
		http.Redirect(w, req, "/", http.StatusFound)
		return
	}
	p, err := new(Page).load(title)
	if err != nil {
		http.Redirect(w, req, "/edit/"+title, http.StatusFound)
		return
	}
	model := buildModel(p, true)
	renderTemplate(w, "view", model)
}

func editHandler(w http.ResponseWriter, req *http.Request, title string) {
	p, err := new(Page).load(title)
	if err != nil {
		p = &Page{Title: title}
	}
	model := buildModel(p, false)
	renderTemplate(w, "edit", model)
}

func saveHandler(w http.ResponseWriter, req *http.Request, title string) {
	body := req.FormValue("body")
	p := &Page{Title: title, Body: body}
	err := p.save()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, req, "/view/"+title, http.StatusFound)
}

// renderTemplate renders the html template with the data model.
func renderTemplate(w http.ResponseWriter, tmpl string, model Model) {
	err := templates.ExecuteTemplate(w, tmpl+".html", model)
	if err != nil {
		fmt.Println("Error in rendertemplate: ", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// makeHandler wraps handler functions with common functionality.
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		pathParts := validPath.FindStringSubmatch(req.URL.Path)
		if pathParts == nil {
			http.Redirect(w, req, "/error/", http.StatusFound)
			return
		}
		fn(w, req, pathParts[3])
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"bytes"
	"github.com/SpectoLabs/hoverfly/core/handlers/v2"
	"github.com/SpectoLabs/hoverfly/hoverctl/wrapper"
	"github.com/spf13/cobra"
)

var diffStoreCmd = &cobra.Command{
	Use:   "diff",
	Short: "Manage the diffs for Hoverfly",
	Long: `
This allows you to get or clean the differences
between expected and actual responses stored by
the Diff mode in Hoverfly. The diffs are represented
as lists of strings grouped by the same requests.
	`,
}

const errorMsgTemplate = "The \"%s\" parameter is not same - the expected value was [%s], but the actual one [%s]\n"

var getAllDiffStoreCmd = &cobra.Command{
	Use:   "get",
	Short: "Gets all diffs stored in Hoverfly",
	Long: `
Returns all differences between expected and actual responses from Hoverfly.
	`,
	Run: func(cmd *cobra.Command, args []string) {

		checkTargetAndExit(target)

		if len(args) == 0 {
			diffs, err := wrapper.GetAllDiffs(*target)
			handleIfError(err)
			var output bytes.Buffer

			for _, diffsWithRequest := range diffs {
				output.WriteString(
					fmt.Sprintf("\nFor the request with the simple definition:\n"+
						"\n Method: %s \n Host: %s \n Path: %s \n Query:  %s \n\nhave been recorded %s diff(s):\n",
						diffsWithRequest.Request.Method,
						diffsWithRequest.Request.Host,
						diffsWithRequest.Request.Path,
						diffsWithRequest.Request.Query,
						fmt.Sprint(len(diffsWithRequest.DiffReport))))

				for index, diff := range diffsWithRequest.DiffReport {
					output.WriteString(fmt.Sprintf("\n%s. recorded at %s\n%s\n",
						fmt.Sprint(index+1), diff.Timestamp, diffReportMessage(diff)))
				}
			}

			if len(output.Bytes()) == 0 {
				fmt.Println("There are no diffs stored in Hoverfly")
			} else {
				fmt.Println(output.String())
			}
		}
	},
}

var deleteDiffsCmd = &cobra.Command{
	Use:   "delete",
	Short: "Deletes all diffs",
	Long: `
Deletes all differences between expected and actual responses stored in Hoverfly.
	`,
	Run: func(cmd *cobra.Command, args []string) {

		checkTargetAndExit(target)

		err := wrapper.DeleteAllDiffs(*target)
		handleIfError(err)
		fmt.Println("All diffs have been deleted")
	},
}

func diffReportMessage(report v2.DiffReport) string {
	var msg bytes.Buffer
	for index, entry := range report.DiffEntries {
		msg.Write([]byte(fmt.Sprintf("(%d)"+errorMsgTemplate, index+1, entry.Field, entry.Expected, entry.Actual)))
	}
	return msg.String()
}

func init() {
	RootCmd.AddCommand(diffStoreCmd)
	diffStoreCmd.AddCommand(getAllDiffStoreCmd)
	diffStoreCmd.AddCommand(deleteDiffsCmd)
}
<commit_msg>Tidy up output of diff<commit_after>package cmd

import (
	"fmt"

	"bytes"

	"github.com/SpectoLabs/hoverfly/core/handlers/v2"
	"github.com/SpectoLabs/hoverfly/hoverctl/wrapper"
	"github.com/spf13/cobra"
)

var diffStoreCmd = &cobra.Command{
	Use:   "diff",
	Short: "Manage the diffs for Hoverfly",
	Long: `
This allows you to get or clean the differences
between expected and actual responses stored by
the Diff mode in Hoverfly. The diffs are represented
as lists of strings grouped by the same requests.
	`,
}

const errorMsgTemplate = "\"%s\"\nthe expected value was [%s], but actual value was [%s]\n\n"

var getAllDiffStoreCmd = &cobra.Command{
	Use:   "get",
	Short: "Gets all diffs stored in Hoverfly",
	Long: `
Returns all differences between expected and actual responses from Hoverfly.
	`,
	Run: func(cmd *cobra.Command, args []string) {

		checkTargetAndExit(target)

		if len(args) == 0 {
			diffs, err := wrapper.GetAllDiffs(*target)
			handleIfError(err)
			var output bytes.Buffer

			for _, diffsWithRequest := range diffs {

				diffString := "diff"
				if len(diffsWithRequest.DiffReport) > 1 {
					diffString = "diffs"
				}
				output.WriteString(
					fmt.Sprintf("For request:\n"+
						"\n Method: %s \n Host: %s \n Path: %s \n Query:  %s \n\n%s %s recorded:\n",
						diffsWithRequest.Request.Method,
						diffsWithRequest.Request.Host,
						diffsWithRequest.Request.Path,
						diffsWithRequest.Request.Query,
						fmt.Sprint(len(diffsWithRequest.DiffReport)),
						diffString,
					))

				for index, diff := range diffsWithRequest.DiffReport {
					output.WriteString(fmt.Sprintf("\n%s. %s\n%s\n",
						fmt.Sprint(index+1), diff.Timestamp, diffReportMessage(diff)))
				}
			}

			if len(output.Bytes()) == 0 {
				fmt.Println("There are no diffs stored in Hoverfly")
			} else {
				fmt.Println(output.String())
			}
		}
	},
}

var deleteDiffsCmd = &cobra.Command{
	Use:   "delete",
	Short: "Deletes all diffs",
	Long: `
Deletes all differences between expected and actual responses stored in Hoverfly.
	`,
	Run: func(cmd *cobra.Command, args []string) {

		checkTargetAndExit(target)

		err := wrapper.DeleteAllDiffs(*target)
		handleIfError(err)
		fmt.Println("All diffs have been deleted")
	},
}

func diffReportMessage(report v2.DiffReport) string {
	var msg bytes.Buffer
	for index, entry := range report.DiffEntries {
		msg.Write([]byte(fmt.Sprintf("(%d)"+errorMsgTemplate, index+1, entry.Field, entry.Expected, entry.Actual)))
	}
	return msg.String()
}

func init() {
	RootCmd.AddCommand(diffStoreCmd)
	diffStoreCmd.AddCommand(getAllDiffStoreCmd)
	diffStoreCmd.AddCommand(deleteDiffsCmd)
}
<|endoftext|>


<commit_before>// +build proto

/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package api_test

import (
	"encoding/hex"
	"math/rand"
	"testing"

	"github.com/gogo/protobuf/proto"
	"k8s.io/kubernetes/pkg/api"
	apitesting "k8s.io/kubernetes/pkg/api/testing"
	"k8s.io/kubernetes/pkg/api/v1"
	_ "k8s.io/kubernetes/pkg/apis/extensions"
	_ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/protobuf"
	"k8s.io/kubernetes/pkg/util/diff"
)

func init() {
	codecsToTest = append(codecsToTest, func(version string, item runtime.Object) (runtime.Codec, error) {
		return protobuf.NewCodec(version, api.Scheme, api.Scheme, api.Scheme), nil
	})
}

func TestProtobufRoundTrip(t *testing.T) {
	obj := &v1.Pod{}
	apitesting.FuzzerFor(t, "v1", rand.NewSource(benchmarkSeed)).Fuzz(obj)
	data, err := obj.Marshal()
	if err != nil {
		t.Fatal(err)
	}
	out := &v1.Pod{}
	if err := out.Unmarshal(data); err != nil {
		t.Fatal(err)
	}
	if !api.Semantic.Equalities.DeepEqual(out, obj) {
		t.Logf("marshal\n%s", hex.Dump(data))
		t.Fatalf("Unmarshal is unequal\n%s", diff.ObjectGoPrintSideBySide(out, obj))
	}
}

func BenchmarkEncodeProtobufGeneratedMarshal(b *testing.B) {
	items := benchmarkItems()
	width := len(items)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if _, err := items[i%width].Marshal(); err != nil {
			b.Fatal(err)
		}
	}
	b.StopTimer()
}

// BenchmarkDecodeJSON provides a baseline for regular JSON decode performance
func BenchmarkDecodeIntoProtobuf(b *testing.B) {
	items := benchmarkItems()
	width := len(items)
	encoded := make([][]byte, width)
	for i := range items {
		data, err := (&items[i]).Marshal()
		if err != nil {
			b.Fatal(err)
		}
		encoded[i] = data
		validate := &v1.Pod{}
		if err := proto.Unmarshal(data, validate); err != nil {
			b.Fatalf("Failed to unmarshal %d: %v\n%#v", i, err, items[i])
		}
	}

	for i := 0; i < b.N; i++ {
		obj := v1.Pod{}
		if err := proto.Unmarshal(encoded[i%width], &obj); err != nil {
			b.Fatal(err)
		}
	}
}
<commit_msg>Add an experimental protobuf serializer<commit_after>// +build proto

/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package api_test

import (
	"encoding/hex"
	"math/rand"
	"testing"

	"github.com/gogo/protobuf/proto"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/testapi"
	apitesting "k8s.io/kubernetes/pkg/api/testing"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/api/v1"
	_ "k8s.io/kubernetes/pkg/apis/extensions"
	_ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/serializer/protobuf"
	"k8s.io/kubernetes/pkg/util/diff"
)

func init() {
	codecsToTest = append(codecsToTest, func(version unversioned.GroupVersion, item runtime.Object) (runtime.Codec, error) {
		s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme))
		return api.Codecs.CodecForVersions(s, testapi.ExternalGroupVersions(), nil), nil
	})
}

func TestProtobufRoundTrip(t *testing.T) {
	obj := &v1.Pod{}
	apitesting.FuzzerFor(t, v1.SchemeGroupVersion, rand.NewSource(benchmarkSeed)).Fuzz(obj)
	data, err := obj.Marshal()
	if err != nil {
		t.Fatal(err)
	}
	out := &v1.Pod{}
	if err := out.Unmarshal(data); err != nil {
		t.Fatal(err)
	}
	if !api.Semantic.Equalities.DeepEqual(out, obj) {
		t.Logf("marshal\n%s", hex.Dump(data))
		t.Fatalf("Unmarshal is unequal\n%s", diff.ObjectGoPrintSideBySide(out, obj))
	}
}

// BenchmarkEncodeCodec measures the cost of performing a codec encode, which includes
// reflection (to clear APIVersion and Kind)
func BenchmarkEncodeCodecProtobuf(b *testing.B) {
	items := benchmarkItems()
	width := len(items)
	s := protobuf.NewSerializer(nil, nil)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if _, err := runtime.Encode(s, &items[i%width]); err != nil {
			b.Fatal(err)
		}
	}
	b.StopTimer()
}

// BenchmarkEncodeCodecFromInternalProtobuf measures the cost of performing a codec encode,
// including conversions and any type setting. This is a "full" encode.
func BenchmarkEncodeCodecFromInternalProtobuf(b *testing.B) {
	items := benchmarkItems()
	width := len(items)
	encodable := make([]api.Pod, width)
	for i := range items {
		if err := api.Scheme.Convert(&items[i], &encodable[i]); err != nil {
			b.Fatal(err)
		}
	}
	s := protobuf.NewSerializer(nil, nil)
	codec := api.Codecs.EncoderForVersion(s, v1.SchemeGroupVersion)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if _, err := runtime.Encode(codec, &encodable[i%width]); err != nil {
			b.Fatal(err)
		}
	}
	b.StopTimer()
}

func BenchmarkEncodeProtobufGeneratedMarshal(b *testing.B) {
	items := benchmarkItems()
	width := len(items)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if _, err := items[i%width].Marshal(); err != nil {
			b.Fatal(err)
		}
	}
	b.StopTimer()
}

// BenchmarkDecodeJSON provides a baseline for regular JSON decode performance
func BenchmarkDecodeIntoProtobuf(b *testing.B) {
	items := benchmarkItems()
	width := len(items)
	encoded := make([][]byte, width)
	for i := range items {
		data, err := (&items[i]).Marshal()
		if err != nil {
			b.Fatal(err)
		}
		encoded[i] = data
		validate := &v1.Pod{}
		if err := proto.Unmarshal(data, validate); err != nil {
			b.Fatalf("Failed to unmarshal %d: %v\n%#v", i, err, items[i])
		}
	}

	for i := 0; i < b.N; i++ {
		obj := v1.Pod{}
		if err := proto.Unmarshal(encoded[i%width], &obj); err != nil {
			b.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package diagnostics

import (
	"fmt"

	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/sets"

	clientdiags "github.com/openshift/origin/pkg/diagnostics/client"
	"github.com/openshift/origin/pkg/diagnostics/types"
)

var (
	// availableClientDiagnostics contains the names of client diagnostics that can be executed
	// during a single run of diagnostics. Add more diagnostics to the list as they are defined.
	availableClientDiagnostics = sets.NewString(clientdiags.ConfigContextsName, clientdiags.DiagnosticPodName)
)

// buildClientDiagnostics builds client Diagnostic objects based on the rawConfig passed in.
// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.) {
func (o DiagnosticsOptions) buildClientDiagnostics(rawConfig *clientcmdapi.Config) ([]types.Diagnostic, bool, error) {
	available := availableClientDiagnostics

	// osClient, kubeClient, clientErr := o.Factory.Clients() // use with a diagnostic that needs OpenShift/Kube client
	_, kubeClient, clientErr := o.Factory.Clients()
	if clientErr != nil {
		o.Logger.Notice("CED0001", "Could not configure a client, so client diagnostics are limited to testing configuration and connection")
		available = sets.NewString(clientdiags.ConfigContextsName)
	}

	diagnostics := []types.Diagnostic{}
	requestedDiagnostics := available.Intersection(sets.NewString(o.RequestedDiagnostics...)).List()
	for _, diagnosticName := range requestedDiagnostics {
		switch diagnosticName {
		case clientdiags.ConfigContextsName:
			seen := map[string]bool{}
			for contextName := range rawConfig.Contexts {
				diagnostic := clientdiags.ConfigContext{RawConfig: rawConfig, ContextName: contextName}
				if clusterUser, defined := diagnostic.ContextClusterUser(); !defined {
					// definitely want to diagnose the broken context
					diagnostics = append(diagnostics, diagnostic)
				} else if !seen[clusterUser] {
					seen[clusterUser] = true // avoid validating same user for multiple projects
					diagnostics = append(diagnostics, diagnostic)
				}
			}
		case clientdiags.DiagnosticPodName:
			diagnostics = append(diagnostics, &clientdiags.DiagnosticPod{
				KubeClient:          *kubeClient,
				Namespace:           rawConfig.Contexts[rawConfig.CurrentContext].Namespace,
				Level:               o.LogOptions.Level,
				Factory:             o.Factory,
				PreventModification: o.PreventModification,
				ImageTemplate:       o.ImageTemplate,
			})
		default:
			return nil, false, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
		}
	}
	return diagnostics, true, clientErr
}
<commit_msg>Expose network diagnostics via 'oadm diagnostics NetworkCheck'<commit_after>package diagnostics

import (
	"fmt"

	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/sets"

	clientdiags "github.com/openshift/origin/pkg/diagnostics/client"
	networkdiags "github.com/openshift/origin/pkg/diagnostics/network"
	"github.com/openshift/origin/pkg/diagnostics/types"
)

var (
	// availableClientDiagnostics contains the names of client diagnostics that can be executed
	// during a single run of diagnostics. Add more diagnostics to the list as they are defined.
	availableClientDiagnostics = sets.NewString(clientdiags.ConfigContextsName, clientdiags.DiagnosticPodName, networkdiags.NetworkDiagnosticName)
)

// buildClientDiagnostics builds client Diagnostic objects based on the rawConfig passed in.
// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.) {
func (o DiagnosticsOptions) buildClientDiagnostics(rawConfig *clientcmdapi.Config) ([]types.Diagnostic, bool, error) {
	available := availableClientDiagnostics

	osClient, kubeClient, clientErr := o.Factory.Clients()
	if clientErr != nil {
		o.Logger.Notice("CED0001", "Could not configure a client, so client diagnostics are limited to testing configuration and connection")
		available = sets.NewString(clientdiags.ConfigContextsName)
	}

	diagnostics := []types.Diagnostic{}
	requestedDiagnostics := available.Intersection(sets.NewString(o.RequestedDiagnostics...)).List()
	for _, diagnosticName := range requestedDiagnostics {
		switch diagnosticName {
		case clientdiags.ConfigContextsName:
			seen := map[string]bool{}
			for contextName := range rawConfig.Contexts {
				diagnostic := clientdiags.ConfigContext{RawConfig: rawConfig, ContextName: contextName}
				if clusterUser, defined := diagnostic.ContextClusterUser(); !defined {
					// definitely want to diagnose the broken context
					diagnostics = append(diagnostics, diagnostic)
				} else if !seen[clusterUser] {
					seen[clusterUser] = true // avoid validating same user for multiple projects
					diagnostics = append(diagnostics, diagnostic)
				}
			}
		case clientdiags.DiagnosticPodName:
			diagnostics = append(diagnostics, &clientdiags.DiagnosticPod{
				KubeClient:          *kubeClient,
				Namespace:           rawConfig.Contexts[rawConfig.CurrentContext].Namespace,
				Level:               o.LogOptions.Level,
				Factory:             o.Factory,
				PreventModification: o.PreventModification,
				ImageTemplate:       o.ImageTemplate,
			})
		case networkdiags.NetworkDiagnosticName:
			diagnostics = append(diagnostics, &networkdiags.NetworkDiagnostic{
				KubeClient:          kubeClient,
				OSClient:            osClient,
				ClientFlags:         o.ClientFlags,
				Level:               o.LogOptions.Level,
				Factory:             o.Factory,
				PreventModification: o.PreventModification,
				LogDir:              o.NetworkDiagLogDir,
			})
		default:
			return nil, false, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
		}
	}
	return diagnostics, true, clientErr
}
<|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 kustomize

import (
	"testing"
)

func TestValidate(t *testing.T) {
	var cases = []struct {
		name  string
		args  []string
		path  string
		erMsg string
	}{
		{"noargs", []string{}, "./", ""},
		{"file", []string{"beans"}, "beans", ""},
		{"path", []string{"a/b/c"}, "a/b/c", ""},
		{"path", []string{"too", "many"},
			"", "specify one path to a kustomization directory"},
	}
	for _, mycase := range cases {
		opts := kustomizeOptions{}
		e := opts.Validate(mycase.args)
		if len(mycase.erMsg) > 0 {
			if e == nil {
				t.Errorf("%s: Expected an error %v", mycase.name, mycase.erMsg)
			}
			if e.Error() != mycase.erMsg {
				t.Errorf("%s: Expected error %s, but got %v", mycase.name, mycase.erMsg, e)
			}
			continue
		}
		if e != nil {
			t.Errorf("%s: unknown error %v", mycase.name, e)
			continue
		}
		if opts.kustomizationDir != mycase.path {
			t.Errorf("%s: expected path '%s', got '%s'", mycase.name, mycase.path, opts.kustomizationDir)
		}
	}
}
<commit_msg>Delete staging/src/k8s.io/cli-runtime/pkg/kustomize<commit_after><|endoftext|>


<commit_before>/*
Package eventstore provides mongo implementation of domain event store
*/
package eventstore

import (
	"context"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"github.com/vardius/go-api-boilerplate/pkg/domain"
	apperrors "github.com/vardius/go-api-boilerplate/pkg/errors"
	baseeventstore "github.com/vardius/go-api-boilerplate/pkg/eventstore"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type eventStore struct {
	collection *mongo.Collection
}

// New creates new mongo event store
func New(ctx context.Context, collectionName string, mongoDB *mongo.Database) (baseeventstore.EventStore, error) {
	if collectionName == "" {
		collectionName = "events"
	}

	collection := mongoDB.Collection(collectionName)

	if _, err := collection.Indexes().CreateMany(ctx, []mongo.IndexModel{
		{
			Keys:    bson.D{{Key: "event_id", Value: -1}},
			Options: options.Index().SetUnique(true),
		},
		{Keys: bson.D{{Key: "stream_id", Value: -1}}},
		{Keys: bson.D{{Key: "occurred_at", Value: 1}}},
		{Keys: bson.D{
			{Key: "stream_id", Value: 1},
			{Key: "stream_name", Value: 1},
			{Key: "event_type", Value: 1},
			{Key: "occurred_at", Value: 1},
		}},
	}); err != nil {
		return nil, fmt.Errorf("failed to create indexes: %w", err)
	}

	return &eventStore{collection: collection}, nil
}

func (s *eventStore) Store(ctx context.Context, events []domain.Event) error {
	if len(events) == 0 {
		return nil
	}

	var buffer []mongo.WriteModel
	for _, e := range events {
		upsert := mongo.NewInsertOneModel()
		upsert.SetDocument(bson.M{"$set": e})

		buffer = append(buffer, upsert)
	}

	opts := options.BulkWrite()
	opts.SetOrdered(true)

	const chunkSize = 500

	for i := 0; i < len(buffer); i += chunkSize {
		end := i + chunkSize

		if end > len(buffer) {
			end = len(buffer)
		}

		if _, err := s.collection.BulkWrite(ctx, buffer[i:end], opts); err != nil {
			return err
		}
	}

	return nil
}

func (s *eventStore) Get(ctx context.Context, id uuid.UUID) (domain.Event, error) {
	filter := bson.M{
		"event_id": id.String(),
	}

	var result domain.Event
	if err := s.collection.FindOne(ctx, filter).Decode(&result); err != nil {
		if errors.Is(err, mongo.ErrNoDocuments) {
			return domain.NullEvent, apperrors.Wrap(fmt.Errorf("%s: %w", err, baseeventstore.ErrEventNotFound))
		}

		return domain.NullEvent, apperrors.Wrap(err)
	}

	return result, nil
}

func (s *eventStore) FindAll(ctx context.Context) ([]domain.Event, error) {
	filter := bson.M{}
	findOptions := options.FindOptions{
		Sort: bson.D{
			primitive.E{Key: "occurred_at", Value: 1},
		},
	}

	cur, err := s.collection.Find(ctx, filter, &findOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to query events: %w", err)
	}
	defer cur.Close(ctx)

	var result []domain.Event
	for cur.Next(ctx) {
		var event domain.Event
		if err := cur.Decode(&event); err != nil {
			return nil, fmt.Errorf("failed to decode event: %w", err)
		}

		result = append(result, event)
	}

	return result, nil
}

func (s *eventStore) GetStream(ctx context.Context, streamID uuid.UUID, streamName string) ([]domain.Event, error) {
	filter := bson.M{
		"stream_id":   streamID.String(),
		"stream_name": streamName,
	}
	findOptions := options.FindOptions{
		Sort: bson.D{
			primitive.E{Key: "occurred_at", Value: 1},
		},
	}

	cur, err := s.collection.Find(ctx, filter, &findOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to query events: %w", err)
	}
	defer cur.Close(ctx)

	var result []domain.Event
	for cur.Next(ctx) {
		var event domain.Event
		if err := cur.Decode(&event); err != nil {
			return nil, fmt.Errorf("failed to decode event: %w", err)
		}

		result = append(result, event)
	}

	return result, nil
}

func (s *eventStore) GetStreamEventsByType(ctx context.Context, streamID uuid.UUID, streamName, eventType string) ([]domain.Event, error) {
	filter := bson.M{
		"stream_id":   streamID.String(),
		"stream_name": streamName,
		"event_type":  eventType,
	}
	findOptions := options.FindOptions{
		Sort: bson.D{
			primitive.E{Key: "occurred_at", Value: 1},
		},
	}

	cur, err := s.collection.Find(ctx, filter, &findOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to query events: %w", err)
	}
	defer cur.Close(ctx)

	var result []domain.Event
	for cur.Next(ctx) {
		var event domain.Event
		if err := cur.Decode(&event); err != nil {
			return nil, fmt.Errorf("failed to decode event: %w", err)
		}

		result = append(result, event)
	}

	return result, nil
}
<commit_msg>Fix bulk insert<commit_after>/*
Package eventstore provides mongo implementation of domain event store
*/
package eventstore

import (
	"context"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"github.com/vardius/go-api-boilerplate/pkg/domain"
	apperrors "github.com/vardius/go-api-boilerplate/pkg/errors"
	baseeventstore "github.com/vardius/go-api-boilerplate/pkg/eventstore"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type eventStore struct {
	collection *mongo.Collection
}

// New creates new mongo event store
func New(ctx context.Context, collectionName string, mongoDB *mongo.Database) (baseeventstore.EventStore, error) {
	if collectionName == "" {
		collectionName = "events"
	}

	collection := mongoDB.Collection(collectionName)

	if _, err := collection.Indexes().CreateMany(ctx, []mongo.IndexModel{
		{
			Keys:    bson.D{{Key: "event_id", Value: -1}},
			Options: options.Index().SetUnique(true),
		},
		{Keys: bson.D{{Key: "stream_id", Value: -1}}},
		{Keys: bson.D{{Key: "occurred_at", Value: 1}}},
		{Keys: bson.D{
			{Key: "stream_id", Value: 1},
			{Key: "stream_name", Value: 1},
			{Key: "event_type", Value: 1},
			{Key: "occurred_at", Value: 1},
		}},
	}); err != nil {
		return nil, fmt.Errorf("failed to create indexes: %w", err)
	}

	return &eventStore{collection: collection}, nil
}

func (s *eventStore) Store(ctx context.Context, events []domain.Event) error {
	if len(events) == 0 {
		return nil
	}

	var buffer []mongo.WriteModel
	for _, e := range events {
		upsert := mongo.NewInsertOneModel()
		upsert.SetDocument(e)

		buffer = append(buffer, upsert)
	}

	opts := options.BulkWrite()
	opts.SetOrdered(true)

	const chunkSize = 500

	for i := 0; i < len(buffer); i += chunkSize {
		end := i + chunkSize

		if end > len(buffer) {
			end = len(buffer)
		}

		if _, err := s.collection.BulkWrite(ctx, buffer[i:end], opts); err != nil {
			return err
		}
	}

	return nil
}

func (s *eventStore) Get(ctx context.Context, id uuid.UUID) (domain.Event, error) {
	filter := bson.M{
		"event_id": id.String(),
	}

	var result domain.Event
	if err := s.collection.FindOne(ctx, filter).Decode(&result); err != nil {
		if errors.Is(err, mongo.ErrNoDocuments) {
			return domain.NullEvent, apperrors.Wrap(fmt.Errorf("%s: %w", err, baseeventstore.ErrEventNotFound))
		}

		return domain.NullEvent, apperrors.Wrap(err)
	}

	return result, nil
}

func (s *eventStore) FindAll(ctx context.Context) ([]domain.Event, error) {
	filter := bson.M{}
	findOptions := options.FindOptions{
		Sort: bson.D{
			primitive.E{Key: "occurred_at", Value: 1},
		},
	}

	cur, err := s.collection.Find(ctx, filter, &findOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to query events: %w", err)
	}
	defer cur.Close(ctx)

	var result []domain.Event
	for cur.Next(ctx) {
		var event domain.Event
		if err := cur.Decode(&event); err != nil {
			return nil, fmt.Errorf("failed to decode event: %w", err)
		}

		result = append(result, event)
	}

	return result, nil
}

func (s *eventStore) GetStream(ctx context.Context, streamID uuid.UUID, streamName string) ([]domain.Event, error) {
	filter := bson.M{
		"stream_id":   streamID.String(),
		"stream_name": streamName,
	}
	findOptions := options.FindOptions{
		Sort: bson.D{
			primitive.E{Key: "occurred_at", Value: 1},
		},
	}

	cur, err := s.collection.Find(ctx, filter, &findOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to query events: %w", err)
	}
	defer cur.Close(ctx)

	var result []domain.Event
	for cur.Next(ctx) {
		var event domain.Event
		if err := cur.Decode(&event); err != nil {
			return nil, fmt.Errorf("failed to decode event: %w", err)
		}

		result = append(result, event)
	}

	return result, nil
}

func (s *eventStore) GetStreamEventsByType(ctx context.Context, streamID uuid.UUID, streamName, eventType string) ([]domain.Event, error) {
	filter := bson.M{
		"stream_id":   streamID.String(),
		"stream_name": streamName,
		"event_type":  eventType,
	}
	findOptions := options.FindOptions{
		Sort: bson.D{
			primitive.E{Key: "occurred_at", Value: 1},
		},
	}

	cur, err := s.collection.Find(ctx, filter, &findOptions)
	if err != nil {
		return nil, fmt.Errorf("failed to query events: %w", err)
	}
	defer cur.Close(ctx)

	var result []domain.Event
	for cur.Next(ctx) {
		var event domain.Event
		if err := cur.Decode(&event); err != nil {
			return nil, fmt.Errorf("failed to decode event: %w", err)
		}

		result = append(result, event)
	}

	return result, nil
}
<|endoftext|>


<commit_before>package sqlstore

import (
	"testing"

	"github.com/grafana/grafana/pkg/components/simplejson"
	m "github.com/grafana/grafana/pkg/models"
	. "github.com/smartystreets/goconvey/convey"
)

func TestAlertingDataAccess(t *testing.T) {
	Convey("Testing Alerting data access", t, func() {
		InitTestDB(t)

		testDash := insertTestDashboard("dashboard with alerts", 1, "alert")

		items := []*m.Alert{
			{
				PanelId:     1,
				DashboardId: testDash.Id,
				OrgId:       testDash.OrgId,
				Name:        "Alerting title",
				Message:     "Alerting message",
				Settings:    simplejson.New(),
				Frequency:   1,
			},
		}

		cmd := m.SaveAlertsCommand{
			Alerts:      items,
			DashboardId: testDash.Id,
			OrgId:       1,
			UserId:      1,
		}

		err := SaveAlerts(&cmd)

		Convey("Can create one alert", func() {
			So(err, ShouldBeNil)
		})

		Convey("Can read properties", func() {
			alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1}
			err2 := HandleAlertsQuery(&alertQuery)

			alert := alertQuery.Result[0]
			So(err2, ShouldBeNil)
			So(alert.Name, ShouldEqual, "Alerting title")
			So(alert.Message, ShouldEqual, "Alerting message")
			So(alert.State, ShouldEqual, "pending")
			So(alert.Frequency, ShouldEqual, 1)
		})

		Convey("Alerts with same dashboard id and panel id should update", func() {
			modifiedItems := items
			modifiedItems[0].Name = "Name"

			modifiedCmd := m.SaveAlertsCommand{
				DashboardId: testDash.Id,
				OrgId:       1,
				UserId:      1,
				Alerts:      modifiedItems,
			}

			err := SaveAlerts(&modifiedCmd)

			Convey("Can save alerts with same dashboard and panel id", func() {
				So(err, ShouldBeNil)
			})

			Convey("Alerts should be updated", func() {
				query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
				err2 := HandleAlertsQuery(&query)

				So(err2, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 1)
				So(query.Result[0].Name, ShouldEqual, "Name")

				Convey("Alert state should not be updated", func() {
					So(query.Result[0].State, ShouldEqual, "pending")
				})
			})

			Convey("Updates without changes should be ignored", func() {
				err3 := SaveAlerts(&modifiedCmd)
				So(err3, ShouldBeNil)
			})
		})

		Convey("Multiple alerts per dashboard", func() {
			multipleItems := []*m.Alert{
				{
					DashboardId: testDash.Id,
					PanelId:     1,
					Name:        "1",
					OrgId:       1,
					Settings:    simplejson.New(),
				},
				{
					DashboardId: testDash.Id,
					PanelId:     2,
					Name:        "2",
					OrgId:       1,
					Settings:    simplejson.New(),
				},
				{
					DashboardId: testDash.Id,
					PanelId:     3,
					Name:        "3",
					OrgId:       1,
					Settings:    simplejson.New(),
				},
			}

			cmd.Alerts = multipleItems
			err = SaveAlerts(&cmd)

			Convey("Should save 3 dashboards", func() {
				So(err, ShouldBeNil)

				queryForDashboard := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
				err2 := HandleAlertsQuery(&queryForDashboard)

				So(err2, ShouldBeNil)
				So(len(queryForDashboard.Result), ShouldEqual, 3)
			})

			Convey("should updated two dashboards and delete one", func() {
				missingOneAlert := multipleItems[:2]

				cmd.Alerts = missingOneAlert
				err = SaveAlerts(&cmd)

				Convey("should delete the missing alert", func() {
					query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
					err2 := HandleAlertsQuery(&query)
					So(err2, ShouldBeNil)
					So(len(query.Result), ShouldEqual, 2)
				})
			})
		})

		Convey("When dashboard is removed", func() {
			items := []*m.Alert{
				{
					PanelId:     1,
					DashboardId: testDash.Id,
					Name:        "Alerting title",
					Message:     "Alerting message",
				},
			}

			cmd := m.SaveAlertsCommand{
				Alerts:      items,
				DashboardId: testDash.Id,
				OrgId:       1,
				UserId:      1,
			}

			SaveAlerts(&cmd)

			err = DeleteDashboard(&m.DeleteDashboardCommand{
				OrgId: 1,
				Slug:  testDash.Slug,
			})

			So(err, ShouldBeNil)

			Convey("Alerts should be removed", func() {
				query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
				err2 := HandleAlertsQuery(&query)

				So(testDash.Id, ShouldEqual, 1)
				So(err2, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 0)
			})
		})
	})
}
<commit_msg>test(alerting): fixes broken unit tests<commit_after>package sqlstore

import (
	"testing"

	"github.com/grafana/grafana/pkg/components/simplejson"
	m "github.com/grafana/grafana/pkg/models"
	. "github.com/smartystreets/goconvey/convey"
)

func TestAlertingDataAccess(t *testing.T) {
	Convey("Testing Alerting data access", t, func() {
		InitTestDB(t)

		testDash := insertTestDashboard("dashboard with alerts", 1, "alert")

		items := []*m.Alert{
			{
				PanelId:     1,
				DashboardId: testDash.Id,
				OrgId:       testDash.OrgId,
				Name:        "Alerting title",
				Message:     "Alerting message",
				Settings:    simplejson.New(),
				Frequency:   1,
			},
		}

		cmd := m.SaveAlertsCommand{
			Alerts:      items,
			DashboardId: testDash.Id,
			OrgId:       1,
			UserId:      1,
		}

		err := SaveAlerts(&cmd)

		Convey("Can create one alert", func() {
			So(err, ShouldBeNil)
		})

		Convey("Can read properties", func() {
			alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1}
			err2 := HandleAlertsQuery(&alertQuery)

			alert := alertQuery.Result[0]
			So(err2, ShouldBeNil)
			So(alert.Name, ShouldEqual, "Alerting title")
			So(alert.Message, ShouldEqual, "Alerting message")
			So(alert.State, ShouldEqual, "unknown")
			So(alert.Frequency, ShouldEqual, 1)
		})

		Convey("Alerts with same dashboard id and panel id should update", func() {
			modifiedItems := items
			modifiedItems[0].Name = "Name"

			modifiedCmd := m.SaveAlertsCommand{
				DashboardId: testDash.Id,
				OrgId:       1,
				UserId:      1,
				Alerts:      modifiedItems,
			}

			err := SaveAlerts(&modifiedCmd)

			Convey("Can save alerts with same dashboard and panel id", func() {
				So(err, ShouldBeNil)
			})

			Convey("Alerts should be updated", func() {
				query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
				err2 := HandleAlertsQuery(&query)

				So(err2, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 1)
				So(query.Result[0].Name, ShouldEqual, "Name")

				Convey("Alert state should not be updated", func() {
					So(query.Result[0].State, ShouldEqual, "unknown")
				})
			})

			Convey("Updates without changes should be ignored", func() {
				err3 := SaveAlerts(&modifiedCmd)
				So(err3, ShouldBeNil)
			})
		})

		Convey("Multiple alerts per dashboard", func() {
			multipleItems := []*m.Alert{
				{
					DashboardId: testDash.Id,
					PanelId:     1,
					Name:        "1",
					OrgId:       1,
					Settings:    simplejson.New(),
				},
				{
					DashboardId: testDash.Id,
					PanelId:     2,
					Name:        "2",
					OrgId:       1,
					Settings:    simplejson.New(),
				},
				{
					DashboardId: testDash.Id,
					PanelId:     3,
					Name:        "3",
					OrgId:       1,
					Settings:    simplejson.New(),
				},
			}

			cmd.Alerts = multipleItems
			err = SaveAlerts(&cmd)

			Convey("Should save 3 dashboards", func() {
				So(err, ShouldBeNil)

				queryForDashboard := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
				err2 := HandleAlertsQuery(&queryForDashboard)

				So(err2, ShouldBeNil)
				So(len(queryForDashboard.Result), ShouldEqual, 3)
			})

			Convey("should updated two dashboards and delete one", func() {
				missingOneAlert := multipleItems[:2]

				cmd.Alerts = missingOneAlert
				err = SaveAlerts(&cmd)

				Convey("should delete the missing alert", func() {
					query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
					err2 := HandleAlertsQuery(&query)
					So(err2, ShouldBeNil)
					So(len(query.Result), ShouldEqual, 2)
				})
			})
		})

		Convey("When dashboard is removed", func() {
			items := []*m.Alert{
				{
					PanelId:     1,
					DashboardId: testDash.Id,
					Name:        "Alerting title",
					Message:     "Alerting message",
				},
			}

			cmd := m.SaveAlertsCommand{
				Alerts:      items,
				DashboardId: testDash.Id,
				OrgId:       1,
				UserId:      1,
			}

			SaveAlerts(&cmd)

			err = DeleteDashboard(&m.DeleteDashboardCommand{
				OrgId: 1,
				Slug:  testDash.Slug,
			})

			So(err, ShouldBeNil)

			Convey("Alerts should be removed", func() {
				query := m.GetAlertsQuery{DashboardId: testDash.Id, OrgId: 1}
				err2 := HandleAlertsQuery(&query)

				So(testDash.Id, ShouldEqual, 1)
				So(err2, ShouldBeNil)
				So(len(query.Result), ShouldEqual, 0)
			})
		})
	})
}
<|endoftext|>


<commit_before>package sessions

import (
	"context"
	"encoding/json"
	"sync"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/utils"
)

var log = logger.WithNamespace("sessions")

const redisKey = "registration-logins"

type registrationEntry struct {
	Domain       string
	ClientID     string
	LoginEntryID string
	Expire       time.Time
}

func (r *registrationEntry) Key() string {
	return r.Domain + "|" + r.ClientID
}

var (
	registrationExpirationDuration = 5 * time.Minute

	registrationsMap     map[string]registrationEntry
	registrationsMapLock sync.Mutex
)

// SweepLoginRegistrations starts the login registration process.
//
// This process involving a queue of registration login entries is necessary to
// distinguish "normal" logins from logins to give right to an OAuth
// application.
//
// Since we cannot really distinguish between them other than trusting the
// user, we send a notification to the user by following this process:
//   - if we identify a login for a device registration — by looking at the
//     redirection address — we push an entry onto the queu
//   - if we do not receive the activation of the device by the user in 5
//     minutes, we send a notification for a "normal" login
//   - otherwise we send a notification for the activation of a new device.
func SweepLoginRegistrations() utils.Shutdowner {
	closed := make(chan struct{})
	go func() {
		waitDuration := registrationExpirationDuration / 2
		for {
			select {
			case <-time.After(waitDuration):
				var err error
				waitDuration, err = sweepRegistrations()
				if err != nil {
					log.Errorf("Could not sweep registration queue: %s", err)
				}
				if waitDuration <= 0 {
					waitDuration = registrationExpirationDuration
				}
			case <-closed:
				return
			}
		}
	}()
	return &sweeper{closed}
}

type sweeper struct {
	closed chan struct{}
}

func (s *sweeper) Shutdown(ctx context.Context) error {
	select {
	case s.closed <- struct{}{}:
	case <-ctx.Done():
	}
	return nil
}

// PushLoginRegistration pushes a new login into the registration queue.
func PushLoginRegistration(domain string, login *LoginEntry) error {
	entry := registrationEntry{
		Domain:       domain,
		ClientID:     login.ClientID,
		LoginEntryID: login.ID(),
		Expire:       time.Now(),
	}

	if cli := config.GetConfig().SessionStorage.Client(); cli != nil {
		b, err := json.Marshal(entry)
		if err != nil {
			return err
		}
		return cli.HSet(redisKey, entry.Key(), b).Err()
	}

	registrationsMapLock.Lock()
	registrationsMap[entry.Key()] = entry
	registrationsMapLock.Unlock()
	return nil
}

// RemoveLoginRegistration removes a login from the registration map.
func RemoveLoginRegistration(domain, clientID string) error {
	key := domain + "|" + clientID
	if cli := config.GetConfig().SessionStorage.Client(); cli != nil {
		return cli.HDel(redisKey, key).Err()
	}

	var entryPtr *registrationEntry
	{
		registrationsMapLock.Lock()
		entry, ok := registrationsMap[key]
		if ok {
			delete(registrationsMap, key)
			entryPtr = &entry
		}
		registrationsMapLock.Unlock()
	}
	if entryPtr != nil {
		sendRegistrationNotification(entryPtr, true)
	}

	return nil
}

func sweepRegistrations() (waitDuration time.Duration, err error) {
	var expiredLogins []registrationEntry

	now := time.Now()
	if cli := config.GetConfig().SessionStorage.Client(); cli != nil {
		var vals map[string]string
		vals, err = cli.HGetAll(redisKey).Result()
		if err != nil {
			return
		}

		var deletedKeys []string
		for key, data := range vals {
			var entry registrationEntry
			if err = json.Unmarshal([]byte(data), &entry); err != nil {
				deletedKeys = append(deletedKeys, key)
				continue
			}
			diff := entry.Expire.Sub(now)
			if diff < -24*time.Hour {
				// skip too old entries
				deletedKeys = append(deletedKeys, entry.Key())
			} else if diff <= 10*time.Second {
				expiredLogins = append(expiredLogins, entry)
				deletedKeys = append(deletedKeys, entry.Key())
			} else if waitDuration == 0 || waitDuration > diff {
				waitDuration = diff
			}
		}

		if len(deletedKeys) > 0 {
			err = cli.HDel(redisKey, deletedKeys...).Err()
		}
	} else {
		registrationsMapLock.Lock()

		var deletedKeys []string
		for _, entry := range registrationsMap {
			diff := entry.Expire.Sub(now)
			if diff < -24*time.Hour {
				// skip too old entries
				deletedKeys = append(deletedKeys, entry.Key())
			} else if diff <= 10*time.Second {
				expiredLogins = append(expiredLogins, entry)
				deletedKeys = append(deletedKeys, entry.Key())
			} else if waitDuration == 0 || waitDuration > diff {
				waitDuration = diff
			}
		}

		for _, key := range deletedKeys {
			delete(registrationsMap, key)
		}

		registrationsMapLock.Unlock()
	}

	if len(expiredLogins) > 0 {
		sendExpiredRegistrationNotifications(expiredLogins)
	}

	return
}

func sendRegistrationNotification(entry *registrationEntry, registrationNotification bool) error {
	var login LoginEntry
	i, err := instance.Get(entry.Domain)
	if err != nil {
		return err
	}
	err = couchdb.GetDoc(i, consts.SessionsLogins, entry.LoginEntryID, &login)
	if err != nil {
		return err
	}
	return sendLoginNotification(i, &login, registrationNotification)
}

func sendExpiredRegistrationNotifications(entries []registrationEntry) {
	for _, entry := range entries {
		sendRegistrationNotification(&entry, false)
	}
}
<commit_msg>Send notification on removing entry from redis<commit_after>package sessions

import (
	"context"
	"encoding/json"
	"sync"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/utils"
)

var log = logger.WithNamespace("sessions")

const redisRegistationKey = "registration-logins"

type registrationEntry struct {
	Domain       string
	ClientID     string
	LoginEntryID string
	Expire       time.Time
}

func (r *registrationEntry) Key() string {
	return r.Domain + "|" + r.ClientID
}

var (
	registrationExpirationDuration = 5 * time.Minute

	registrationsMap     map[string]registrationEntry
	registrationsMapLock sync.Mutex
)

// SweepLoginRegistrations starts the login registration process.
//
// This process involving a queue of registration login entries is necessary to
// distinguish "normal" logins from logins to give right to an OAuth
// application.
//
// Since we cannot really distinguish between them other than trusting the
// user, we send a notification to the user by following this process:
//   - if we identify a login for a device registration — by looking at the
//     redirection address — we push an entry onto the queu
//   - if we do not receive the activation of the device by the user in 5
//     minutes, we send a notification for a "normal" login
//   - otherwise we send a notification for the activation of a new device.
func SweepLoginRegistrations() utils.Shutdowner {
	closed := make(chan struct{})
	go func() {
		waitDuration := registrationExpirationDuration / 2
		for {
			select {
			case <-time.After(waitDuration):
				var err error
				waitDuration, err = sweepRegistrations()
				if err != nil {
					log.Errorf("Could not sweep registration queue: %s", err)
				}
				if waitDuration <= 0 {
					waitDuration = registrationExpirationDuration
				}
			case <-closed:
				return
			}
		}
	}()
	return &sweeper{closed}
}

type sweeper struct {
	closed chan struct{}
}

func (s *sweeper) Shutdown(ctx context.Context) error {
	select {
	case s.closed <- struct{}{}:
	case <-ctx.Done():
	}
	return nil
}

// PushLoginRegistration pushes a new login into the registration queue.
func PushLoginRegistration(domain string, login *LoginEntry) error {
	entry := registrationEntry{
		Domain:       domain,
		ClientID:     login.ClientID,
		LoginEntryID: login.ID(),
		Expire:       time.Now(),
	}

	if cli := config.GetConfig().SessionStorage.Client(); cli != nil {
		b, err := json.Marshal(entry)
		if err != nil {
			return err
		}
		return cli.HSet(redisRegistationKey, entry.Key(), b).Err()
	}

	registrationsMapLock.Lock()
	registrationsMap[entry.Key()] = entry
	registrationsMapLock.Unlock()
	return nil
}

// RemoveLoginRegistration removes a login from the registration map.
func RemoveLoginRegistration(domain, clientID string) error {
	var entryPtr *registrationEntry
	key := domain + "|" + clientID
	if cli := config.GetConfig().SessionStorage.Client(); cli != nil {
		b, err := cli.HGet(redisRegistationKey, key).Result()
		if err != nil {
			return err
		}
		var entry registrationEntry
		if err = json.Unmarshal([]byte(b), &entry); err != nil {
			return err
		}
		if err = cli.HDel(redisRegistationKey, key).Err(); err != nil {
			return err
		}
		entryPtr = &entry
	} else {
		registrationsMapLock.Lock()
		entry, ok := registrationsMap[key]
		if ok {
			delete(registrationsMap, key)
			entryPtr = &entry
		}
		registrationsMapLock.Unlock()
	}
	if entryPtr != nil {
		sendRegistrationNotification(entryPtr, true)
	}
	return nil
}

func sweepRegistrations() (waitDuration time.Duration, err error) {
	var expiredLogins []registrationEntry

	now := time.Now()
	if cli := config.GetConfig().SessionStorage.Client(); cli != nil {
		var vals map[string]string
		vals, err = cli.HGetAll(redisRegistationKey).Result()
		if err != nil {
			return
		}

		var deletedKeys []string
		for key, data := range vals {
			var entry registrationEntry
			if err = json.Unmarshal([]byte(data), &entry); err != nil {
				deletedKeys = append(deletedKeys, key)
				continue
			}
			diff := entry.Expire.Sub(now)
			if diff < -24*time.Hour {
				// skip too old entries
				deletedKeys = append(deletedKeys, entry.Key())
			} else if diff <= 10*time.Second {
				expiredLogins = append(expiredLogins, entry)
				deletedKeys = append(deletedKeys, entry.Key())
			} else if waitDuration == 0 || waitDuration > diff {
				waitDuration = diff
			}
		}

		if len(deletedKeys) > 0 {
			err = cli.HDel(redisRegistationKey, deletedKeys...).Err()
		}
	} else {
		registrationsMapLock.Lock()

		var deletedKeys []string
		for _, entry := range registrationsMap {
			diff := entry.Expire.Sub(now)
			if diff < -24*time.Hour {
				// skip too old entries
				deletedKeys = append(deletedKeys, entry.Key())
			} else if diff <= 10*time.Second {
				expiredLogins = append(expiredLogins, entry)
				deletedKeys = append(deletedKeys, entry.Key())
			} else if waitDuration == 0 || waitDuration > diff {
				waitDuration = diff
			}
		}

		for _, key := range deletedKeys {
			delete(registrationsMap, key)
		}

		registrationsMapLock.Unlock()
	}

	if len(expiredLogins) > 0 {
		sendExpiredRegistrationNotifications(expiredLogins)
	}

	return
}

func sendRegistrationNotification(entry *registrationEntry, registrationNotification bool) error {
	var login LoginEntry
	i, err := instance.Get(entry.Domain)
	if err != nil {
		return err
	}
	err = couchdb.GetDoc(i, consts.SessionsLogins, entry.LoginEntryID, &login)
	if err != nil {
		return err
	}
	return sendLoginNotification(i, &login, registrationNotification)
}

func sendExpiredRegistrationNotifications(entries []registrationEntry) {
	for _, entry := range entries {
		sendRegistrationNotification(&entry, false)
	}
}
<|endoftext|>


<commit_before>package acr

import (
	"context"
	cr "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry"
	"github.com/Azure/go-autorest/autorest/azure/auth"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"io"
	"regexp"
)

func (b *Builder) Build(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts []*latest.Artifact) ([]build.Artifact, error) {
	return build.InParallel(ctx, out, tagger, artifacts, b.buildArtifact)
}

func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, tagger tag.Tagger, artifact *latest.Artifact) (string, error) {
	client := cr.NewRegistriesClient(b.Credentials.SubscriptionId)
	authorizer, err := auth.NewClientCredentialsConfig(b.Credentials.ClientId, b.Credentials.ClientSecret, b.Credentials.TenantId).Authorizer()
	if err != nil {
		return "", errors.Wrap(err, "authorizing client")
	}
	client.Authorizer = authorizer

	result, err := client.GetBuildSourceUploadURL(ctx, b.ResourceGroup, b.ContainerRegistry)
	if err != nil {
		return "", errors.Wrap(err, "build source upload url")
	}
	blob := NewBlobStorage(*result.UploadURL)

	err = docker.CreateDockerTarGzContext(blob.Writer(), artifact.Workspace, artifact.DockerArtifact)
	if err != nil {
		return "", errors.Wrap(err, "create context tar.gz")
	}

	err = blob.UploadFileToBlob()
	if err != nil {
		return "", errors.Wrap(err, "upload file to blob")
	}

	imageTag, err := tagger.GenerateFullyQualifiedImageName(artifact.Workspace, &tag.Options{
		Digest:    util.RandomID(),
		ImageName: artifact.ImageName,
	})
	if err != nil {
		return "", errors.Wrap(err, "create fully qualified image name")
	}

	imageTag, err = getImageTagWithoutFQDN(imageTag)
	if err != nil {
		return "", errors.Wrap(err, "get azure image tag")
	}

	buildRequest := cr.DockerBuildRequest{
		ImageNames:     &[]string{imageTag},
		IsPushEnabled:  &[]bool{true}[0], //who invented bool pointers
		SourceLocation: result.RelativePath,
		Platform: &cr.PlatformProperties{
			Variant:      cr.V8,
			Os:           cr.Linux,
			Architecture: cr.Amd64,
		},
		DockerFilePath: &artifact.DockerArtifact.DockerfilePath,
		Type:           cr.TypeDockerBuildRequest,
	}
	future, err := client.ScheduleRun(ctx, b.ResourceGroup, b.ContainerRegistry, buildRequest)
	if err != nil {
		return "", errors.Wrap(err, "schedule build request")
	}

	err = future.WaitForCompletionRef(ctx, client.Client)
	if err != nil {
		return "", errors.Wrap(err, "wait for build completion")
	}

	return imageTag, nil
}

// ACR needs the image tag in the following format
// <registryName>/<repository>:<tag>
func getImageTagWithoutFQDN(imageTag string) (string, error) {
	r, err := regexp.Compile("(.*)\\..*\\..*(/.*)")
	if err != nil {
		return "", errors.Wrap(err, "create regexp")
	}

	matches := r.FindStringSubmatch(imageTag)
	if len(matches) < 3 {
		return "", errors.New("invalid image tag")
	}

	return matches[1] + matches[2], nil
}
<commit_msg>Added build status polling<commit_after>package acr

import (
	"bufio"
	"context"
	cr "github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2018-09-01/containerregistry"
	"github.com/Azure/go-autorest/autorest/azure/auth"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"io"
	"net/http"
	"regexp"
	"time"
)

const BUILD_STATUS_HEADER = "x-ms-meta-Complete"

func (b *Builder) Build(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts []*latest.Artifact) ([]build.Artifact, error) {
	return build.InParallel(ctx, out, tagger, artifacts, b.buildArtifact)
}

func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, tagger tag.Tagger, artifact *latest.Artifact) (string, error) {
	client := cr.NewRegistriesClient(b.Credentials.SubscriptionId)
	authorizer, err := auth.NewClientCredentialsConfig(b.Credentials.ClientId, b.Credentials.ClientSecret, b.Credentials.TenantId).Authorizer()
	if err != nil {
		return "", errors.Wrap(err, "authorizing client")
	}
	client.Authorizer = authorizer

	result, err := client.GetBuildSourceUploadURL(ctx, b.ResourceGroup, b.ContainerRegistry)
	if err != nil {
		return "", errors.Wrap(err, "build source upload url")
	}
	blob := NewBlobStorage(*result.UploadURL)

	err = docker.CreateDockerTarGzContext(blob.Writer(), artifact.Workspace, artifact.DockerArtifact)
	if err != nil {
		return "", errors.Wrap(err, "create context tar.gz")
	}

	err = blob.UploadFileToBlob()
	if err != nil {
		return "", errors.Wrap(err, "upload file to blob")
	}

	imageTag, err := tagger.GenerateFullyQualifiedImageName(artifact.Workspace, &tag.Options{
		Digest:    util.RandomID(),
		ImageName: artifact.ImageName,
	})
	if err != nil {
		return "", errors.Wrap(err, "create fully qualified image name")
	}

	imageTag, err = getImageTagWithoutFQDN(imageTag)
	if err != nil {
		return "", errors.Wrap(err, "get azure image tag")
	}

	buildRequest := cr.DockerBuildRequest{
		ImageNames:     &[]string{imageTag},
		IsPushEnabled:  &[]bool{true}[0], //who invented bool pointers
		SourceLocation: result.RelativePath,
		Platform: &cr.PlatformProperties{
			Variant:      cr.V8,
			Os:           cr.Linux,
			Architecture: cr.Amd64,
		},
		DockerFilePath: &artifact.DockerArtifact.DockerfilePath,
		Type:           cr.TypeDockerBuildRequest,
	}
	future, err := client.ScheduleRun(ctx, b.ResourceGroup, b.ContainerRegistry, buildRequest)
	if err != nil {
		return "", errors.Wrap(err, "schedule build request")
	}

	run, err := future.Result(client)
	if err != nil {
		return "", errors.Wrap(err, "get run id")
	}
	runId := *run.RunID

	runsClient := cr.NewRunsClient(b.Credentials.SubscriptionId)
	runsClient.Authorizer = client.Authorizer
	logUrl, err := runsClient.GetLogSasURL(ctx, b.ResourceGroup, b.ContainerRegistry, runId)
	if err != nil {
		return "", errors.Wrap(err, "get log url")
	}

	err = pollBuildStatus(*logUrl.LogLink, out)
	if err != nil {
		return "", errors.Wrap(err, "polling build status")
	}

	return imageTag, nil
}

func pollBuildStatus(logUrl string, out io.Writer) error {
	offset := int32(0)
	for {
		resp, err := http.Get(logUrl)
		if err != nil {
			return err
		}

		scanner := bufio.NewScanner(resp.Body)
		line := int32(0)
		for scanner.Scan() {
			if line < offset {
				continue
			}
			out.Write(scanner.Bytes())
			line++
			offset++
		}
		resp.Body.Close()

		switch resp.Header.Get(BUILD_STATUS_HEADER) {
		case "": //run succeeded when there is no status header
			return nil
		case "internalerror":
		case "failed":
			return errors.New("run failed")
		case "timedout":
			return errors.New("run timed out")
		case "canceled":
			return errors.New("run was canceled")
		}

		time.Sleep(2 * time.Second)
	}
}

// ACR needs the image tag in the following format
// <registryName>/<repository>:<tag>
func getImageTagWithoutFQDN(imageTag string) (string, error) {
	r, err := regexp.Compile("(.*)\\..*\\..*(/.*)")
	if err != nil {
		return "", errors.Wrap(err, "create regexp")
	}

	matches := r.FindStringSubmatch(imageTag)
	if len(matches) < 3 {
		return "", errors.New("invalid image tag")
	}

	return matches[1] + matches[2], nil
}
<|endoftext|>