<commit_before>package main

import (
	"flag"
	"fmt"
	"math"
	"os"
	"text/tabwriter"
)

// Money lets is print money numbers pretty
type Money float64

func (m Money) String() string {
	return fmt.Sprintf("$%.2f", m)
}

var (
	amount   = flag.Float64("amount", 0.0, "loan amount")
	interest = flag.Float64("rate", 0.0, "interest rate")
	months   = flag.Int("months", 360, "length of the loan (360 = 30 years)")
	table    = flag.Bool("table", false, "print amortization table")
	payment  = flag.Float64("payment", 0.0, "override the payment amount")
)

func main() {
	flag.Parse()
	if *amount == 0 || *interest == 0 {
		flag.Usage()
		return
	}

	if *table {
		printAmortizationTable()
	} else {
		periodic := (*interest / 100) / 12
		discount := calculateDiscount(*interest/100, *months)
		payment := *amount / discount
		interestPayment := *amount * periodic

		fmt.Println("Monthly Payment:", Money(payment))
		fmt.Println("First Interest Payment:", Money(interestPayment))
		fmt.Println("First Principal Payment:", Money(payment-interestPayment))
	}
}

func calculateDiscount(interest float64, months int) float64 {
	periodic := interest / 12
	daily := math.Pow(periodic+1, float64(months))
	return (daily - 1) / (periodic * daily)
}

func printAmortizationTable() {
	var totalInterest float64

	periodic := (*interest / 100) / 12
	discount := calculateDiscount(*interest/100, *months)
	balance := *amount
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, 2, '\t', 0)
	monthlyPayment := *payment
	if monthlyPayment == 0 {
		monthlyPayment = balance / discount
	}

	headers := "Period\tOpening\tPayment\tInterest\tPrincipal\tEnding\n"
	writer.Write([]byte(headers))

	for period := 1; period <= *months && balance > 0; period++ {
		interestPayment := balance * periodic
		principalPayment := monthlyPayment - interestPayment

		if principalPayment > balance {
			principalPayment = balance
			monthlyPayment = interestPayment + principalPayment
		}

		line := fmt.Sprintf("%d\t%s\t%s\t%s\t%s\t%s\n", period,
			Money(balance), Money(monthlyPayment), Money(interestPayment),
			Money(principalPayment), Money(balance-principalPayment))

		totalInterest += interestPayment

		writer.Write([]byte(line))
		balance -= principalPayment
	}

	writer.Flush()

	writer = tabwriter.NewWriter(os.Stdout, 0, 8, 2, '\t', 0)
	writer.Write([]byte(fmt.Sprintf("Principal\t%s\n", Money(*amount))))
	writer.Write([]byte(fmt.Sprintf("Total Interest\t%s\n", Money(totalInterest))))
	writer.Write([]byte(fmt.Sprintf("Total Payments\t%s\n", Money(*amount+totalInterest))))
	writer.Flush()
}
<commit_msg>use spaces instead of tabs<commit_after>package main

import (
	"flag"
	"fmt"
	"math"
	"os"
	"text/tabwriter"
)

// Money lets is print money numbers pretty
type Money float64

func (m Money) String() string {
	return fmt.Sprintf("$%.2f", m)
}

var (
	amount   = flag.Float64("amount", 0.0, "loan amount")
	interest = flag.Float64("rate", 0.0, "interest rate")
	months   = flag.Int("months", 360, "length of the loan (360 = 30 years)")
	table    = flag.Bool("table", false, "print amortization table")
	payment  = flag.Float64("payment", 0.0, "override the payment amount")
)

func main() {
	flag.Parse()
	if *amount == 0 || *interest == 0 {
		flag.Usage()
		return
	}

	if *table {
		printAmortizationTable()
	} else {
		periodic := (*interest / 100) / 12
		discount := calculateDiscount(*interest/100, *months)
		payment := *amount / discount
		interestPayment := *amount * periodic

		fmt.Println("Monthly Payment:", Money(payment))
		fmt.Println("First Interest Payment:", Money(interestPayment))
		fmt.Println("First Principal Payment:", Money(payment-interestPayment))
	}
}

func calculateDiscount(interest float64, months int) float64 {
	periodic := interest / 12
	daily := math.Pow(periodic+1, float64(months))
	return (daily - 1) / (periodic * daily)
}

func printAmortizationTable() {
	var totalInterest float64

	periodic := (*interest / 100) / 12
	discount := calculateDiscount(*interest/100, *months)
	balance := *amount
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
	monthlyPayment := *payment
	if monthlyPayment == 0 {
		monthlyPayment = balance / discount
	}

	headers := "Period\tOpening\tPayment\tInterest\tPrincipal\tEnding\n"
	writer.Write([]byte(headers))

	for period := 1; period <= *months && balance > 0; period++ {
		interestPayment := balance * periodic
		principalPayment := monthlyPayment - interestPayment

		if principalPayment > balance {
			principalPayment = balance
			monthlyPayment = interestPayment + principalPayment
		}

		line := fmt.Sprintf("%d\t%s\t%s\t%s\t%s\t%s\n", period,
			Money(balance), Money(monthlyPayment), Money(interestPayment),
			Money(principalPayment), Money(balance-principalPayment))

		totalInterest += interestPayment

		writer.Write([]byte(line))
		balance -= principalPayment
	}

	writer.Flush()

	writer = tabwriter.NewWriter(os.Stdout, 0, 8, 2, '\t', 0)
	writer.Write([]byte(fmt.Sprintf("Principal\t%s\n", Money(*amount))))
	writer.Write([]byte(fmt.Sprintf("Total Interest\t%s\n", Money(totalInterest))))
	writer.Write([]byte(fmt.Sprintf("Total Payments\t%s\n", Money(*amount+totalInterest))))
	writer.Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

const dir = "./example/autobahn"

var addr = flag.String("listen", ":9001", "addr to listen")

func main() {
	log.SetFlags(0)
	flag.Parse()

	http.HandleFunc("/ws", wsHandler)
	http.HandleFunc("/wsutil", wsutilHandler)
	http.HandleFunc("/helpers/low", helpersLowLevelHandler)
	http.HandleFunc("/helpers/high", helpersHighLevelHandler)

	ln, err := net.Listen("tcp", *addr)
	if err != nil {
		log.Fatalf("listen %q error: %v", err)
	}
	log.Printf("listening %s (%q)", ln.Addr(), *addr)

	var (
		s     = new(http.Server)
		serve = make(chan error, 1)
		sig   = make(chan os.Signal, 1)
	)
	signal.Notify(sig, syscall.SIGTERM)
	go func() { serve <- s.Serve(ln) }()

	select {
	case err := <-serve:
		log.Fatal(err)
	case sig := <-sig:
		const timeout = 5 * time.Second

		log.Printf("signal %q received; shutting down with %s timeout", sig, timeout)

		ctx, _ := context.WithTimeout(context.Background(), timeout)
		if err := s.Shutdown(ctx); err != nil {
			log.Fatal(err)
		}
	}
}

var (
	closeInvalidPayload = ws.MustCompileFrame(
		ws.NewCloseFrame(ws.StatusInvalidFramePayloadData, ""),
	)
	closeProtocolError = ws.MustCompileFrame(
		ws.NewCloseFrame(ws.StatusProtocolError, ""),
	)
)

func helpersHighLevelHandler(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	for {
		bts, op, err := wsutil.ReadClientData(conn)
		if err != nil {
			log.Printf("read message error: %v", err)
			return
		}
		err = wsutil.WriteServerMessage(conn, op, bts)
		if err != nil {
			log.Printf("write message error: %v", err)
			return
		}
	}
}

func helpersLowLevelHandler(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	msg := make([]wsutil.Message, 0, 4)

	for {
		msg, err = wsutil.ReadClientMessage(conn, msg[:0])
		if err != nil {
			log.Printf("read message error: %v", err)
			return
		}
		for _, m := range msg {
			if m.OpCode.IsControl() {
				err := wsutil.HandleClientControl(conn, m.OpCode, m.Payload)
				if err != nil {
					log.Printf("handle control error: %v", err)
					return
				}
				continue
			}
			err := wsutil.WriteServerMessage(conn, m.OpCode, m.Payload)
			if err != nil {
				log.Printf("write message error: %v", err)
				return
			}
		}
	}
}

func wsutilHandler(res http.ResponseWriter, req *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(req, res)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	state := ws.StateServerSide

	ch := wsutil.ControlHandler(conn, state)
	r := &wsutil.Reader{
		Source:         conn,
		State:          state,
		CheckUTF8:      true,
		OnIntermediate: ch,
	}
	w := wsutil.NewWriter(conn, state, 0)

	for {
		h, err := r.NextFrame()
		if err != nil {
			log.Printf("next frame error: %v", err)
			return
		}
		if h.OpCode.IsControl() {
			if err = ch(h, r); err != nil {
				log.Printf("handle control error: %v", err)
				return
			}
			continue
		}

		w.Reset(conn, state, h.OpCode)

		if _, err = io.Copy(w, r); err == nil {
			err = w.Flush()
		}
		if err != nil {
			log.Printf("echo error: %s", err)
			return
		}
	}
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	state := ws.StateServerSide

	textPending := false
	utf8Reader := wsutil.NewUTF8Reader(nil)
	cipherReader := wsutil.NewCipherReader(nil, [4]byte{0, 0, 0, 0})

	for {
		header, err := ws.ReadHeader(conn)
		if err != nil {
			log.Printf("read header error: %s", err)
			break
		}
		if err = ws.CheckHeader(header, state); err != nil {
			log.Printf("header check error: %s", err)
			conn.Write(closeProtocolError)
			return
		}

		cipherReader.Reset(
			io.LimitReader(conn, header.Length),
			header.Mask,
		)

		var utf8Fin bool
		var r io.Reader = cipherReader

		switch header.OpCode {
		case ws.OpPing:
			header.OpCode = ws.OpPong
			header.Masked = false
			ws.WriteHeader(conn, header)
			io.CopyN(conn, cipherReader, header.Length)
			continue

		case ws.OpPong:
			io.CopyN(ioutil.Discard, conn, header.Length)
			continue

		case ws.OpClose:
			utf8Fin = true

		case ws.OpContinuation:
			if textPending {
				utf8Reader.Source = cipherReader
				r = utf8Reader
			}
			if header.Fin {
				state = state.Clear(ws.StateFragmented)
				textPending = false
				utf8Fin = true
			}

		case ws.OpText:
			utf8Reader.Reset(cipherReader)
			r = utf8Reader

			if !header.Fin {
				state = state.Set(ws.StateFragmented)
				textPending = true
			} else {
				utf8Fin = true
			}

		case ws.OpBinary:
			if !header.Fin {
				state = state.Set(ws.StateFragmented)
			}
		}

		payload := make([]byte, header.Length)
		_, err = io.ReadFull(r, payload)
		if err == nil && utf8Fin && !utf8Reader.Valid() {
			err = wsutil.ErrInvalidUTF8
		}
		if err != nil {
			log.Printf("read payload error: %s", err)
			if err == wsutil.ErrInvalidUTF8 {
				conn.Write(closeInvalidPayload)
			} else {
				conn.Write(ws.CompiledClose)
			}
			return
		}

		if header.OpCode == ws.OpClose {
			code, reason := ws.ParseCloseFrameData(payload)
			log.Printf("close frame received: %v %v", code, reason)

			if !code.Empty() {
				switch {
				case code.IsProtocolSpec() && !code.IsProtocolDefined():
					err = fmt.Errorf("close code from spec range is not defined")
				default:
					err = ws.CheckCloseFrameData(code, reason)
				}
				if err != nil {
					log.Printf("invalid close data: %s", err)
					conn.Write(closeProtocolError)
				} else {
					ws.WriteFrame(conn, ws.NewCloseFrame(code, ""))
				}
				return
			}

			conn.Write(ws.CompiledClose)
			return
		}

		header.Masked = false
		ws.WriteHeader(conn, header)
		conn.Write(payload)
	}
}
<commit_msg>support frame changes<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

const dir = "./example/autobahn"

var addr = flag.String("listen", ":9001", "addr to listen")

func main() {
	log.SetFlags(0)
	flag.Parse()

	http.HandleFunc("/ws", wsHandler)
	http.HandleFunc("/wsutil", wsutilHandler)
	http.HandleFunc("/helpers/low", helpersLowLevelHandler)
	http.HandleFunc("/helpers/high", helpersHighLevelHandler)

	ln, err := net.Listen("tcp", *addr)
	if err != nil {
		log.Fatalf("listen %q error: %v", err)
	}
	log.Printf("listening %s (%q)", ln.Addr(), *addr)

	var (
		s     = new(http.Server)
		serve = make(chan error, 1)
		sig   = make(chan os.Signal, 1)
	)
	signal.Notify(sig, syscall.SIGTERM)
	go func() { serve <- s.Serve(ln) }()

	select {
	case err := <-serve:
		log.Fatal(err)
	case sig := <-sig:
		const timeout = 5 * time.Second

		log.Printf("signal %q received; shutting down with %s timeout", sig, timeout)

		ctx, _ := context.WithTimeout(context.Background(), timeout)
		if err := s.Shutdown(ctx); err != nil {
			log.Fatal(err)
		}
	}
}

var (
	closeInvalidPayload = ws.MustCompileFrame(
		ws.NewCloseFrame(ws.NewCloseFrameBody(
			ws.StatusInvalidFramePayloadData, "",
		)),
	)
	closeProtocolError = ws.MustCompileFrame(
		ws.NewCloseFrame(ws.NewCloseFrameBody(
			ws.StatusProtocolError, "",
		)),
	)
)

func helpersHighLevelHandler(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	for {
		bts, op, err := wsutil.ReadClientData(conn)
		if err != nil {
			log.Printf("read message error: %v", err)
			return
		}
		err = wsutil.WriteServerMessage(conn, op, bts)
		if err != nil {
			log.Printf("write message error: %v", err)
			return
		}
	}
}

func helpersLowLevelHandler(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	msg := make([]wsutil.Message, 0, 4)

	for {
		msg, err = wsutil.ReadClientMessage(conn, msg[:0])
		if err != nil {
			log.Printf("read message error: %v", err)
			return
		}
		for _, m := range msg {
			if m.OpCode.IsControl() {
				err := wsutil.HandleClientControl(conn, m.OpCode, m.Payload)
				if err != nil {
					log.Printf("handle control error: %v", err)
					return
				}
				continue
			}
			err := wsutil.WriteServerMessage(conn, m.OpCode, m.Payload)
			if err != nil {
				log.Printf("write message error: %v", err)
				return
			}
		}
	}
}

func wsutilHandler(res http.ResponseWriter, req *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(req, res)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	state := ws.StateServerSide

	ch := wsutil.ControlHandler(conn, state)
	r := &wsutil.Reader{
		Source:         conn,
		State:          state,
		CheckUTF8:      true,
		OnIntermediate: ch,
	}
	w := wsutil.NewWriter(conn, state, 0)

	for {
		h, err := r.NextFrame()
		if err != nil {
			log.Printf("next frame error: %v", err)
			return
		}
		if h.OpCode.IsControl() {
			if err = ch(h, r); err != nil {
				log.Printf("handle control error: %v", err)
				return
			}
			continue
		}

		w.Reset(conn, state, h.OpCode)

		if _, err = io.Copy(w, r); err == nil {
			err = w.Flush()
		}
		if err != nil {
			log.Printf("echo error: %s", err)
			return
		}
	}
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		log.Printf("upgrade error: %s", err)
		return
	}
	defer conn.Close()

	state := ws.StateServerSide

	textPending := false
	utf8Reader := wsutil.NewUTF8Reader(nil)
	cipherReader := wsutil.NewCipherReader(nil, [4]byte{0, 0, 0, 0})

	for {
		header, err := ws.ReadHeader(conn)
		if err != nil {
			log.Printf("read header error: %s", err)
			break
		}
		if err = ws.CheckHeader(header, state); err != nil {
			log.Printf("header check error: %s", err)
			conn.Write(closeProtocolError)
			return
		}

		cipherReader.Reset(
			io.LimitReader(conn, header.Length),
			header.Mask,
		)

		var utf8Fin bool
		var r io.Reader = cipherReader

		switch header.OpCode {
		case ws.OpPing:
			header.OpCode = ws.OpPong
			header.Masked = false
			ws.WriteHeader(conn, header)
			io.CopyN(conn, cipherReader, header.Length)
			continue

		case ws.OpPong:
			io.CopyN(ioutil.Discard, conn, header.Length)
			continue

		case ws.OpClose:
			utf8Fin = true

		case ws.OpContinuation:
			if textPending {
				utf8Reader.Source = cipherReader
				r = utf8Reader
			}
			if header.Fin {
				state = state.Clear(ws.StateFragmented)
				textPending = false
				utf8Fin = true
			}

		case ws.OpText:
			utf8Reader.Reset(cipherReader)
			r = utf8Reader

			if !header.Fin {
				state = state.Set(ws.StateFragmented)
				textPending = true
			} else {
				utf8Fin = true
			}

		case ws.OpBinary:
			if !header.Fin {
				state = state.Set(ws.StateFragmented)
			}
		}

		payload := make([]byte, header.Length)
		_, err = io.ReadFull(r, payload)
		if err == nil && utf8Fin && !utf8Reader.Valid() {
			err = wsutil.ErrInvalidUTF8
		}
		if err != nil {
			log.Printf("read payload error: %s", err)
			if err == wsutil.ErrInvalidUTF8 {
				conn.Write(closeInvalidPayload)
			} else {
				conn.Write(ws.CompiledClose)
			}
			return
		}

		if header.OpCode == ws.OpClose {
			code, reason := ws.ParseCloseFrameData(payload)
			log.Printf("close frame received: %v %v", code, reason)

			if !code.Empty() {
				switch {
				case code.IsProtocolSpec() && !code.IsProtocolDefined():
					err = fmt.Errorf("close code from spec range is not defined")
				default:
					err = ws.CheckCloseFrameData(code, reason)
				}
				if err != nil {
					log.Printf("invalid close data: %s", err)
					conn.Write(closeProtocolError)
				} else {
					ws.WriteFrame(conn, ws.NewCloseFrame(ws.NewCloseFrameBody(
						code, "",
					)))
				}
				return
			}

			conn.Write(ws.CompiledClose)
			return
		}

		header.Masked = false
		ws.WriteHeader(conn, header)
		conn.Write(payload)
	}
}
<|endoftext|>


<commit_before>package oss

import "os"

// ACLType bucket/object ACL
type ACLType string

const (
	// ACLPrivate definition : private read and write
	ACLPrivate ACLType = "private"

	// ACLPublicRead definition : public read and private write
	ACLPublicRead ACLType = "public-read"

	// ACLPublicReadWrite definition : public read and public write
	ACLPublicReadWrite ACLType = "public-read-write"

	// ACLDefault Object. It's only applicable for object.
	ACLDefault ACLType = "default"
)

// MetadataDirectiveType specifying whether use the metadata of source object when copying object.
type MetadataDirectiveType string

const (
	// MetaCopy the target object's metadata is copied from the source one
	MetaCopy MetadataDirectiveType = "COPY"

	// MetaReplace the target object's metadata is created as part of the copy request (not same as the source one)
	MetaReplace MetadataDirectiveType = "REPLACE"
)

// StorageClassType bucket storage type
type StorageClassType string

const (
	// StorageStandard standard
	StorageStandard StorageClassType = "Standard"

	// StorageIA infrequent access
	StorageIA StorageClassType = "IA"

	// StorageArchive archive
	StorageArchive StorageClassType = "Archive"
)

// PayerType the type of request payer
type PayerType string

const (
	// Requester the requester who send the request
	Requester PayerType = "requester"
)

// HTTPMethod HTTP request method
type HTTPMethod string

const (
	// HTTPGet HTTP GET
	HTTPGet HTTPMethod = "GET"

	// HTTPPut HTTP PUT
	HTTPPut HTTPMethod = "PUT"

	// HTTPHead HTTP HEAD
	HTTPHead HTTPMethod = "HEAD"

	// HTTPPost HTTP POST
	HTTPPost HTTPMethod = "POST"

	// HTTPDelete HTTP DELETE
	HTTPDelete HTTPMethod = "DELETE"
)

// HTTP headers
const (
	HTTPHeaderAcceptEncoding     string = "Accept-Encoding"
	HTTPHeaderAuthorization             = "Authorization"
	HTTPHeaderCacheControl              = "Cache-Control"
	HTTPHeaderContentDisposition        = "Content-Disposition"
	HTTPHeaderContentEncoding           = "Content-Encoding"
	HTTPHeaderContentLength             = "Content-Length"
	HTTPHeaderContentMD5                = "Content-MD5"
	HTTPHeaderContentType               = "Content-Type"
	HTTPHeaderContentLanguage           = "Content-Language"
	HTTPHeaderDate                      = "Date"
	HTTPHeaderEtag                      = "ETag"
	HTTPHeaderExpires                   = "Expires"
	HTTPHeaderHost                      = "Host"
	HTTPHeaderLastModified              = "Last-Modified"
	HTTPHeaderRange                     = "Range"
	HTTPHeaderLocation                  = "Location"
	HTTPHeaderOrigin                    = "Origin"
	HTTPHeaderServer                    = "Server"
	HTTPHeaderUserAgent                 = "User-Agent"
	HTTPHeaderIfModifiedSince           = "If-Modified-Since"
	HTTPHeaderIfUnmodifiedSince         = "If-Unmodified-Since"
	HTTPHeaderIfMatch                   = "If-Match"
	HTTPHeaderIfNoneMatch               = "If-None-Match"

	HTTPHeaderOssACL                         = "X-Oss-Acl"
	HTTPHeaderOssMetaPrefix                  = "X-Oss-Meta-"
	HTTPHeaderOssObjectACL                   = "X-Oss-Object-Acl"
	HTTPHeaderOssSecurityToken               = "X-Oss-Security-Token"
	HTTPHeaderOssServerSideEncryption        = "X-Oss-Server-Side-Encryption"
	HTTPHeaderOssCopySource                  = "X-Oss-Copy-Source"
	HTTPHeaderOssCopySourceRange             = "X-Oss-Copy-Source-Range"
	HTTPHeaderOssCopySourceIfMatch           = "X-Oss-Copy-Source-If-Match"
	HTTPHeaderOssCopySourceIfNoneMatch       = "X-Oss-Copy-Source-If-None-Match"
	HTTPHeaderOssCopySourceIfModifiedSince   = "X-Oss-Copy-Source-If-Modified-Since"
	HTTPHeaderOssCopySourceIfUnmodifiedSince = "X-Oss-Copy-Source-If-Unmodified-Since"
	HTTPHeaderOssMetadataDirective           = "X-Oss-Metadata-Directive"
	HTTPHeaderOssNextAppendPosition          = "X-Oss-Next-Append-Position"
	HTTPHeaderOssRequestID                   = "X-Oss-Request-Id"
	HTTPHeaderOssCRC64                       = "X-Oss-Hash-Crc64ecma"
	HTTPHeaderOssSymlinkTarget               = "X-Oss-Symlink-Target"
	HTTPHeaderOssStorageClass                = "X-Oss-Storage-Class"
	HTTPHeaderOssCallback                    = "X-Oss-Callback"
	HTTPHeaderOssCallbackVar                 = "X-Oss-Callback-Var"
	HTTPHeaderOSSRequester                   = "x-oss-request-payer"
)

// HTTP Param
const (
	HTTPParamExpires       = "Expires"
	HTTPParamAccessKeyID   = "OSSAccessKeyId"
	HTTPParamSignature     = "Signature"
	HTTPParamSecurityToken = "security-token"
)

// Other constants
const (
	MaxPartSize = 5 * 1024 * 1024 * 1024 // Max part size, 5GB
	MinPartSize = 100 * 1024             // Min part size, 100KB

	FilePermMode = os.FileMode(0664) // Default file permission

	TempFilePrefix = "oss-go-temp-" // Temp file prefix
	TempFileSuffix = ".temp"        // Temp file suffix

	CheckpointFileSuffix = ".cp" // Checkpoint file suffix

	Version = "1.9.1" // Go SDK version
)
<commit_msg>modify the value of HTTPHeaderOSSRequester to uppercase<commit_after>package oss

import "os"

// ACLType bucket/object ACL
type ACLType string

const (
	// ACLPrivate definition : private read and write
	ACLPrivate ACLType = "private"

	// ACLPublicRead definition : public read and private write
	ACLPublicRead ACLType = "public-read"

	// ACLPublicReadWrite definition : public read and public write
	ACLPublicReadWrite ACLType = "public-read-write"

	// ACLDefault Object. It's only applicable for object.
	ACLDefault ACLType = "default"
)

// MetadataDirectiveType specifying whether use the metadata of source object when copying object.
type MetadataDirectiveType string

const (
	// MetaCopy the target object's metadata is copied from the source one
	MetaCopy MetadataDirectiveType = "COPY"

	// MetaReplace the target object's metadata is created as part of the copy request (not same as the source one)
	MetaReplace MetadataDirectiveType = "REPLACE"
)

// StorageClassType bucket storage type
type StorageClassType string

const (
	// StorageStandard standard
	StorageStandard StorageClassType = "Standard"

	// StorageIA infrequent access
	StorageIA StorageClassType = "IA"

	// StorageArchive archive
	StorageArchive StorageClassType = "Archive"
)

// PayerType the type of request payer
type PayerType string

const (
	// Requester the requester who send the request
	Requester PayerType = "requester"
)

// HTTPMethod HTTP request method
type HTTPMethod string

const (
	// HTTPGet HTTP GET
	HTTPGet HTTPMethod = "GET"

	// HTTPPut HTTP PUT
	HTTPPut HTTPMethod = "PUT"

	// HTTPHead HTTP HEAD
	HTTPHead HTTPMethod = "HEAD"

	// HTTPPost HTTP POST
	HTTPPost HTTPMethod = "POST"

	// HTTPDelete HTTP DELETE
	HTTPDelete HTTPMethod = "DELETE"
)

// HTTP headers
const (
	HTTPHeaderAcceptEncoding     string = "Accept-Encoding"
	HTTPHeaderAuthorization             = "Authorization"
	HTTPHeaderCacheControl              = "Cache-Control"
	HTTPHeaderContentDisposition        = "Content-Disposition"
	HTTPHeaderContentEncoding           = "Content-Encoding"
	HTTPHeaderContentLength             = "Content-Length"
	HTTPHeaderContentMD5                = "Content-MD5"
	HTTPHeaderContentType               = "Content-Type"
	HTTPHeaderContentLanguage           = "Content-Language"
	HTTPHeaderDate                      = "Date"
	HTTPHeaderEtag                      = "ETag"
	HTTPHeaderExpires                   = "Expires"
	HTTPHeaderHost                      = "Host"
	HTTPHeaderLastModified              = "Last-Modified"
	HTTPHeaderRange                     = "Range"
	HTTPHeaderLocation                  = "Location"
	HTTPHeaderOrigin                    = "Origin"
	HTTPHeaderServer                    = "Server"
	HTTPHeaderUserAgent                 = "User-Agent"
	HTTPHeaderIfModifiedSince           = "If-Modified-Since"
	HTTPHeaderIfUnmodifiedSince         = "If-Unmodified-Since"
	HTTPHeaderIfMatch                   = "If-Match"
	HTTPHeaderIfNoneMatch               = "If-None-Match"

	HTTPHeaderOssACL                         = "X-Oss-Acl"
	HTTPHeaderOssMetaPrefix                  = "X-Oss-Meta-"
	HTTPHeaderOssObjectACL                   = "X-Oss-Object-Acl"
	HTTPHeaderOssSecurityToken               = "X-Oss-Security-Token"
	HTTPHeaderOssServerSideEncryption        = "X-Oss-Server-Side-Encryption"
	HTTPHeaderOssCopySource                  = "X-Oss-Copy-Source"
	HTTPHeaderOssCopySourceRange             = "X-Oss-Copy-Source-Range"
	HTTPHeaderOssCopySourceIfMatch           = "X-Oss-Copy-Source-If-Match"
	HTTPHeaderOssCopySourceIfNoneMatch       = "X-Oss-Copy-Source-If-None-Match"
	HTTPHeaderOssCopySourceIfModifiedSince   = "X-Oss-Copy-Source-If-Modified-Since"
	HTTPHeaderOssCopySourceIfUnmodifiedSince = "X-Oss-Copy-Source-If-Unmodified-Since"
	HTTPHeaderOssMetadataDirective           = "X-Oss-Metadata-Directive"
	HTTPHeaderOssNextAppendPosition          = "X-Oss-Next-Append-Position"
	HTTPHeaderOssRequestID                   = "X-Oss-Request-Id"
	HTTPHeaderOssCRC64                       = "X-Oss-Hash-Crc64ecma"
	HTTPHeaderOssSymlinkTarget               = "X-Oss-Symlink-Target"
	HTTPHeaderOssStorageClass                = "X-Oss-Storage-Class"
	HTTPHeaderOssCallback                    = "X-Oss-Callback"
	HTTPHeaderOssCallbackVar                 = "X-Oss-Callback-Var"
	HTTPHeaderOSSRequester                   = "X-Oss-Request-Payer"
)

// HTTP Param
const (
	HTTPParamExpires       = "Expires"
	HTTPParamAccessKeyID   = "OSSAccessKeyId"
	HTTPParamSignature     = "Signature"
	HTTPParamSecurityToken = "security-token"
)

// Other constants
const (
	MaxPartSize = 5 * 1024 * 1024 * 1024 // Max part size, 5GB
	MinPartSize = 100 * 1024             // Min part size, 100KB

	FilePermMode = os.FileMode(0664) // Default file permission

	TempFilePrefix = "oss-go-temp-" // Temp file prefix
	TempFileSuffix = ".temp"        // Temp file suffix

	CheckpointFileSuffix = ".cp" // Checkpoint file suffix

	Version = "1.9.1" // Go SDK version
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os/exec"
	"strings"
)

func mainHandler(w http.ResponseWriter, r *http.Request) {

	path := strings.TrimSpace(r.URL.Path)

	//Cut off the leading and trailing forward slashes, if they exist.
	//This cuts off the leading forward slash.
	if strings.HasPrefix(path, "/") {
		path = path[1:]
	}
	//This cuts off the trailing forward slash.
	if strings.HasSuffix(path, "/") {
		cut_off_last_char_len := len(path) - 1
		path = path[:cut_off_last_char_len]
	}

	if len(path) == 0 {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// We need to isolate the individual components of the path.
	components := strings.Split(path, "/")

	salt_node := fmt.Sprintf("G@node_type:%s", components[0])

	cmdName := "sudo salt"
	cmdArgs := []string{"-C", salt_node, "state.highstate"}

	out, err := exec.Command(cmdName, cmdArgs...).Output()
	if err != nil {
		fmt.Fprintf(w, "There was an error running command: %s %s %s", cmdName, cmdArgs, err)
		return
	}

	fmt.Fprintf(w, string(out))
	return
}
<commit_msg>	modified:   mainHandler.go<commit_after>package main

import (
	"fmt"
	"net/http"
	"os/exec"
	"strings"
)

func mainHandler(w http.ResponseWriter, r *http.Request) {

	path := strings.TrimSpace(r.URL.Path)

	//Cut off the leading and trailing forward slashes, if they exist.
	//This cuts off the leading forward slash.
	if strings.HasPrefix(path, "/") {
		path = path[1:]
	}
	//This cuts off the trailing forward slash.
	if strings.HasSuffix(path, "/") {
		cut_off_last_char_len := len(path) - 1
		path = path[:cut_off_last_char_len]
	}

	if len(path) == 0 {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// We need to isolate the individual components of the path.
	components := strings.Split(path, "/")

	salt_node := fmt.Sprintf("G@node_type:%s", components[0])

	cmdName := "sudo"
	cmdArgs := []string{"salt", "-C", salt_node, "state.highstate"}

	out, err := exec.Command(cmdName, cmdArgs...).Output()
	if err != nil {
		fmt.Fprintf(w, "There was an error running command: %s %s %s", cmdName, cmdArgs, err)
		return
	}

	fmt.Fprintf(w, string(out))
	return
}
<|endoftext|>


<commit_before>package otto

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/hashicorp/otto/app"
	"github.com/hashicorp/otto/appfile"
	"github.com/hashicorp/otto/directory"
	"github.com/hashicorp/otto/infrastructure"
	"github.com/hashicorp/otto/ui"
)

// Core is the main struct to use to interact with Otto as a library.
type Core struct {
	appfile         *appfile.File
	appfileCompiled *appfile.Compiled
	apps            map[app.Tuple]app.Factory
	dir             directory.Backend
	infras          map[string]infrastructure.Factory
	outputDir       string
	ui              ui.Ui
}

// CoreConfig is configuration for creating a new core with NewCore.
type CoreConfig struct {
	// OutputDir is the directory where data will be written. Each
	// compilation will clear this directory prior to writing to it.
	OutputDir string

	// Appfile is the appfile that this core will be using for configuration.
	// This must be a compiled Appfile.
	Appfile *appfile.Compiled

	// Directory is the directory where data is stored about this Appfile.
	Directory directory.Backend

	// Apps is the map of available app implementations.
	Apps map[app.Tuple]app.Factory

	// Infrastructures is the map of available infrastructures. The
	// value is a factory that can create the infrastructure impl.
	Infrastructures map[string]infrastructure.Factory

	// Ui is the Ui that will be used to comunicate with the user.
	Ui ui.Ui
}

// NewCore creates a new core.
//
// Once this function is called, this CoreConfig should not be used again
// or modified, since the Core may use parts of it without deep copying.
func NewCore(c *CoreConfig) (*Core, error) {
	return &Core{
		appfile:         c.Appfile.File,
		appfileCompiled: c.Appfile,
		apps:            c.Apps,
		dir:             c.Directory,
		infras:          c.Infrastructures,
		outputDir:       c.OutputDir,
		ui:              c.Ui,
	}, nil
}

// Compile takes the Appfile and compiles all the resulting data.
func (c *Core) Compile() error {
	// Get the infra implementation for this
	infra, infraCtx, err := c.infra()
	if err != nil {
		return err
	}

	// Get the application implementation for this
	app, appCtx, err := c.app()
	if err != nil {
		return err
	}

	// Delete the prior output directory
	log.Printf("[INFO] deleting prior compilation contents: %s", c.outputDir)
	if err := os.RemoveAll(c.outputDir); err != nil {
		return err
	}

	// Compile!
	log.Printf("[INFO] running infra compile...")
	if _, err := infra.Compile(infraCtx); err != nil {
		return err
	}
	log.Printf("[INFO] running app compile...")
	if _, err := app.Compile(appCtx); err != nil {
		return err
	}

	return nil
}

// Execute executes the given task for this Appfile.
func (c *Core) Execute(opts *ExecuteOpts) error {
	switch opts.Task {
	case ExecuteTaskDev:
		return c.executeApp(opts)
	case ExecuteTaskInfra:
		return c.executeInfra(opts)
	default:
		return fmt.Errorf("unknown task: %s", opts.Task)
	}
}

func (c *Core) executeApp(opts *ExecuteOpts) error {
	// Get the infra implementation for this
	app, appCtx, err := c.app()
	if err != nil {
		return err
	}

	// Set the action and action args
	appCtx.Action = opts.Action
	appCtx.ActionArgs = opts.Args

	// Build the infrastructure compilation context
	switch opts.Task {
	case ExecuteTaskDev:
		return app.Dev(appCtx)
	default:
		panic(fmt.Sprintf("uknown task: %s", opts.Task))
	}
}

func (c *Core) executeInfra(opts *ExecuteOpts) error {
	// Get the infra implementation for this
	infra, infraCtx, err := c.infra()
	if err != nil {
		return err
	}

	// Set the action and action args
	infraCtx.Action = opts.Action
	infraCtx.ActionArgs = opts.Args

	// Build the infrastructure compilation context
	return infra.Execute(infraCtx)
}

func (c *Core) app() (app.App, *app.Context, error) {
	// We need the configuration for the active infrastructure
	// so that we can build the tuple below
	config := c.appfile.ActiveInfrastructure()
	if config == nil {
		return nil, nil, fmt.Errorf(
			"infrastructure not found in appfile: %s",
			c.appfile.Project.Infrastructure)
	}

	// The tuple we're looking for is the application type, the
	// infrastructure type, and the infrastructure flavor. Build that
	// tuple.
	tuple := app.Tuple{
		App:         c.appfile.Application.Type,
		Infra:       c.appfile.Project.Infrastructure,
		InfraFlavor: config.Flavor,
	}

	// Look for the app impl. factory
	f, ok := c.apps[tuple]
	if !ok {
		return nil, nil, fmt.Errorf(
			"app implementation for tuple not found: %s", tuple)
	}

	// Start the impl.
	result, err := f()
	if err != nil {
		return nil, nil, fmt.Errorf(
			"app failed to start properly: %s", err)
	}

	// The output directory for data
	outputDir := filepath.Join(c.outputDir, "app")

	return result, &app.Context{
		Dir:         outputDir,
		Tuple:       tuple,
		Appfile:     c.appfile,
		Application: c.appfile.Application,
		Ui:          c.ui,
	}, nil
}

func (c *Core) infra() (infrastructure.Infrastructure, *infrastructure.Context, error) {
	// Get the infrastructure factory
	f, ok := c.infras[c.appfile.Project.Infrastructure]
	if !ok {
		return nil, nil, fmt.Errorf(
			"infrastructure type not supported: %s",
			c.appfile.Project.Infrastructure)
	}

	// Get the infrastructure configuration
	config := c.appfile.ActiveInfrastructure()
	if config == nil {
		return nil, nil, fmt.Errorf(
			"infrastructure not found in appfile: %s",
			c.appfile.Project.Infrastructure)
	}

	// Start the infrastructure implementation
	infra, err := f()
	if err != nil {
		return nil, nil, err
	}

	// The output directory for data
	outputDir := filepath.Join(
		c.outputDir, fmt.Sprintf("infra-%s", c.appfile.Project.Infrastructure))

	// Build the context
	return infra, &infrastructure.Context{
		Dir:       outputDir,
		Infra:     config,
		Ui:        c.ui,
		Directory: c.dir,
	}, nil
}
<commit_msg>otto: comment typo<commit_after>package otto

import (
	"fmt"
	"log"
	"os"
	"path/filepath"

	"github.com/hashicorp/otto/app"
	"github.com/hashicorp/otto/appfile"
	"github.com/hashicorp/otto/directory"
	"github.com/hashicorp/otto/infrastructure"
	"github.com/hashicorp/otto/ui"
)

// Core is the main struct to use to interact with Otto as a library.
type Core struct {
	appfile         *appfile.File
	appfileCompiled *appfile.Compiled
	apps            map[app.Tuple]app.Factory
	dir             directory.Backend
	infras          map[string]infrastructure.Factory
	outputDir       string
	ui              ui.Ui
}

// CoreConfig is configuration for creating a new core with NewCore.
type CoreConfig struct {
	// OutputDir is the directory where data will be written. Each
	// compilation will clear this directory prior to writing to it.
	OutputDir string

	// Appfile is the appfile that this core will be using for configuration.
	// This must be a compiled Appfile.
	Appfile *appfile.Compiled

	// Directory is the directory where data is stored about this Appfile.
	Directory directory.Backend

	// Apps is the map of available app implementations.
	Apps map[app.Tuple]app.Factory

	// Infrastructures is the map of available infrastructures. The
	// value is a factory that can create the infrastructure impl.
	Infrastructures map[string]infrastructure.Factory

	// Ui is the Ui that will be used to communicate with the user.
	Ui ui.Ui
}

// NewCore creates a new core.
//
// Once this function is called, this CoreConfig should not be used again
// or modified, since the Core may use parts of it without deep copying.
func NewCore(c *CoreConfig) (*Core, error) {
	return &Core{
		appfile:         c.Appfile.File,
		appfileCompiled: c.Appfile,
		apps:            c.Apps,
		dir:             c.Directory,
		infras:          c.Infrastructures,
		outputDir:       c.OutputDir,
		ui:              c.Ui,
	}, nil
}

// Compile takes the Appfile and compiles all the resulting data.
func (c *Core) Compile() error {
	// Get the infra implementation for this
	infra, infraCtx, err := c.infra()
	if err != nil {
		return err
	}

	// Get the application implementation for this
	app, appCtx, err := c.app()
	if err != nil {
		return err
	}

	// Delete the prior output directory
	log.Printf("[INFO] deleting prior compilation contents: %s", c.outputDir)
	if err := os.RemoveAll(c.outputDir); err != nil {
		return err
	}

	// Compile!
	log.Printf("[INFO] running infra compile...")
	if _, err := infra.Compile(infraCtx); err != nil {
		return err
	}
	log.Printf("[INFO] running app compile...")
	if _, err := app.Compile(appCtx); err != nil {
		return err
	}

	return nil
}

// Execute executes the given task for this Appfile.
func (c *Core) Execute(opts *ExecuteOpts) error {
	switch opts.Task {
	case ExecuteTaskDev:
		return c.executeApp(opts)
	case ExecuteTaskInfra:
		return c.executeInfra(opts)
	default:
		return fmt.Errorf("unknown task: %s", opts.Task)
	}
}

func (c *Core) executeApp(opts *ExecuteOpts) error {
	// Get the infra implementation for this
	app, appCtx, err := c.app()
	if err != nil {
		return err
	}

	// Set the action and action args
	appCtx.Action = opts.Action
	appCtx.ActionArgs = opts.Args

	// Build the infrastructure compilation context
	switch opts.Task {
	case ExecuteTaskDev:
		return app.Dev(appCtx)
	default:
		panic(fmt.Sprintf("uknown task: %s", opts.Task))
	}
}

func (c *Core) executeInfra(opts *ExecuteOpts) error {
	// Get the infra implementation for this
	infra, infraCtx, err := c.infra()
	if err != nil {
		return err
	}

	// Set the action and action args
	infraCtx.Action = opts.Action
	infraCtx.ActionArgs = opts.Args

	// Build the infrastructure compilation context
	return infra.Execute(infraCtx)
}

func (c *Core) app() (app.App, *app.Context, error) {
	// We need the configuration for the active infrastructure
	// so that we can build the tuple below
	config := c.appfile.ActiveInfrastructure()
	if config == nil {
		return nil, nil, fmt.Errorf(
			"infrastructure not found in appfile: %s",
			c.appfile.Project.Infrastructure)
	}

	// The tuple we're looking for is the application type, the
	// infrastructure type, and the infrastructure flavor. Build that
	// tuple.
	tuple := app.Tuple{
		App:         c.appfile.Application.Type,
		Infra:       c.appfile.Project.Infrastructure,
		InfraFlavor: config.Flavor,
	}

	// Look for the app impl. factory
	f, ok := c.apps[tuple]
	if !ok {
		return nil, nil, fmt.Errorf(
			"app implementation for tuple not found: %s", tuple)
	}

	// Start the impl.
	result, err := f()
	if err != nil {
		return nil, nil, fmt.Errorf(
			"app failed to start properly: %s", err)
	}

	// The output directory for data
	outputDir := filepath.Join(c.outputDir, "app")

	return result, &app.Context{
		Dir:         outputDir,
		Tuple:       tuple,
		Appfile:     c.appfile,
		Application: c.appfile.Application,
		Ui:          c.ui,
	}, nil
}

func (c *Core) infra() (infrastructure.Infrastructure, *infrastructure.Context, error) {
	// Get the infrastructure factory
	f, ok := c.infras[c.appfile.Project.Infrastructure]
	if !ok {
		return nil, nil, fmt.Errorf(
			"infrastructure type not supported: %s",
			c.appfile.Project.Infrastructure)
	}

	// Get the infrastructure configuration
	config := c.appfile.ActiveInfrastructure()
	if config == nil {
		return nil, nil, fmt.Errorf(
			"infrastructure not found in appfile: %s",
			c.appfile.Project.Infrastructure)
	}

	// Start the infrastructure implementation
	infra, err := f()
	if err != nil {
		return nil, nil, err
	}

	// The output directory for data
	outputDir := filepath.Join(
		c.outputDir, fmt.Sprintf("infra-%s", c.appfile.Project.Infrastructure))

	// Build the context
	return infra, &infrastructure.Context{
		Dir:       outputDir,
		Infra:     config,
		Ui:        c.ui,
		Directory: c.dir,
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"text/template"

	"code.uber.internal/personal/joshua/gwr"
)

type reqLogger struct {
	handler http.Handler
	watcher gwr.GenericDataWatcher
}

func logged(handler http.Handler) *reqLogger {
	return &reqLogger{
		handler: handler,
	}
}

var reqLogTextTemplate = template.Must(template.New("req_logger_text").Parse(`
{{- define "item" -}}
{{ .method }} {{ .URL }}
{{ end -}}
`))

func (rl *reqLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if rl.watcher != nil {
		info := map[string]interface{}{
			"method": r.Method,
			"URL":    r.URL,
		}
		if !rl.watcher(info) {
			rl.watcher = nil
		}
	}
	rl.handler.ServeHTTP(w, r)
}

func (rl *reqLogger) Info() gwr.GenericDataSourceInfo {
	return gwr.GenericDataSourceInfo{
		Name: "/request_log",
		// TODO: afford watch-only nature
		TextTemplate: reqLogTextTemplate,
	}
}

func (rl *reqLogger) Get() interface{} {
	return nil
}

func (rl *reqLogger) GetInit() interface{} {
	return nil
}

func (rl *reqLogger) Watch(watcher gwr.GenericDataWatcher) {
	rl.watcher = watcher
}
<commit_msg>example_server: use a struct for reqLogger info<commit_after>package main

import (
	"net/http"
	"text/template"

	"code.uber.internal/personal/joshua/gwr"
)

type reqLogger struct {
	handler http.Handler
	watcher gwr.GenericDataWatcher
}

func logged(handler http.Handler) *reqLogger {
	return &reqLogger{
		handler: handler,
	}
}

var reqLogTextTemplate = template.Must(template.New("req_logger_text").Parse(`
{{- define "item" -}}
{{ .Method }} {{ .Path }} {{ .Query }}
{{ end -}}
`))

type reqInfo struct {
	Method string `json:"method"`
	Path   string `json:"path"`
	Query  string `json:"query"`
}

func (rl *reqLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if rl.watcher != nil {
		info := reqInfo{
			Method: r.Method,
			Path:   r.URL.Path,
			Query:  r.URL.RawQuery,
		}
		if !rl.watcher(info) {
			rl.watcher = nil
		}
	}
	rl.handler.ServeHTTP(w, r)
}

func (rl *reqLogger) Info() gwr.GenericDataSourceInfo {
	return gwr.GenericDataSourceInfo{
		Name: "/request_log",
		// TODO: afford watch-only nature
		TextTemplate: reqLogTextTemplate,
	}
}

func (rl *reqLogger) Get() interface{} {
	return nil
}

func (rl *reqLogger) GetInit() interface{} {
	return nil
}

func (rl *reqLogger) Watch(watcher gwr.GenericDataWatcher) {
	rl.watcher = watcher
}
<|endoftext|>


<commit_before>// Copyright Istio 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 status

import (
	"context"
	"sync"
	"time"

	v2 "istio.io/istio/pilot/pkg/proxy/envoy/v2"

	"github.com/pkg/errors"

	"gopkg.in/yaml.v2"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	v1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/utils/clock"

	"istio.io/istio/pilot/pkg/model"
)

func NewIstioContext(stop <-chan struct{}) context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		<-stop
		cancel()
	}()
	return ctx
}

type inProgressEntry struct {
	// the resource, including resourceVersion, we are currently tracking
	Resource
	// the number of reports we have written with this resource at 100%
	completedIterations int
}

type Reporter struct {
	mu                     sync.RWMutex
	status                 map[string]string
	reverseStatus          map[string][]string
	dirty                  bool
	inProgressResources    map[string]*inProgressEntry
	client                 v1.ConfigMapInterface
	cm                     *corev1.ConfigMap
	UpdateInterval         time.Duration
	PodName                string
	clock                  clock.Clock
	store                  model.ConfigStore
	distributionEventQueue chan distributionEvent
}

var _ v2.DistributionStatusCache = &Reporter{}

const labelKey = "internal.istio.io/distribution-report"
const dataField = "distribution-report"

// Starts the reporter, which watches dataplane ack's and resource changes so that it can update status leader
// with distribution information.  To run in read-only mode, (for supporting istioctl wait), set writeMode = false
func (r *Reporter) Start(clientSet kubernetes.Interface, namespace string, store model.ConfigStore, writeMode bool, stop <-chan struct{}) {
	scope.Info("Starting status follower controller")
	if r.clock == nil {
		r.clock = clock.RealClock{}
	}
	r.store = store
	// default UpdateInterval
	if r.UpdateInterval == 0 {
		r.UpdateInterval = 500 * time.Millisecond
	}
	r.distributionEventQueue = make(chan distributionEvent, 10^5)
	r.status = make(map[string]string)
	r.reverseStatus = make(map[string][]string)
	r.inProgressResources = make(map[string]*inProgressEntry)
	go r.readFromEventQueue()
	if !writeMode {
		return
	}
	r.client = clientSet.CoreV1().ConfigMaps(namespace)
	r.cm = &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:   (r.PodName + "-distribution"),
			Labels: map[string]string{labelKey: "true"},
		},
		Data: make(map[string]string),
	}
	t := r.clock.Tick(r.UpdateInterval)
	ctx := NewIstioContext(stop)
	go func() {
		for {
			select {
			case <-ctx.Done():
				if r.cm != nil {
					// TODO: is the use of a cancelled context here a problem?  Maybe set a short timeout context?
					if err := r.client.Delete(context.Background(), r.cm.Name, metav1.DeleteOptions{}); err != nil {
						scope.Errorf("failed to properly clean up distribution report: %v", err)
					}
				}
				close(r.distributionEventQueue)
				return
			case <-t:
				// TODO, check if report is necessary?  May already be handled by client
				r.writeReport(ctx)
			}
		}
	}()
}

// build a distribution report to send to status leader
func (r *Reporter) buildReport() (DistributionReport, []Resource) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var finishedResources []Resource
	out := DistributionReport{
		Reporter:            r.PodName,
		DataPlaneCount:      len(r.status),
		InProgressResources: map[string]int{},
	}
	// for every resource in flight
	for _, ipr := range r.inProgressResources {
		res := ipr.Resource
		key := res.String()
		// for every version (nonce) of the config currently in play
		for nonce, dataplanes := range r.reverseStatus {

			// check to see if this version of the config contains this version of the resource
			// it might be more optimal to provide for a full dump of the config at a certain version?
			dpVersion, err := r.store.GetResourceAtVersion(nonce, res.ToModelKey())
			if err == nil && dpVersion == res.ResourceVersion {
				if _, ok := out.InProgressResources[key]; !ok {
					out.InProgressResources[key] = len(dataplanes)
				} else {
					out.InProgressResources[key] += len(dataplanes)
				}
			} else if err != nil {
				scope.Errorf("Encountered error retrieving version %s of key %s from Store: %v", nonce, key, err)
				continue
			} else if nonce == r.store.Version() {
				scope.Warnf("Cache appears to be missing latest version of %s", key)
			}
			if out.InProgressResources[key] >= out.DataPlaneCount {
				// if this resource is done reconciling, let's not worry about it anymore
				finishedResources = append(finishedResources, res)
				// deleting it here doesn't work because we have a read lock and are inside an iterator.
				// TODO: this will leak when a resource never reaches 100% before it is replaced.
				// TODO: do deletes propagate through this thing?
			}
		}
	}
	return out, finishedResources
}

// For efficiency, we don't want to be checking on resources that have already reached 100% distribution.
// When this happens, we remove them from our watch list.
func (r *Reporter) removeCompletedResource(completedResources []Resource) {
	r.mu.Lock()
	defer r.mu.Unlock()
	var toDelete []Resource
	for _, item := range completedResources {
		// TODO: handle cache miss
		total := r.inProgressResources[item.ToModelKey()].completedIterations + 1
		if int64(total) > (time.Minute.Milliseconds() / r.UpdateInterval.Milliseconds()) {
			//remove from inProgressResources // TODO: cleanup completedResources
			toDelete = append(toDelete, item)
		} else {
			r.inProgressResources[item.ToModelKey()].completedIterations = total
		}
	}
	for _, resource := range toDelete {
		delete(r.inProgressResources, resource.ToModelKey())
	}
}

// This function must be called every time a resource change is detected by pilot.  This allows us to lookup
// only the resources we expect to be in flight, not the ones that have already distributed
func (r *Reporter) AddInProgressResource(res model.Config) {
	myRes := ResourceFromModelConfig(res)
	if myRes == nil {
		scope.Errorf("Unable to locate schema for %v, will not update status.", res)
		return
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	r.inProgressResources[myRes.ToModelKey()] = &inProgressEntry{
		Resource:            *myRes,
		completedIterations: 0,
	}
}

func (r *Reporter) DeleteInProgressResource(res model.Config) {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.inProgressResources, res.Key())
}

// generate a distribution report and write it to a ConfigMap for the leader to read.
func (r *Reporter) writeReport(ctx context.Context) {
	report, finishedResources := r.buildReport()
	go r.removeCompletedResource(finishedResources)
	//write to kubernetes here.
	reportbytes, err := yaml.Marshal(report)
	if err != nil {
		scope.Errorf("Error serializing Distribution Report: %v", err)
		return
	}
	r.cm.Data[dataField] = string(reportbytes)
	// TODO: short circuit this write in the leader
	_, err = CreateOrUpdateConfigMap(ctx, r.cm, r.client)
	if err != nil {
		scope.Errorf("Error writing Distribution Report: %v", err)
	}
}

// this is lifted with few modifications from kubeadm's apiclient
func CreateOrUpdateConfigMap(ctx context.Context, cm *corev1.ConfigMap, client v1.ConfigMapInterface) (res *corev1.ConfigMap, err error) {
	if res, err = client.Create(ctx, cm, metav1.CreateOptions{}); err != nil {
		if !apierrors.IsAlreadyExists(err) && !apierrors.IsInvalid(err) {
			scope.Errorf("%v", err)
			return nil, errors.Wrap(err, "unable to create ConfigMap")
		}

		if res, err = client.Update(context.TODO(), cm, metav1.UpdateOptions{}); err != nil {
			return nil, errors.Wrap(err, "unable to update ConfigMap")
		}
	}
	return res, nil
}

type distributionEvent struct {
	conID            string
	distributionType v2.EventType
	nonce            string
}

func (r *Reporter) QueryLastNonce(conID string, distributionType v2.EventType) (noncePrefix string) {
	key := conID + string(distributionType)
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.status[key]
}

// Register that a dataplane has acknowledged a new version of the config.
// Theoretically, we could use the ads connections themselves to harvest this data,
// but the mutex there is pretty hot, and it seems best to trade memory for time.
func (r *Reporter) RegisterEvent(conID string, distributionType v2.EventType, nonce string) {
	d := distributionEvent{nonce: nonce, distributionType: distributionType, conID: conID}
	select {
	case r.distributionEventQueue <- d:
		return
	default:
		scope.Errorf("Distribution Event Queue overwhelmed, status will be invalid.")
	}
}

func (r *Reporter) readFromEventQueue() {
	for ev := range r.distributionEventQueue {
		// TODO might need to batch this to prevent lock contention
		r.processEvent(ev.conID, ev.distributionType, ev.nonce)
	}

}
func (r *Reporter) processEvent(conID string, distributionType v2.EventType, nonce string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.dirty = true
	key := conID + string(distributionType) // TODO: delimit?
	r.deleteKeyFromReverseMap(key)
	var version string
	if len(nonce) > 12 {
		version = nonce[:v2.VersionLen]
	} else {
		version = nonce
	}
	// touch
	r.status[key] = version
	r.reverseStatus[version] = append(r.reverseStatus[version], key)
}

// This is a helper function for keeping our reverseStatus map in step with status.
// must have write lock before calling.
func (r *Reporter) deleteKeyFromReverseMap(key string) {
	if old, ok := r.status[key]; ok {
		if keys, ok := r.reverseStatus[old]; ok {
			for i := range keys {
				if keys[i] == key {
					r.reverseStatus[old] = append(keys[:i], keys[i+1:]...)
					break
				}
			}
			if len(r.reverseStatus[old]) < 1 {
				delete(r.reverseStatus, old)
			}
		}
	}
}

// When a dataplane disconnects, we should no longer count it, nor expect it to ack config.
func (r *Reporter) RegisterDisconnect(conID string, types []v2.EventType) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.dirty = true
	for _, xdsType := range types {
		key := conID + string(xdsType) // TODO: delimit?
		r.deleteKeyFromReverseMap(key)
		delete(r.status, key)
	}
}
<commit_msg>Fix ledger capacity size (#24529)<commit_after>// Copyright Istio 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 status

import (
	"context"
	"sync"
	"time"

	v2 "istio.io/istio/pilot/pkg/proxy/envoy/v2"

	"github.com/pkg/errors"

	"gopkg.in/yaml.v2"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	v1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/utils/clock"

	"istio.io/istio/pilot/pkg/model"
)

func NewIstioContext(stop <-chan struct{}) context.Context {
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		<-stop
		cancel()
	}()
	return ctx
}

type inProgressEntry struct {
	// the resource, including resourceVersion, we are currently tracking
	Resource
	// the number of reports we have written with this resource at 100%
	completedIterations int
}

type Reporter struct {
	mu                     sync.RWMutex
	status                 map[string]string
	reverseStatus          map[string][]string
	dirty                  bool
	inProgressResources    map[string]*inProgressEntry
	client                 v1.ConfigMapInterface
	cm                     *corev1.ConfigMap
	UpdateInterval         time.Duration
	PodName                string
	clock                  clock.Clock
	store                  model.ConfigStore
	distributionEventQueue chan distributionEvent
}

var _ v2.DistributionStatusCache = &Reporter{}

const labelKey = "internal.istio.io/distribution-report"
const dataField = "distribution-report"

// Starts the reporter, which watches dataplane ack's and resource changes so that it can update status leader
// with distribution information.  To run in read-only mode, (for supporting istioctl wait), set writeMode = false
func (r *Reporter) Start(clientSet kubernetes.Interface, namespace string, store model.ConfigStore, writeMode bool, stop <-chan struct{}) {
	scope.Info("Starting status follower controller")
	if r.clock == nil {
		r.clock = clock.RealClock{}
	}
	r.store = store
	// default UpdateInterval
	if r.UpdateInterval == 0 {
		r.UpdateInterval = 500 * time.Millisecond
	}
	r.distributionEventQueue = make(chan distributionEvent, 100_000)
	r.status = make(map[string]string)
	r.reverseStatus = make(map[string][]string)
	r.inProgressResources = make(map[string]*inProgressEntry)
	go r.readFromEventQueue()
	if !writeMode {
		return
	}
	r.client = clientSet.CoreV1().ConfigMaps(namespace)
	r.cm = &corev1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:   (r.PodName + "-distribution"),
			Labels: map[string]string{labelKey: "true"},
		},
		Data: make(map[string]string),
	}
	t := r.clock.Tick(r.UpdateInterval)
	ctx := NewIstioContext(stop)
	go func() {
		for {
			select {
			case <-ctx.Done():
				if r.cm != nil {
					// TODO: is the use of a cancelled context here a problem?  Maybe set a short timeout context?
					if err := r.client.Delete(context.Background(), r.cm.Name, metav1.DeleteOptions{}); err != nil {
						scope.Errorf("failed to properly clean up distribution report: %v", err)
					}
				}
				close(r.distributionEventQueue)
				return
			case <-t:
				// TODO, check if report is necessary?  May already be handled by client
				r.writeReport(ctx)
			}
		}
	}()
}

// build a distribution report to send to status leader
func (r *Reporter) buildReport() (DistributionReport, []Resource) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	var finishedResources []Resource
	out := DistributionReport{
		Reporter:            r.PodName,
		DataPlaneCount:      len(r.status),
		InProgressResources: map[string]int{},
	}
	// for every resource in flight
	for _, ipr := range r.inProgressResources {
		res := ipr.Resource
		key := res.String()
		// for every version (nonce) of the config currently in play
		for nonce, dataplanes := range r.reverseStatus {

			// check to see if this version of the config contains this version of the resource
			// it might be more optimal to provide for a full dump of the config at a certain version?
			dpVersion, err := r.store.GetResourceAtVersion(nonce, res.ToModelKey())
			if err == nil && dpVersion == res.ResourceVersion {
				if _, ok := out.InProgressResources[key]; !ok {
					out.InProgressResources[key] = len(dataplanes)
				} else {
					out.InProgressResources[key] += len(dataplanes)
				}
			} else if err != nil {
				scope.Errorf("Encountered error retrieving version %s of key %s from Store: %v", nonce, key, err)
				continue
			} else if nonce == r.store.Version() {
				scope.Warnf("Cache appears to be missing latest version of %s", key)
			}
			if out.InProgressResources[key] >= out.DataPlaneCount {
				// if this resource is done reconciling, let's not worry about it anymore
				finishedResources = append(finishedResources, res)
				// deleting it here doesn't work because we have a read lock and are inside an iterator.
				// TODO: this will leak when a resource never reaches 100% before it is replaced.
				// TODO: do deletes propagate through this thing?
			}
		}
	}
	return out, finishedResources
}

// For efficiency, we don't want to be checking on resources that have already reached 100% distribution.
// When this happens, we remove them from our watch list.
func (r *Reporter) removeCompletedResource(completedResources []Resource) {
	r.mu.Lock()
	defer r.mu.Unlock()
	var toDelete []Resource
	for _, item := range completedResources {
		// TODO: handle cache miss
		total := r.inProgressResources[item.ToModelKey()].completedIterations + 1
		if int64(total) > (time.Minute.Milliseconds() / r.UpdateInterval.Milliseconds()) {
			//remove from inProgressResources // TODO: cleanup completedResources
			toDelete = append(toDelete, item)
		} else {
			r.inProgressResources[item.ToModelKey()].completedIterations = total
		}
	}
	for _, resource := range toDelete {
		delete(r.inProgressResources, resource.ToModelKey())
	}
}

// This function must be called every time a resource change is detected by pilot.  This allows us to lookup
// only the resources we expect to be in flight, not the ones that have already distributed
func (r *Reporter) AddInProgressResource(res model.Config) {
	myRes := ResourceFromModelConfig(res)
	if myRes == nil {
		scope.Errorf("Unable to locate schema for %v, will not update status.", res)
		return
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	r.inProgressResources[myRes.ToModelKey()] = &inProgressEntry{
		Resource:            *myRes,
		completedIterations: 0,
	}
}

func (r *Reporter) DeleteInProgressResource(res model.Config) {
	r.mu.Lock()
	defer r.mu.Unlock()
	delete(r.inProgressResources, res.Key())
}

// generate a distribution report and write it to a ConfigMap for the leader to read.
func (r *Reporter) writeReport(ctx context.Context) {
	report, finishedResources := r.buildReport()
	go r.removeCompletedResource(finishedResources)
	//write to kubernetes here.
	reportbytes, err := yaml.Marshal(report)
	if err != nil {
		scope.Errorf("Error serializing Distribution Report: %v", err)
		return
	}
	r.cm.Data[dataField] = string(reportbytes)
	// TODO: short circuit this write in the leader
	_, err = CreateOrUpdateConfigMap(ctx, r.cm, r.client)
	if err != nil {
		scope.Errorf("Error writing Distribution Report: %v", err)
	}
}

// this is lifted with few modifications from kubeadm's apiclient
func CreateOrUpdateConfigMap(ctx context.Context, cm *corev1.ConfigMap, client v1.ConfigMapInterface) (res *corev1.ConfigMap, err error) {
	if res, err = client.Create(ctx, cm, metav1.CreateOptions{}); err != nil {
		if !apierrors.IsAlreadyExists(err) && !apierrors.IsInvalid(err) {
			scope.Errorf("%v", err)
			return nil, errors.Wrap(err, "unable to create ConfigMap")
		}

		if res, err = client.Update(context.TODO(), cm, metav1.UpdateOptions{}); err != nil {
			return nil, errors.Wrap(err, "unable to update ConfigMap")
		}
	}
	return res, nil
}

type distributionEvent struct {
	conID            string
	distributionType v2.EventType
	nonce            string
}

func (r *Reporter) QueryLastNonce(conID string, distributionType v2.EventType) (noncePrefix string) {
	key := conID + string(distributionType)
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.status[key]
}

// Register that a dataplane has acknowledged a new version of the config.
// Theoretically, we could use the ads connections themselves to harvest this data,
// but the mutex there is pretty hot, and it seems best to trade memory for time.
func (r *Reporter) RegisterEvent(conID string, distributionType v2.EventType, nonce string) {
	d := distributionEvent{nonce: nonce, distributionType: distributionType, conID: conID}
	select {
	case r.distributionEventQueue <- d:
		return
	default:
		scope.Errorf("Distribution Event Queue overwhelmed, status will be invalid.")
	}
}

func (r *Reporter) readFromEventQueue() {
	for ev := range r.distributionEventQueue {
		// TODO might need to batch this to prevent lock contention
		r.processEvent(ev.conID, ev.distributionType, ev.nonce)
	}

}
func (r *Reporter) processEvent(conID string, distributionType v2.EventType, nonce string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.dirty = true
	key := conID + string(distributionType) // TODO: delimit?
	r.deleteKeyFromReverseMap(key)
	var version string
	if len(nonce) > 12 {
		version = nonce[:v2.VersionLen]
	} else {
		version = nonce
	}
	// touch
	r.status[key] = version
	r.reverseStatus[version] = append(r.reverseStatus[version], key)
}

// This is a helper function for keeping our reverseStatus map in step with status.
// must have write lock before calling.
func (r *Reporter) deleteKeyFromReverseMap(key string) {
	if old, ok := r.status[key]; ok {
		if keys, ok := r.reverseStatus[old]; ok {
			for i := range keys {
				if keys[i] == key {
					r.reverseStatus[old] = append(keys[:i], keys[i+1:]...)
					break
				}
			}
			if len(r.reverseStatus[old]) < 1 {
				delete(r.reverseStatus, old)
			}
		}
	}
}

// When a dataplane disconnects, we should no longer count it, nor expect it to ack config.
func (r *Reporter) RegisterDisconnect(conID string, types []v2.EventType) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.dirty = true
	for _, xdsType := range types {
		key := conID + string(xdsType) // TODO: delimit?
		r.deleteKeyFromReverseMap(key)
		delete(r.status, key)
	}
}
<|endoftext|>


<commit_before>package log
// Copyright 2013, David Fisher. 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.
//
// author: David Fisher <ddf1991@gmail.com>
// based on previous package by: Cong Ding <dinggnu@gmail.com>

import (
	"fmt"
	"github.com/coreos/go-systemd/journal"
	"io"
	"strings"
	"sync"
)

const AsyncBuffer = 100

type Sink interface {
	Log(Fields)
}

type writerSink struct {
	lock   sync.Mutex
	out    io.Writer
	format string
	fields []string
}

func (sink *writerSink) Log(fields Fields) {
	vals := make([]interface{}, len(sink.fields))
	for i, field := range sink.fields {
		var ok bool
		vals[i], ok = fields[field]
		if !ok {
			vals[i] = "???"
		}
	}

	sink.lock.Lock()
	defer sink.lock.Unlock()
	fmt.Fprintf(sink.out, sink.format, vals...)
}

func WriterSink(out io.Writer, format string, fields []string) Sink {
	return &writerSink{
		out:    out,
		format: format,
		fields: fields,
	}
}

type journalSink struct{}

func (sink *journalSink) Log(fields Fields) {
	message := fields["message"].(string)
	priority := toJournalPriority(fields["priority"].(Priority))
	journalFields := make(map[string]string)
	for k, v := range fields {
		if k == "message" || k == "priority" {
			continue
		}
		journalFields[strings.ToUpper(k)] = fmt.Sprint(v)
	}
	journal.Send(message, priority, journalFields)
}

func toJournalPriority(priority Priority) journal.Priority {
	switch priority {
	case PriEmerg:
		return journal.PriEmerg
	case PriAlert:
		return journal.PriAlert
	case PriCrit:
		return journal.PriCrit
	case PriErr:
		return journal.PriErr
	case PriWarning:
		return journal.PriWarning
	case PriNotice:
		return journal.PriNotice
	case PriInfo:
		return journal.PriInfo
	case PriDebug:
		return journal.PriDebug

	default:
		return journal.PriErr
	}
}

func JournalSink() Sink {
	return &journalSink{}
}

type tryJournalSink struct {
	j journalSink
	w writerSink
}

func (sink *tryJournalSink) Log(fields Fields) {
	if journal.Enabled() {
		sink.j.Log(fields)
	} else {
		sink.w.Log(fields)
	}
}

func JournalFallbackSink(out io.Writer, format string, fields []string) Sink {
	return &tryJournalSink{
		w: writerSink{
			out:    out,
			format: format,
			fields: fields,
		},
	}
}
<commit_msg>Add priority and null sinks<commit_after>package log

// Copyright 2013, David Fisher. 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.
//
// author: David Fisher <ddf1991@gmail.com>
// based on previous package by: Cong Ding <dinggnu@gmail.com>

import (
	"fmt"
	"github.com/coreos/go-systemd/journal"
	"io"
	"strings"
	"sync"
)

const AsyncBuffer = 100

type Sink interface {
	Log(Fields)
}

type nullSink struct{}

func (sink *nullSink) Log(fields Fields) {}

func NullSink() Sink {
	return &nullSink{}
}

type writerSink struct {
	lock   sync.Mutex
	out    io.Writer
	format string
	fields []string
}

func (sink *writerSink) Log(fields Fields) {
	vals := make([]interface{}, len(sink.fields))
	for i, field := range sink.fields {
		var ok bool
		vals[i], ok = fields[field]
		if !ok {
			vals[i] = "???"
		}
	}

	sink.lock.Lock()
	defer sink.lock.Unlock()
	fmt.Fprintf(sink.out, sink.format, vals...)
}

func WriterSink(out io.Writer, format string, fields []string) Sink {
	return &writerSink{
		out:    out,
		format: format,
		fields: fields,
	}
}

type journalSink struct{}

func (sink *journalSink) Log(fields Fields) {
	message := fields["message"].(string)
	priority := toJournalPriority(fields["priority"].(Priority))
	journalFields := make(map[string]string)
	for k, v := range fields {
		if k == "message" || k == "priority" {
			continue
		}
		journalFields[strings.ToUpper(k)] = fmt.Sprint(v)
	}
	journal.Send(message, priority, journalFields)
}

func toJournalPriority(priority Priority) journal.Priority {
	switch priority {
	case PriEmerg:
		return journal.PriEmerg
	case PriAlert:
		return journal.PriAlert
	case PriCrit:
		return journal.PriCrit
	case PriErr:
		return journal.PriErr
	case PriWarning:
		return journal.PriWarning
	case PriNotice:
		return journal.PriNotice
	case PriInfo:
		return journal.PriInfo
	case PriDebug:
		return journal.PriDebug

	default:
		return journal.PriErr
	}
}

func JournalSink() Sink {
	return &journalSink{}
}

type tryJournalSink struct {
	j journalSink
	w writerSink
}

func (sink *tryJournalSink) Log(fields Fields) {
	if journal.Enabled() {
		sink.j.Log(fields)
	} else {
		sink.w.Log(fields)
	}
}

func JournalFallbackSink(out io.Writer, format string, fields []string) Sink {
	return &tryJournalSink{
		w: writerSink{
			out:    out,
			format: format,
			fields: fields,
		},
	}
}

type priorityFilter struct {
	priority Priority
	target   Sink
}

func (filter *priorityFilter) Log(fields Fields) {
	// lower priority values indicate more important messages
	if fields["priority"].(Priority) <= filter.priority {
		filter.target.Log(fields)
	}
}

func PriorityFilter(priority Priority, target Sink) Sink {
	return &priorityFilter{
		priority: priority,
		target:   target,
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 The Cockroach 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. See the AUTHORS file
// for names of contributors.

package main

import (
	"database/sql"
	"math/rand"
	"time"

	"context"

	"github.com/cockroachdb/cockroach-go/crdb"
)

// 2.7 The Delivery Transaction

// The Delivery business transaction consists of processing a batch of 10 new
// (not yet delivered) orders. Each order is processed (delivered) in full
// within the scope of a read-write database transaction. The number of orders
// delivered as a group (or batched) within the same database transaction is
// implementation specific. The business transaction, comprised of one or more
// (up to 10) database transactions, has a low frequency of execution and must
// complete within a relaxed response time requirement.

// The Delivery transaction is intended to be executed in deferred mode through
// a queuing mechanism, rather than interactively, with terminal response
// indicating transaction completion. The result of the deferred execution is
// recorded into a result file.

type delivery struct{}

var _ tpccTx = newOrder{}

func (del delivery) run(db *sql.DB, wID int) (interface{}, error) {
	oCarrierID := rand.Intn(10) + 1
	olDeliveryD := time.Now()

	if err := crdb.ExecuteTx(
		context.Background(),
		db,
		txOpts,
		func(tx *sql.Tx) error {
			getNewOrder, err := tx.Prepare(`
			SELECT no_o_id
			FROM new_order
			WHERE no_w_id = $1 AND no_d_id = $2
			ORDER BY no_o_id ASC
			LIMIT 1`)
			if err != nil {
				return err
			}
			delNewOrder, err := tx.Prepare(`
			DELETE FROM new_order
			WHERE no_w_id = $1 AND no_d_id = $2 AND no_o_id = $3`)
			if err != nil {
				return err
			}
			updateOrder, err := tx.Prepare(`
			UPDATE "order"
			SET o_carrier_id = $1
			WHERE o_w_id = $2 AND o_d_id = $3 AND o_id = $4
			RETURNING o_c_id`)
			if err != nil {
				return err
			}
			updateOrderLine, err := tx.Prepare(`
			UPDATE order_line
			SET ol_delivery_d = $1
			WHERE ol_w_id = $2 AND ol_d_id = $3 AND ol_o_id = $4`)
			if err != nil {
				return err
			}
			sumOrderLine, err := tx.Prepare(`
			SELECT SUM(ol_amount) FROM order_line
			WHERE ol_w_id = $1 AND ol_d_id = $2 AND ol_o_id = $3`)
			if err != nil {
				return err
			}
			updateCustomer, err := tx.Prepare(`
			UPDATE customer
			SET (c_balance, c_delivery_cnt) =
				(c_Balance + $1, c_delivery_cnt + 1)
			WHERE c_w_id = $2 AND c_d_id = $3 AND c_id = $4`)
			if err != nil {
				return err
			}

			// 2.7.4.2. For each district:
			for dID := 1; dID <= 10; dID++ {
				var oID int
				if err := getNewOrder.QueryRow(wID, dID).Scan(&oID); err != nil {
					// If no matching order is found, the delivery of this order is skipped.
					if err != sql.ErrNoRows {
						return err
					}
					continue
				}
				if _, err := delNewOrder.Exec(wID, dID, oID); err != nil {
					return err
				}
				var oCID int
				if err := updateOrder.QueryRow(oCarrierID, wID, dID, oID).Scan(&oCID); err != nil {
					return err
				}
				if _, err := updateOrderLine.Exec(olDeliveryD, wID, dID, oID); err != nil {
					return err
				}
				var olTotal float64
				if err := sumOrderLine.QueryRow(wID, dID, oID).Scan(&olTotal); err != nil {
					return err
				}
				if _, err := updateCustomer.Exec(olTotal, wID, dID, oID); err != nil {
					return err
				}
			}
			return nil
		}); err != nil {
		return nil, err
	}
	return nil, nil
}
<commit_msg>tpcc: bugfix to delivery<commit_after>// Copyright 2017 The Cockroach 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. See the AUTHORS file
// for names of contributors.

package main

import (
	"database/sql"
	"math/rand"
	"time"

	"context"

	"github.com/cockroachdb/cockroach-go/crdb"
)

// 2.7 The Delivery Transaction

// The Delivery business transaction consists of processing a batch of 10 new
// (not yet delivered) orders. Each order is processed (delivered) in full
// within the scope of a read-write database transaction. The number of orders
// delivered as a group (or batched) within the same database transaction is
// implementation specific. The business transaction, comprised of one or more
// (up to 10) database transactions, has a low frequency of execution and must
// complete within a relaxed response time requirement.

// The Delivery transaction is intended to be executed in deferred mode through
// a queuing mechanism, rather than interactively, with terminal response
// indicating transaction completion. The result of the deferred execution is
// recorded into a result file.

type delivery struct{}

var _ tpccTx = newOrder{}

func (del delivery) run(db *sql.DB, wID int) (interface{}, error) {
	oCarrierID := rand.Intn(10) + 1
	olDeliveryD := time.Now()

	if err := crdb.ExecuteTx(
		context.Background(),
		db,
		txOpts,
		func(tx *sql.Tx) error {
			getNewOrder, err := tx.Prepare(`
			SELECT no_o_id
			FROM new_order
			WHERE no_w_id = $1 AND no_d_id = $2
			ORDER BY no_o_id ASC
			LIMIT 1`)
			if err != nil {
				return err
			}
			delNewOrder, err := tx.Prepare(`
			DELETE FROM new_order
			WHERE no_w_id = $1 AND no_d_id = $2 AND no_o_id = $3`)
			if err != nil {
				return err
			}
			updateOrder, err := tx.Prepare(`
			UPDATE "order"
			SET o_carrier_id = $1
			WHERE o_w_id = $2 AND o_d_id = $3 AND o_id = $4
			RETURNING o_c_id`)
			if err != nil {
				return err
			}
			updateOrderLine, err := tx.Prepare(`
			UPDATE order_line
			SET ol_delivery_d = $1
			WHERE ol_w_id = $2 AND ol_d_id = $3 AND ol_o_id = $4`)
			if err != nil {
				return err
			}
			sumOrderLine, err := tx.Prepare(`
			SELECT SUM(ol_amount) FROM order_line
			WHERE ol_w_id = $1 AND ol_d_id = $2 AND ol_o_id = $3`)
			if err != nil {
				return err
			}
			updateCustomer, err := tx.Prepare(`
			UPDATE customer
			SET (c_balance, c_delivery_cnt) =
				(c_Balance + $1, c_delivery_cnt + 1)
			WHERE c_w_id = $2 AND c_d_id = $3 AND c_id = $4`)
			if err != nil {
				return err
			}

			// 2.7.4.2. For each district:
			for dID := 1; dID <= 10; dID++ {
				var oID int
				if err := getNewOrder.QueryRow(wID, dID).Scan(&oID); err != nil {
					// If no matching order is found, the delivery of this order is skipped.
					if err != sql.ErrNoRows {
						return err
					}
					continue
				}
				if _, err := delNewOrder.Exec(wID, dID, oID); err != nil {
					return err
				}
				var oCID int
				if err := updateOrder.QueryRow(oCarrierID, wID, dID, oID).Scan(&oCID); err != nil {
					return err
				}
				if _, err := updateOrderLine.Exec(olDeliveryD, wID, dID, oID); err != nil {
					return err
				}
				var olTotal float64
				if err := sumOrderLine.QueryRow(wID, dID, oID).Scan(&olTotal); err != nil {
					return err
				}
				if _, err := updateCustomer.Exec(olTotal, wID, dID, oCID); err != nil {
					return err
				}
			}
			return nil
		}); err != nil {
		return nil, err
	}
	return nil, nil
}
<|endoftext|>


<commit_before>package gobot

import (
	"errors"
	"log"
	"os"
	"testing"

	"github.com/hybridgroup/gobot/gobottest"
)

func TestConnectionEach(t *testing.T) {
	r := newTestRobot("Robot1")

	i := 0
	r.Connections().Each(func(conn Connection) {
		i++
	})
	gobottest.Assert(t, r.Connections().Len(), i)
}

func initTestMaster() *Master {
	log.SetOutput(&NullReadWriteCloser{})
	g := NewMaster()
	g.trap = func(c chan os.Signal) {
		c <- os.Interrupt
	}
	g.AddRobot(newTestRobot("Robot1"))
	g.AddRobot(newTestRobot("Robot2"))
	g.AddRobot(newTestRobot(""))
	return g
}

func initTestMaster1Robot() *Master {
	log.SetOutput(&NullReadWriteCloser{})
	g := NewMaster()
	g.trap = func(c chan os.Signal) {
		c <- os.Interrupt
	}
	g.AddRobot(newTestRobot("Robot99"))

	return g
}

func TestVersion(t *testing.T) {
	gobottest.Assert(t, version, Version())
}

func TestNullReadWriteCloser(t *testing.T) {
	n := &NullReadWriteCloser{}
	i, _ := n.Write([]byte{1, 2, 3})
	gobottest.Assert(t, i, 3)
	i, _ = n.Read(make([]byte, 10))
	gobottest.Assert(t, i, 10)
	gobottest.Assert(t, n.Close(), nil)
}

func TestGobotRobot(t *testing.T) {
	g := initTestMaster()
	gobottest.Assert(t, g.Robot("Robot1").Name, "Robot1")
	gobottest.Assert(t, g.Robot("Robot4"), (*Robot)(nil))
	gobottest.Assert(t, g.Robot("Robot4").Device("Device1"), (Device)(nil))
	gobottest.Assert(t, g.Robot("Robot4").Connection("Connection1"), (Connection)(nil))
	gobottest.Assert(t, g.Robot("Robot1").Device("Device4"), (Device)(nil))
	gobottest.Assert(t, g.Robot("Robot1").Device("Device1").Name(), "Device1")
	gobottest.Assert(t, g.Robot("Robot1").Devices().Len(), 3)
	gobottest.Assert(t, g.Robot("Robot1").Connection("Connection4"), (Connection)(nil))
	gobottest.Assert(t, g.Robot("Robot1").Connections().Len(), 3)
}

func TestGobotToJSON(t *testing.T) {
	g := initTestMaster()
	g.AddCommand("test_function", func(params map[string]interface{}) interface{} {
		return nil
	})
	json := NewJSONMaster(g)
	gobottest.Assert(t, len(json.Robots), g.Robots().Len())
	gobottest.Assert(t, len(json.Commands), len(g.Commands()))
}

func TestMasterStart(t *testing.T) {
	g := initTestMaster()
	gobottest.Assert(t, g.Start(), nil)
	gobottest.Assert(t, g.Stop(), nil)
}

func TestMasterStartDriverErrors(t *testing.T) {
	g := initTestMaster1Robot()

	testDriverStart = func() (err error) {
		return errors.New("driver start error 1")
	}

	gobottest.Assert(t, g.Start().Error(), "3 error(s) occurred:\n\n* driver start error 1\n* driver start error 1\n* driver start error 1")
	gobottest.Assert(t, g.Stop(), nil)

	testDriverStart = func() (err error) { return }
}

func TestMasterStartAdaptorErrors(t *testing.T) {
	g := initTestMaster1Robot()

	testAdaptorConnect = func() (err error) {
		return errors.New("adaptor start error 1")
	}

	gobottest.Assert(t, g.Start().Error(), "3 error(s) occurred:\n\n* adaptor start error 1\n* adaptor start error 1\n* adaptor start error 1")
	gobottest.Assert(t, g.Stop(), nil)

	testAdaptorConnect = func() (err error) { return }
}

func TestMasterHaltErrors(t *testing.T) {
	g := initTestMaster1Robot()

	testDriverHalt = func() (err error) {
		return errors.New("driver halt error 2")
	}

	testAdaptorFinalize = func() (err error) {
		return errors.New("adaptor finalize error 2")
	}

	gobottest.Assert(t, g.Start(), nil)
	gobottest.Assert(t, g.Stop().Error(), "3 error(s) occurred:\n\n* adaptor finalize error 2\n* adaptor finalize error 2\n* adaptor finalize error 2")
}
<commit_msg>core: better use of hashicorp/go-multierror for Travis builds<commit_after>package gobot

import (
	"errors"
	"log"
	"os"
	"testing"

	multierror "github.com/hashicorp/go-multierror"
	"github.com/hybridgroup/gobot/gobottest"
)

func TestConnectionEach(t *testing.T) {
	r := newTestRobot("Robot1")

	i := 0
	r.Connections().Each(func(conn Connection) {
		i++
	})
	gobottest.Assert(t, r.Connections().Len(), i)
}

func initTestMaster() *Master {
	log.SetOutput(&NullReadWriteCloser{})
	g := NewMaster()
	g.trap = func(c chan os.Signal) {
		c <- os.Interrupt
	}
	g.AddRobot(newTestRobot("Robot1"))
	g.AddRobot(newTestRobot("Robot2"))
	g.AddRobot(newTestRobot(""))
	return g
}

func initTestMaster1Robot() *Master {
	log.SetOutput(&NullReadWriteCloser{})
	g := NewMaster()
	g.trap = func(c chan os.Signal) {
		c <- os.Interrupt
	}
	g.AddRobot(newTestRobot("Robot99"))

	return g
}

func TestVersion(t *testing.T) {
	gobottest.Assert(t, version, Version())
}

func TestNullReadWriteCloser(t *testing.T) {
	n := &NullReadWriteCloser{}
	i, _ := n.Write([]byte{1, 2, 3})
	gobottest.Assert(t, i, 3)
	i, _ = n.Read(make([]byte, 10))
	gobottest.Assert(t, i, 10)
	gobottest.Assert(t, n.Close(), nil)
}

func TestGobotRobot(t *testing.T) {
	g := initTestMaster()
	gobottest.Assert(t, g.Robot("Robot1").Name, "Robot1")
	gobottest.Assert(t, g.Robot("Robot4"), (*Robot)(nil))
	gobottest.Assert(t, g.Robot("Robot4").Device("Device1"), (Device)(nil))
	gobottest.Assert(t, g.Robot("Robot4").Connection("Connection1"), (Connection)(nil))
	gobottest.Assert(t, g.Robot("Robot1").Device("Device4"), (Device)(nil))
	gobottest.Assert(t, g.Robot("Robot1").Device("Device1").Name(), "Device1")
	gobottest.Assert(t, g.Robot("Robot1").Devices().Len(), 3)
	gobottest.Assert(t, g.Robot("Robot1").Connection("Connection4"), (Connection)(nil))
	gobottest.Assert(t, g.Robot("Robot1").Connections().Len(), 3)
}

func TestGobotToJSON(t *testing.T) {
	g := initTestMaster()
	g.AddCommand("test_function", func(params map[string]interface{}) interface{} {
		return nil
	})
	json := NewJSONMaster(g)
	gobottest.Assert(t, len(json.Robots), g.Robots().Len())
	gobottest.Assert(t, len(json.Commands), len(g.Commands()))
}

func TestMasterStart(t *testing.T) {
	g := initTestMaster()
	gobottest.Assert(t, g.Start(), nil)
	gobottest.Assert(t, g.Stop(), nil)
}

func TestMasterStartDriverErrors(t *testing.T) {
	g := initTestMaster1Robot()
	e := errors.New("driver start error 1")
	testDriverStart = func() (err error) {
		return e
	}

	var expected error
	expected = multierror.Append(expected, e)
	expected = multierror.Append(expected, e)
	expected = multierror.Append(expected, e)

	gobottest.Assert(t, g.Start(), expected)
	gobottest.Assert(t, g.Stop(), nil)

	testDriverStart = func() (err error) { return }
}

func TestMasterStartAdaptorErrors(t *testing.T) {
	g := initTestMaster1Robot()
	e := errors.New("adaptor start error 1")

	testAdaptorConnect = func() (err error) {
		return e
	}

	var expected error
	expected = multierror.Append(expected, e)
	expected = multierror.Append(expected, e)
	expected = multierror.Append(expected, e)

	gobottest.Assert(t, g.Start(), expected)
	gobottest.Assert(t, g.Stop(), nil)

	testAdaptorConnect = func() (err error) { return }
}

func TestMasterFinalizeErrors(t *testing.T) {
	g := initTestMaster1Robot()
	e := errors.New("adaptor finalize error 2")

	testAdaptorFinalize = func() (err error) {
		return e
	}

	var expected error
	expected = multierror.Append(expected, e)
	expected = multierror.Append(expected, e)
	expected = multierror.Append(expected, e)

	gobottest.Assert(t, g.Start(), nil)
	gobottest.Assert(t, g.Stop(), expected)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 validation

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"github.com/emicklei/go-restful/swagger"
	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/errors"
	errs "k8s.io/kubernetes/pkg/util/fielderrors"
	"k8s.io/kubernetes/pkg/util/yaml"
)

type InvalidTypeError struct {
	ExpectedKind reflect.Kind
	ObservedKind reflect.Kind
	FieldName    string
}

func (i *InvalidTypeError) Error() string {
	return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String())
}

func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error {
	return &InvalidTypeError{expected, observed, fieldName}
}

// Schema is an interface that knows how to validate an API object serialized to a byte array.
type Schema interface {
	ValidateBytes(data []byte) error
}

type NullSchema struct{}

func (NullSchema) ValidateBytes(data []byte) error { return nil }

type SwaggerSchema struct {
	api swagger.ApiDeclaration
}

func NewSwaggerSchemaFromBytes(data []byte) (Schema, error) {
	schema := &SwaggerSchema{}
	err := json.Unmarshal(data, &schema.api)
	if err != nil {
		return nil, err
	}
	return schema, nil
}

func (s *SwaggerSchema) ValidateBytes(data []byte) error {
	var obj interface{}
	out, err := yaml.ToJSON(data)
	if err != nil {
		return err
	}
	data = out
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("error in unmarshaling data %s", string(data))
	}
	apiVersion := fields["apiVersion"]
	if apiVersion == nil {
		return fmt.Errorf("apiVersion not set")
	}
	kind := fields["kind"]
	if kind == nil {
		return fmt.Errorf("kind not set")
	}
	allErrs := s.ValidateObject(obj, apiVersion.(string), "", apiVersion.(string)+"."+kind.(string))
	if len(allErrs) == 1 {
		return allErrs[0]
	}
	return errors.NewAggregate(allErrs)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) errs.ValidationErrorList {
	allErrs := errs.ValidationErrorList{}
	models := s.api.Models
	// TODO: handle required fields here too.
	model, ok := models.At(typeName)
	if !ok {
		return append(allErrs, fmt.Errorf("couldn't find type: %s", typeName))
	}
	properties := model.Properties
	if len(properties.List) == 0 {
		// The object does not have any sub-fields.
		return nil
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	// handle required fields
	for _, requiredKey := range model.Required {
		if _, ok := fields[requiredKey]; !ok {
			allErrs = append(allErrs, fmt.Errorf("field %s: is required", requiredKey))
		}
	}
	for key, value := range fields {
		details, ok := properties.At(key)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName))
			continue
		}
		if details.Type == nil && details.Ref == nil {
			allErrs = append(allErrs, fmt.Errorf("could not find the type of %s from object: %v", key, details))
		}
		var fieldType string
		if details.Type != nil {
			fieldType = *details.Type
		} else {
			fieldType = *details.Ref
		}
		if value == nil {
			glog.V(2).Infof("Skipping nil field: %s", key)
			continue
		}
		errs := s.validateField(value, apiVersion, fieldName+key, fieldType, &details)
		if len(errs) > 0 {
			glog.Errorf("Validation failed for: %s, %v", key, value)
			allErrs = append(allErrs, errs...)
		}
	}
	return allErrs
}

func (s *SwaggerSchema) validateField(value interface{}, apiVersion, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) errs.ValidationErrorList {
	if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, apiVersion, fieldName, fieldType)
	}
	allErrs := errs.ValidationErrorList{}
	switch fieldType {
	case "string":
		// Be loose about what we accept for 'string' since we use IntOrString in a couple of places
		_, isString := value.(string)
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isString && !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		var arrType string
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		if fieldDetails.Items.Ref != nil {
			arrType = *fieldDetails.Items.Ref
		} else {
			arrType = *fieldDetails.Items.Type
		}
		for ix := range arr {
			errs := s.validateField(arr[ix], apiVersion, fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "any":
	default:
		return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType))
	}
	return allErrs
}
<commit_msg>Make --validate default on and shows how to turn if off<commit_after>/*
Copyright 2014 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 validation

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"github.com/emicklei/go-restful/swagger"
	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/errors"
	errs "k8s.io/kubernetes/pkg/util/fielderrors"
	"k8s.io/kubernetes/pkg/util/yaml"
)

type InvalidTypeError struct {
	ExpectedKind reflect.Kind
	ObservedKind reflect.Kind
	FieldName    string
}

func (i *InvalidTypeError) Error() string {
	return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String())
}

func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error {
	return &InvalidTypeError{expected, observed, fieldName}
}

// Schema is an interface that knows how to validate an API object serialized to a byte array.
type Schema interface {
	ValidateBytes(data []byte) error
}

type NullSchema struct{}

func (NullSchema) ValidateBytes(data []byte) error { return nil }

type SwaggerSchema struct {
	api swagger.ApiDeclaration
}

func NewSwaggerSchemaFromBytes(data []byte) (Schema, error) {
	schema := &SwaggerSchema{}
	err := json.Unmarshal(data, &schema.api)
	if err != nil {
		return nil, err
	}
	return schema, nil
}

func (s *SwaggerSchema) ValidateBytes(data []byte) error {
	var obj interface{}
	out, err := yaml.ToJSON(data)
	if err != nil {
		return err
	}
	data = out
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("error in unmarshaling data %s", string(data))
	}
	apiVersion := fields["apiVersion"]
	if apiVersion == nil {
		return fmt.Errorf("apiVersion not set")
	}
	kind := fields["kind"]
	if kind == nil {
		return fmt.Errorf("kind not set")
	}
	allErrs := s.ValidateObject(obj, apiVersion.(string), "", apiVersion.(string)+"."+kind.(string))
	if len(allErrs) == 1 {
		return allErrs[0]
	}
	return errors.NewAggregate(allErrs)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) errs.ValidationErrorList {
	allErrs := errs.ValidationErrorList{}
	models := s.api.Models
	// TODO: handle required fields here too.
	model, ok := models.At(typeName)
	if !ok {
		return append(allErrs, fmt.Errorf("couldn't find type: %s", typeName))
	}
	properties := model.Properties
	if len(properties.List) == 0 {
		// The object does not have any sub-fields.
		return nil
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	// handle required fields
	for _, requiredKey := range model.Required {
		if _, ok := fields[requiredKey]; !ok {
			allErrs = append(allErrs, fmt.Errorf("field %s: is required", requiredKey))
		}
	}
	for key, value := range fields {
		details, ok := properties.At(key)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName))
			continue
		}
		if details.Type == nil && details.Ref == nil {
			allErrs = append(allErrs, fmt.Errorf("could not find the type of %s from object: %v", key, details))
		}
		var fieldType string
		if details.Type != nil {
			fieldType = *details.Type
		} else {
			fieldType = *details.Ref
		}
		if value == nil {
			glog.V(2).Infof("Skipping nil field: %s", key)
			continue
		}
		errs := s.validateField(value, apiVersion, fieldName+key, fieldType, &details)
		if len(errs) > 0 {
			allErrs = append(allErrs, errs...)
		}
	}
	return allErrs
}

func (s *SwaggerSchema) validateField(value interface{}, apiVersion, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) errs.ValidationErrorList {
	if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, apiVersion, fieldName, fieldType)
	}
	allErrs := errs.ValidationErrorList{}
	switch fieldType {
	case "string":
		// Be loose about what we accept for 'string' since we use IntOrString in a couple of places
		_, isString := value.(string)
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isString && !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		var arrType string
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		if fieldDetails.Items.Ref != nil {
			arrType = *fieldDetails.Items.Ref
		} else {
			arrType = *fieldDetails.Items.Type
		}
		for ix := range arr {
			errs := s.validateField(arr[ix], apiVersion, fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "any":
	default:
		return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType))
	}
	return allErrs
}
<|endoftext|>