<commit_before>// Copyright 2017 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 log

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strings"
	"text/template"
	"time"

	"cloud.google.com/go/logging"
	"cloud.google.com/go/logging/logadmin"
	xctx "golang.org/x/net/context"
	"google.golang.org/api/option"
	"google.golang.org/genproto/googleapis/api/monitoredres"

	"istio.io/istio/mixer/adapter/stackdriver/config"
	"istio.io/istio/mixer/adapter/stackdriver/helper"
	"istio.io/istio/mixer/pkg/adapter"
	"istio.io/istio/mixer/pkg/pool"
	"istio.io/istio/mixer/template/logentry"
)

type (
	makeClientFn     func(ctx xctx.Context, projectID string, opts ...option.ClientOption) (*logging.Client, error)
	makeSyncClientFn func(ctx xctx.Context, projectID string, opts ...option.ClientOption) (*logadmin.Client, error)

	logFn   func(logging.Entry)
	flushFn func() error

	builder struct {
		makeClient     makeClientFn
		makeSyncClient makeSyncClientFn
		types          map[string]*logentry.Type
		mg             helper.MetadataGenerator
		cfg            *config.Params
	}

	info struct {
		labels []string
		tmpl   *template.Template
		req    *config.Params_LogInfo_HttpRequestMapping
		log    logFn
		flush  flushFn
	}

	handler struct {
		now func() time.Time // used for testing

		l                  adapter.Logger
		client, syncClient io.Closer
		info               map[string]info
		md                 helper.Metadata
	}
)

var (
	// compile-time assertion that we implement the interfaces we promise
	_ logentry.HandlerBuilder = &builder{}
	_ logentry.Handler        = &handler{}
)

// NewBuilder returns a builder implementing the logentry.HandlerBuilder interface.
func NewBuilder(mg helper.MetadataGenerator) logentry.HandlerBuilder {
	return &builder{makeClient: logging.NewClient, makeSyncClient: logadmin.NewClient, mg: mg}
}

func (b *builder) SetLogEntryTypes(types map[string]*logentry.Type) {
	b.types = types
}
func (b *builder) SetAdapterConfig(cfg adapter.Config) {
	b.cfg = cfg.(*config.Params)
}
func (b *builder) Validate() *adapter.ConfigErrors {
	return nil
}

func (b *builder) Build(ctx context.Context, env adapter.Env) (adapter.Handler, error) {
	logger := env.Logger()
	md := b.mg.GenerateMetadata()
	cfg := b.cfg
	if cfg.ProjectId == "" {
		// Try to fill project id with Metadata if it is not provided.
		cfg.ProjectId = md.ProjectID
	}
	client, err := b.makeClient(context.Background(), cfg.ProjectId, helper.ToOpts(cfg)...)
	if err != nil {
		return nil, fmt.Errorf("failed to create stackdriver logging client: %v", err)
	}
	client.OnError = func(err error) {
		_ = logger.Errorf("Stackdriver logger failed with: %v", err)
	}

	syncClient, err := b.makeSyncClient(context.Background(), cfg.ProjectId, helper.ToOpts(cfg)...)
	if err != nil {
		_ = logger.Errorf("failed to create stackdriver sink logging client: %v", err)
		syncClient = nil
	}

	infos := make(map[string]info)
	for name, log := range cfg.LogInfo {
		_, found := b.types[name]
		if !found {
			logger.Infof("configured with log info about %s which is not an Istio log", name)
			continue
		}
		tmpl, err := template.New(name).Parse(log.PayloadTemplate)
		if err != nil {
			_ = logger.Errorf("failed to evaluate template for log %s, skipping: %v", name, err)
			continue
		}

		if log.SinkInfo != nil {
			sink := &logadmin.Sink{
				ID:          log.SinkInfo.Id,
				Destination: log.SinkInfo.Destination,
				Filter:      log.SinkInfo.Filter,
			}
			ctx := context.Background()
			var sinkErr error
			// first try to CreateSink, if error is AlreadyExists then update that sink.
			if _, sinkErr = syncClient.CreateSinkOpt(ctx, sink,
				logadmin.SinkOptions{UniqueWriterIdentity: log.SinkInfo.UniqueWriterIdentity}); isAlreadyExists(sinkErr) {
				_, sinkErr = syncClient.UpdateSinkOpt(ctx, sink,
					logadmin.SinkOptions{UpdateDestination: log.SinkInfo.UpdateDestination,
						UpdateFilter:          log.SinkInfo.UpdateFilter,
						UpdateIncludeChildren: log.SinkInfo.UpdateIncludeChildren})
			}
			if sinkErr != nil {
				logger.Warningf("failed to create/update stackdriver logging sink: %v", sinkErr)
			}
		}

		infos[name] = info{
			labels: log.LabelNames,
			tmpl:   tmpl,
			req:    log.HttpMapping,
			log:    client.Logger(name).Log,
			flush:  client.Logger(name).Flush,
		}
	}
	return &handler{client: client, syncClient: syncClient, now: time.Now, l: logger, info: infos, md: md}, nil
}

func (h *handler) HandleLogEntry(_ context.Context, values []*logentry.Instance) error {
	for _, v := range values {
		linfo, found := h.info[v.Name]
		if !found {
			h.l.Warningf("got instance for unknown log '%s', skipping", v.Name)
			continue
		}

		defer func() {
			err := linfo.flush()
			if err != nil {
				h.l.Warningf("failed to flush log entry: %v", err)
			}
		}()

		buf := pool.GetBuffer()
		if err := linfo.tmpl.Execute(buf, v.Variables); err != nil {
			// We'll just continue on with an empty payload for this entry - we could still be populating the HTTP req with valuable info, for example.
			_ = h.l.Errorf("failed to execute template for log '%s': %v", v.Name, err)
		}
		payload := buf.String()
		pool.PutBuffer(buf)

		e := logging.Entry{
			Timestamp:   h.now(), // TODO: use timestamp on Instance when timestamps work
			Severity:    logging.ParseSeverity(v.Severity),
			Labels:      toLabelMap(linfo.labels, v.Variables),
			Payload:     payload,
			HTTPRequest: toReq(linfo.req, v.Variables),
		}

		// If we don't set a resource the SDK will populate a global resource for us.
		if v.MonitoredResourceType != "" {
			labels := helper.ToStringMap(v.MonitoredResourceDimensions)
			h.md.FillProjectMetadata(labels)
			e.Resource = &monitoredres.MonitoredResource{
				Type:   v.MonitoredResourceType,
				Labels: labels,
			}
		}
		linfo.log(e)
	}
	return nil
}

func (h *handler) Close() error {
	if h.syncClient != nil {
		if err := h.syncClient.Close(); err != nil {
			h.l.Warningf("Failed to close sync client %v", err)
		}
	}

	return h.client.Close()
}

func toLabelMap(names []string, variables map[string]interface{}) map[string]string {
	out := make(map[string]string, len(names))
	for _, name := range names {
		v := variables[name]
		switch vt := v.(type) {
		case string:
			out[name] = vt
		case []byte:
			out[name] = net.IP(vt).String()
		default:
			out[name] = fmt.Sprintf("%v", vt)
		}
	}
	return out
}

func toReq(mapping *config.Params_LogInfo_HttpRequestMapping, variables map[string]interface{}) *logging.HTTPRequest {
	if mapping == nil {
		return nil
	}

	reqURL := &url.URL{}
	if variables[mapping.Url] != nil {
		if u, err := url.Parse(variables[mapping.Url].(string)); err == nil {
			reqURL = u
		}
	}
	method := ""
	if variables[mapping.Method] != nil {
		method = variables[mapping.Method].(string)
	}
	httpHeaders := make(http.Header)
	if variables[mapping.UserAgent] != nil {
		httpHeaders.Add("User-Agent", variables[mapping.UserAgent].(string))
	}
	if variables[mapping.Referer] != nil {
		httpHeaders.Add("Referer", variables[mapping.Referer].(string))
	}
	// Required to make the Stackdriver client lib not barf.
	req := &logging.HTTPRequest{
		Request: &http.Request{URL: reqURL, Method: method, Header: httpHeaders},
	}

	reqs := variables[mapping.RequestSize]
	if reqsize, ok := toInt64(reqs); ok {
		req.RequestSize = reqsize
	}

	resps := variables[mapping.ResponseSize]
	if respsize, ok := toInt64(resps); ok {
		req.ResponseSize = respsize
	}

	code := variables[mapping.Status]
	if status, ok := code.(int64); ok {
		req.Status = int(status)
	}

	l := variables[mapping.Latency]
	if latency, ok := l.(time.Duration); ok {
		req.Latency = latency
	}

	req.LocalIP = adapter.Stringify(variables[mapping.LocalIp])
	req.RemoteIP = adapter.Stringify(variables[mapping.RemoteIp])
	return req
}

func toInt64(v interface{}) (int64, bool) {
	// case int, int8, int16, ...: return int64(i), true
	// does not compile because go can't handle the type conversion for multiple types in a single branch.
	switch i := v.(type) {
	case int:
		return int64(i), true
	case int64:
		return i, true
	default:
		return 0, false
	}
}

func isAlreadyExists(err error) bool {
	if err == nil {
		return false
	}
	return strings.Contains(err.Error(), "AlreadyExists")
}
<commit_msg>Fix flush behavior in Stackdriver adapter. (#12853)<commit_after>// Copyright 2017 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 log

import (
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strings"
	"text/template"
	"time"

	"cloud.google.com/go/logging"
	"cloud.google.com/go/logging/logadmin"
	xctx "golang.org/x/net/context"
	"google.golang.org/api/option"
	"google.golang.org/genproto/googleapis/api/monitoredres"

	"istio.io/istio/mixer/adapter/stackdriver/config"
	"istio.io/istio/mixer/adapter/stackdriver/helper"
	"istio.io/istio/mixer/pkg/adapter"
	"istio.io/istio/mixer/pkg/pool"
	"istio.io/istio/mixer/template/logentry"
)

type (
	makeClientFn     func(ctx xctx.Context, projectID string, opts ...option.ClientOption) (*logging.Client, error)
	makeSyncClientFn func(ctx xctx.Context, projectID string, opts ...option.ClientOption) (*logadmin.Client, error)

	logFn   func(logging.Entry)
	flushFn func() error

	builder struct {
		makeClient     makeClientFn
		makeSyncClient makeSyncClientFn
		types          map[string]*logentry.Type
		mg             helper.MetadataGenerator
		cfg            *config.Params
	}

	info struct {
		labels []string
		tmpl   *template.Template
		req    *config.Params_LogInfo_HttpRequestMapping
		log    logFn
		flush  flushFn
	}

	handler struct {
		now func() time.Time // used for testing

		l                  adapter.Logger
		client, syncClient io.Closer
		info               map[string]info
		md                 helper.Metadata
	}
)

var (
	// compile-time assertion that we implement the interfaces we promise
	_ logentry.HandlerBuilder = &builder{}
	_ logentry.Handler        = &handler{}
)

// NewBuilder returns a builder implementing the logentry.HandlerBuilder interface.
func NewBuilder(mg helper.MetadataGenerator) logentry.HandlerBuilder {
	return &builder{makeClient: logging.NewClient, makeSyncClient: logadmin.NewClient, mg: mg}
}

func (b *builder) SetLogEntryTypes(types map[string]*logentry.Type) {
	b.types = types
}
func (b *builder) SetAdapterConfig(cfg adapter.Config) {
	b.cfg = cfg.(*config.Params)
}
func (b *builder) Validate() *adapter.ConfigErrors {
	return nil
}

func (b *builder) Build(ctx context.Context, env adapter.Env) (adapter.Handler, error) {
	logger := env.Logger()
	md := b.mg.GenerateMetadata()
	cfg := b.cfg
	if cfg.ProjectId == "" {
		// Try to fill project id with Metadata if it is not provided.
		cfg.ProjectId = md.ProjectID
	}
	client, err := b.makeClient(context.Background(), cfg.ProjectId, helper.ToOpts(cfg)...)
	if err != nil {
		return nil, fmt.Errorf("failed to create stackdriver logging client: %v", err)
	}
	client.OnError = func(err error) {
		_ = logger.Errorf("Stackdriver logger failed with: %v", err)
	}

	syncClient, err := b.makeSyncClient(context.Background(), cfg.ProjectId, helper.ToOpts(cfg)...)
	if err != nil {
		_ = logger.Errorf("failed to create stackdriver sink logging client: %v", err)
		syncClient = nil
	}

	infos := make(map[string]info)
	for name, log := range cfg.LogInfo {
		_, found := b.types[name]
		if !found {
			logger.Infof("configured with log info about %s which is not an Istio log", name)
			continue
		}
		tmpl, err := template.New(name).Parse(log.PayloadTemplate)
		if err != nil {
			_ = logger.Errorf("failed to evaluate template for log %s, skipping: %v", name, err)
			continue
		}

		if log.SinkInfo != nil {
			sink := &logadmin.Sink{
				ID:          log.SinkInfo.Id,
				Destination: log.SinkInfo.Destination,
				Filter:      log.SinkInfo.Filter,
			}
			ctx := context.Background()
			var sinkErr error
			// first try to CreateSink, if error is AlreadyExists then update that sink.
			if _, sinkErr = syncClient.CreateSinkOpt(ctx, sink,
				logadmin.SinkOptions{UniqueWriterIdentity: log.SinkInfo.UniqueWriterIdentity}); isAlreadyExists(sinkErr) {
				_, sinkErr = syncClient.UpdateSinkOpt(ctx, sink,
					logadmin.SinkOptions{UpdateDestination: log.SinkInfo.UpdateDestination,
						UpdateFilter:          log.SinkInfo.UpdateFilter,
						UpdateIncludeChildren: log.SinkInfo.UpdateIncludeChildren})
			}
			if sinkErr != nil {
				logger.Warningf("failed to create/update stackdriver logging sink: %v", sinkErr)
			}
		}

		infos[name] = info{
			labels: log.LabelNames,
			tmpl:   tmpl,
			req:    log.HttpMapping,
			log:    client.Logger(name).Log,
			flush:  client.Logger(name).Flush,
		}
	}
	return &handler{client: client, syncClient: syncClient, now: time.Now, l: logger, info: infos, md: md}, nil
}

func (h *handler) HandleLogEntry(_ context.Context, values []*logentry.Instance) error {
	for _, v := range values {
		linfo, found := h.info[v.Name]
		if !found {
			h.l.Warningf("got instance for unknown log '%s', skipping", v.Name)
			continue
		}

		buf := pool.GetBuffer()
		if err := linfo.tmpl.Execute(buf, v.Variables); err != nil {
			// We'll just continue on with an empty payload for this entry - we could still be populating the HTTP req with valuable info, for example.
			_ = h.l.Errorf("failed to execute template for log '%s': %v", v.Name, err)
		}
		payload := buf.String()
		pool.PutBuffer(buf)

		e := logging.Entry{
			Timestamp:   h.now(), // TODO: use timestamp on Instance when timestamps work
			Severity:    logging.ParseSeverity(v.Severity),
			Labels:      toLabelMap(linfo.labels, v.Variables),
			Payload:     payload,
			HTTPRequest: toReq(linfo.req, v.Variables),
		}

		// If we don't set a resource the SDK will populate a global resource for us.
		if v.MonitoredResourceType != "" {
			labels := helper.ToStringMap(v.MonitoredResourceDimensions)
			h.md.FillProjectMetadata(labels)
			e.Resource = &monitoredres.MonitoredResource{
				Type:   v.MonitoredResourceType,
				Labels: labels,
			}
		}
		linfo.log(e)
	}

	for name, linfo := range h.info {
		err := linfo.flush()
		if err != nil {
			h.l.Warningf("failed to flush log %s: %v", name, err)
		}
	}
	return nil
}

func (h *handler) Close() error {
	if h.syncClient != nil {
		if err := h.syncClient.Close(); err != nil {
			h.l.Warningf("Failed to close sync client %v", err)
		}
	}

	return h.client.Close()
}

func toLabelMap(names []string, variables map[string]interface{}) map[string]string {
	out := make(map[string]string, len(names))
	for _, name := range names {
		v := variables[name]
		switch vt := v.(type) {
		case string:
			out[name] = vt
		case []byte:
			out[name] = net.IP(vt).String()
		default:
			out[name] = fmt.Sprintf("%v", vt)
		}
	}
	return out
}

func toReq(mapping *config.Params_LogInfo_HttpRequestMapping, variables map[string]interface{}) *logging.HTTPRequest {
	if mapping == nil {
		return nil
	}

	reqURL := &url.URL{}
	if variables[mapping.Url] != nil {
		if u, err := url.Parse(variables[mapping.Url].(string)); err == nil {
			reqURL = u
		}
	}
	method := ""
	if variables[mapping.Method] != nil {
		method = variables[mapping.Method].(string)
	}
	httpHeaders := make(http.Header)
	if variables[mapping.UserAgent] != nil {
		httpHeaders.Add("User-Agent", variables[mapping.UserAgent].(string))
	}
	if variables[mapping.Referer] != nil {
		httpHeaders.Add("Referer", variables[mapping.Referer].(string))
	}
	// Required to make the Stackdriver client lib not barf.
	req := &logging.HTTPRequest{
		Request: &http.Request{URL: reqURL, Method: method, Header: httpHeaders},
	}

	reqs := variables[mapping.RequestSize]
	if reqsize, ok := toInt64(reqs); ok {
		req.RequestSize = reqsize
	}

	resps := variables[mapping.ResponseSize]
	if respsize, ok := toInt64(resps); ok {
		req.ResponseSize = respsize
	}

	code := variables[mapping.Status]
	if status, ok := code.(int64); ok {
		req.Status = int(status)
	}

	l := variables[mapping.Latency]
	if latency, ok := l.(time.Duration); ok {
		req.Latency = latency
	}

	req.LocalIP = adapter.Stringify(variables[mapping.LocalIp])
	req.RemoteIP = adapter.Stringify(variables[mapping.RemoteIp])
	return req
}

func toInt64(v interface{}) (int64, bool) {
	// case int, int8, int16, ...: return int64(i), true
	// does not compile because go can't handle the type conversion for multiple types in a single branch.
	switch i := v.(type) {
	case int:
		return int64(i), true
	case int64:
		return i, true
	default:
		return 0, false
	}
}

func isAlreadyExists(err error) bool {
	if err == nil {
		return false
	}
	return strings.Contains(err.Error(), "AlreadyExists")
}
<|endoftext|>


<commit_before>package dumpformats

import (
	"bufio"
	"io"
	"path"
	"strconv"

	"github.com/anaminus/rbxmk/dump"
)

func init() { register(Fragments) }

var Fragments = Format{
	Name: "fragments",
	Func: func(w io.Writer, root dump.Root) error {
		buf := bufio.NewWriter(w)
		ListFragments(root, func(path, ref string) {
			switch ref {
			case "":
				buf.WriteString(">>>> ")
				buf.WriteString(path)
			case "$TODO":
				buf.WriteString("---- ")
				buf.WriteString(path)
			default:
				buf.WriteString(ref)
			}
			buf.WriteString("\n")
		})
		return buf.Flush()
	},
}

// ListFragments traverses root, calling write for each fragment reference that
// was found. ref is the fragment reference, and path describes the location of
// the reference within root.
func ListFragments(root dump.Root, write func(path, ref string)) {
	for _, library := range root.Libraries {
		p := path.Join("libraries", library.Name)
		write(path.Join(p, "Summary"), library.Struct.Summary)
		write(path.Join(p, "Description"), library.Struct.Description)
		fragWriteFields(write, path.Join(p, "Fields"), library.Struct.Fields)
		fragWriteTypes(write, path.Join(p, "Types"), library.Types)
	}
	fragWriteTypes(write, "types", root.Types)
	fragWriteFormats(write, "formats", root.Formats)
	for _, frag := range root.Fragments {
		write("fragments", frag)
	}
}

func fragWriteFormats(write func(path, ref string), p string, formats dump.Formats) {
	sortFormats(formats, func(formatName string, format dump.Format) {
		p := path.Join(p, formatName)
		write(path.Join(p, "Summary"), format.Summary)
		write(path.Join(p, "Description"), format.Description)
	})
}

func fragWriteFields(write func(path, ref string), p string, fields dump.Fields) {
	sortFields(fields, func(fieldName string, field dump.Value) {
		p := path.Join(p, fieldName)
		switch value := field.(type) {
		case dump.Property:
			write(path.Join(p, "Summary"), value.Summary)
			write(path.Join(p, "Description"), value.Description)
		case dump.Struct:
			write(path.Join(p, "Summary"), value.Summary)
			write(path.Join(p, "Description"), value.Description)
			fragWriteFields(write, p, value.Fields)
		case dump.Function:
			write(path.Join(p, "Summary"), value.Summary)
			write(path.Join(p, "Description"), value.Description)
		case dump.MultiFunction:
			for i, value := range value {
				write(path.Join(p, strconv.Itoa(i), "Summary"), value.Summary)
				write(path.Join(p, strconv.Itoa(i), "Description"), value.Description)
			}
		}
	})
}

func fragWriteTypes(write func(path, ref string), p string, types dump.TypeDefs) {
	sortTypeDefs(types, func(defName string, def dump.TypeDef) {
		p := path.Join(p, defName)
		write(path.Join(p, "Summary"), def.Summary)
		write(path.Join(p, "Description"), def.Description)
		sortConstructors(def.Constructors, func(ctorName string, ctor dump.MultiFunction) {
			p := path.Join(p, "Constructors", ctorName)
			for i, fn := range ctor {
				p := path.Join(p, strconv.Itoa(i))
				write(path.Join(p, "Summary"), fn.Summary)
				write(path.Join(p, "Description"), fn.Description)
			}
		})
		sortProperties(def.Properties, func(propName string, prop dump.Property) {
			p := path.Join(p, "Properties", propName)
			write(path.Join(p, "Summary"), prop.Summary)
			write(path.Join(p, "Description"), prop.Description)
		})
		sortProperties(def.Symbols, func(symName string, prop dump.Property) {
			p := path.Join(p, "Symbols", symName)
			write(path.Join(p, "Summary"), prop.Summary)
			write(path.Join(p, "Description"), prop.Description)
		})
		sortMethods(def.Methods, func(methodName string, method dump.Function) {
			p := path.Join(p, "Methods", methodName)
			write(path.Join(p, "Summary"), method.Summary)
			write(path.Join(p, "Description"), method.Description)
		})
		if op := def.Operators; op != nil {
			p := path.Join(p, "Operators")
			for i, fn := range op.Add {
				write(path.Join(p, "Add", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Add", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Sub {
				write(path.Join(p, "Sub", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Sub", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Mul {
				write(path.Join(p, "Mul", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Mul", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Div {
				write(path.Join(p, "Div", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Div", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Mod {
				write(path.Join(p, "Mod", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Mod", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Pow {
				write(path.Join(p, "Pow", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Pow", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Concat {
				write(path.Join(p, "Concat", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Concat", strconv.Itoa(i), "Description"), fn.Description)
			}
			if op.Eq != nil {
				write(path.Join(p, "Eq", "Summary"), op.Eq.Summary)
				write(path.Join(p, "Eq", "Description"), op.Eq.Description)
			}
			if op.Lt != nil {
				write(path.Join(p, "Lt", "Summary"), op.Lt.Summary)
				write(path.Join(p, "Lt", "Description"), op.Lt.Description)
			}
			if op.Le != nil {
				write(path.Join(p, "Le", "Summary"), op.Le.Summary)
				write(path.Join(p, "Le", "Description"), op.Le.Description)
			}
			if op.Len != nil {
				write(path.Join(p, "Len", "Summary"), op.Len.Summary)
				write(path.Join(p, "Len", "Description"), op.Len.Description)
			}
			if op.Unm != nil {
				write(path.Join(p, "Unm", "Summary"), op.Unm.Summary)
				write(path.Join(p, "Unm", "Description"), op.Unm.Description)
			}
			if op.Call != nil {
				write(path.Join(p, "Call", "Summary"), op.Call.Summary)
				write(path.Join(p, "Call", "Description"), op.Call.Description)
			}
			if op.Index != nil {
				write(path.Join(p, "Index", "Summary"), op.Index.Summary)
				write(path.Join(p, "Index", "Description"), op.Index.Description)
			}
			if op.Newindex != nil {
				write(path.Join(p, "Newindex", "Summary"), op.Newindex.Summary)
				write(path.Join(p, "Newindex", "Description"), op.Newindex.Description)
			}
		}
	})
}
<commit_msg>Include top-level fragments.<commit_after>package dumpformats

import (
	"bufio"
	"io"
	"path"
	"strconv"

	"github.com/anaminus/rbxmk/dump"
)

func init() { register(Fragments) }

var Fragments = Format{
	Name: "fragments",
	Func: func(w io.Writer, root dump.Root) error {
		buf := bufio.NewWriter(w)
		ListFragments(root, func(path, ref string) {
			switch ref {
			case "":
				buf.WriteString(">>>> ")
				buf.WriteString(path)
			case "$TODO":
				buf.WriteString("---- ")
				buf.WriteString(path)
			default:
				buf.WriteString(ref)
			}
			buf.WriteString("\n")
		})
		return buf.Flush()
	},
}

// ListFragments traverses root, calling write for each fragment reference that
// was found. ref is the fragment reference, and path describes the location of
// the reference within root.
func ListFragments(root dump.Root, write func(path, ref string)) {
	write("libraries", "Libraries")
	for _, library := range root.Libraries {
		p := path.Join("libraries", library.Name)
		write(path.Join(p, "Summary"), library.Struct.Summary)
		write(path.Join(p, "Description"), library.Struct.Description)
		fragWriteFields(write, path.Join(p, "Fields"), library.Struct.Fields)
		fragWriteTypes(write, path.Join(p, "Types"), library.Types)
	}
	write("types", "Types")
	fragWriteTypes(write, "types", root.Types)
	write("formats", "Formats")
	fragWriteFormats(write, "formats", root.Formats)
	write("fragments", "Fragments")
	for _, frag := range root.Fragments {
		write("fragments", frag)
	}
}

func fragWriteFormats(write func(path, ref string), p string, formats dump.Formats) {
	sortFormats(formats, func(formatName string, format dump.Format) {
		p := path.Join(p, formatName)
		write(path.Join(p, "Summary"), format.Summary)
		write(path.Join(p, "Description"), format.Description)
	})
}

func fragWriteFields(write func(path, ref string), p string, fields dump.Fields) {
	sortFields(fields, func(fieldName string, field dump.Value) {
		p := path.Join(p, fieldName)
		switch value := field.(type) {
		case dump.Property:
			write(path.Join(p, "Summary"), value.Summary)
			write(path.Join(p, "Description"), value.Description)
		case dump.Struct:
			write(path.Join(p, "Summary"), value.Summary)
			write(path.Join(p, "Description"), value.Description)
			fragWriteFields(write, p, value.Fields)
		case dump.Function:
			write(path.Join(p, "Summary"), value.Summary)
			write(path.Join(p, "Description"), value.Description)
		case dump.MultiFunction:
			for i, value := range value {
				write(path.Join(p, strconv.Itoa(i), "Summary"), value.Summary)
				write(path.Join(p, strconv.Itoa(i), "Description"), value.Description)
			}
		}
	})
}

func fragWriteTypes(write func(path, ref string), p string, types dump.TypeDefs) {
	sortTypeDefs(types, func(defName string, def dump.TypeDef) {
		p := path.Join(p, defName)
		write(path.Join(p, "Summary"), def.Summary)
		write(path.Join(p, "Description"), def.Description)
		sortConstructors(def.Constructors, func(ctorName string, ctor dump.MultiFunction) {
			p := path.Join(p, "Constructors", ctorName)
			for i, fn := range ctor {
				p := path.Join(p, strconv.Itoa(i))
				write(path.Join(p, "Summary"), fn.Summary)
				write(path.Join(p, "Description"), fn.Description)
			}
		})
		sortProperties(def.Properties, func(propName string, prop dump.Property) {
			p := path.Join(p, "Properties", propName)
			write(path.Join(p, "Summary"), prop.Summary)
			write(path.Join(p, "Description"), prop.Description)
		})
		sortProperties(def.Symbols, func(symName string, prop dump.Property) {
			p := path.Join(p, "Symbols", symName)
			write(path.Join(p, "Summary"), prop.Summary)
			write(path.Join(p, "Description"), prop.Description)
		})
		sortMethods(def.Methods, func(methodName string, method dump.Function) {
			p := path.Join(p, "Methods", methodName)
			write(path.Join(p, "Summary"), method.Summary)
			write(path.Join(p, "Description"), method.Description)
		})
		if op := def.Operators; op != nil {
			p := path.Join(p, "Operators")
			for i, fn := range op.Add {
				write(path.Join(p, "Add", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Add", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Sub {
				write(path.Join(p, "Sub", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Sub", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Mul {
				write(path.Join(p, "Mul", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Mul", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Div {
				write(path.Join(p, "Div", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Div", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Mod {
				write(path.Join(p, "Mod", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Mod", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Pow {
				write(path.Join(p, "Pow", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Pow", strconv.Itoa(i), "Description"), fn.Description)
			}
			for i, fn := range op.Concat {
				write(path.Join(p, "Concat", strconv.Itoa(i), "Summary"), fn.Summary)
				write(path.Join(p, "Concat", strconv.Itoa(i), "Description"), fn.Description)
			}
			if op.Eq != nil {
				write(path.Join(p, "Eq", "Summary"), op.Eq.Summary)
				write(path.Join(p, "Eq", "Description"), op.Eq.Description)
			}
			if op.Lt != nil {
				write(path.Join(p, "Lt", "Summary"), op.Lt.Summary)
				write(path.Join(p, "Lt", "Description"), op.Lt.Description)
			}
			if op.Le != nil {
				write(path.Join(p, "Le", "Summary"), op.Le.Summary)
				write(path.Join(p, "Le", "Description"), op.Le.Description)
			}
			if op.Len != nil {
				write(path.Join(p, "Len", "Summary"), op.Len.Summary)
				write(path.Join(p, "Len", "Description"), op.Len.Description)
			}
			if op.Unm != nil {
				write(path.Join(p, "Unm", "Summary"), op.Unm.Summary)
				write(path.Join(p, "Unm", "Description"), op.Unm.Description)
			}
			if op.Call != nil {
				write(path.Join(p, "Call", "Summary"), op.Call.Summary)
				write(path.Join(p, "Call", "Description"), op.Call.Description)
			}
			if op.Index != nil {
				write(path.Join(p, "Index", "Summary"), op.Index.Summary)
				write(path.Join(p, "Index", "Description"), op.Index.Description)
			}
			if op.Newindex != nil {
				write(path.Join(p, "Newindex", "Summary"), op.Newindex.Summary)
				write(path.Join(p, "Newindex", "Description"), op.Newindex.Description)
			}
		}
	})
}
<|endoftext|>


<commit_before>package fingerprint

import (
	"testing"

	"github.com/hashicorp/nomad/nomad/structs"
)

func TestConsulFingerprint(t *testing.T) {
	fp := NewConsulFingerprint(testLogger())
	node := &structs.Node{
		Attributes: make(map[string]string),
	}

	assertFingerprintOK(t, fp, node)

	assertNodeAttributeContains(t, node, "consul.server")
	assertNodeAttributeContains(t, node, "consul.version")
	assertNodeAttributeContains(t, node, "consul.revision")
	assertNodeAttributeContains(t, node, "consul.name")
	assertNodeAttributeContains(t, node, "consul.datacenter")

	expectedLink := "consul2.vagrant"
	if node.Links["consul"] != expectedLink {
		t.Errorf("Expected consul link: %s", expectedLink)
	}
}

// Taken from tryconsul using consul release 0.5.2
const mockPayload = `
{
  "Config": {
    "Bootstrap": false,
    "BootstrapExpect": 3,
    "Server": true,
    "Datacenter": "vagrant",
    "DataDir": "/var/lib/consul",
    "DNSRecursor": "",
    "DNSRecursors": [],
    "DNSConfig": {
      "NodeTTL": 0,
      "ServiceTTL": null,
      "AllowStale": false,
      "EnableTruncate": false,
      "MaxStale": 5000000000,
      "OnlyPassing": false
    },
    "Domain": "consul.",
    "LogLevel": "INFO",
    "NodeName": "consul2",
    "ClientAddr": "0.0.0.0",
    "BindAddr": "0.0.0.0",
    "AdvertiseAddr": "172.16.59.133",
    "AdvertiseAddrWan": "172.16.59.133",
    "Ports": {
      "DNS": 8600,
      "HTTP": 8500,
      "HTTPS": -1,
      "RPC": 8400,
      "SerfLan": 8301,
      "SerfWan": 8302,
      "Server": 8300
    },
    "Addresses": {
      "DNS": "",
      "HTTP": "",
      "HTTPS": "",
      "RPC": ""
    },
    "LeaveOnTerm": false,
    "SkipLeaveOnInt": false,
    "StatsiteAddr": "",
    "StatsitePrefix": "consul",
    "StatsdAddr": "",
    "Protocol": 2,
    "EnableDebug": false,
    "VerifyIncoming": false,
    "VerifyOutgoing": false,
    "VerifyServerHostname": false,
    "CAFile": "",
    "CertFile": "",
    "KeyFile": "",
    "ServerName": "",
    "StartJoin": [],
    "StartJoinWan": [],
    "RetryJoin": [],
    "RetryMaxAttempts": 0,
    "RetryIntervalRaw": "",
    "RetryJoinWan": [],
    "RetryMaxAttemptsWan": 0,
    "RetryIntervalWanRaw": "",
    "UiDir": "/opt/consul-ui",
    "PidFile": "",
    "EnableSyslog": true,
    "SyslogFacility": "LOCAL0",
    "RejoinAfterLeave": false,
    "CheckUpdateInterval": 300000000000,
    "ACLDatacenter": "",
    "ACLTTL": 30000000000,
    "ACLTTLRaw": "",
    "ACLDefaultPolicy": "allow",
    "ACLDownPolicy": "extend-cache",
    "Watches": null,
    "DisableRemoteExec": false,
    "DisableUpdateCheck": false,
    "DisableAnonymousSignature": false,
    "HTTPAPIResponseHeaders": null,
    "AtlasInfrastructure": "",
    "AtlasJoin": false,
    "Revision": "9a9cc9341bb487651a0399e3fc5e1e8a42e62dd9+CHANGES",
    "Version": "0.5.2",
    "VersionPrerelease": "",
    "UnixSockets": {
      "Usr": "",
      "Grp": "",
      "Perms": ""
    },
    "SessionTTLMin": 0,
    "SessionTTLMinRaw": ""
  },
  "Member": {
    "Name": "consul2",
    "Addr": "172.16.59.133",
    "Port": 8301,
    "Tags": {
      "build": "0.5.2:9a9cc934",
      "dc": "vagrant",
      "expect": "3",
      "port": "8300",
      "role": "consul",
      "vsn": "2",
      "vsn_max": "2",
      "vsn_min": "1"
    },
    "Status": 1,
    "ProtocolMin": 1,
    "ProtocolMax": 2,
    "ProtocolCur": 2,
    "DelegateMin": 2,
    "DelegateMax": 4,
    "DelegateCur": 4
  }
}
`
<commit_msg>Add working test for consul fingerprinter<commit_after>package fingerprint

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/hashicorp/nomad/client/config"
	"github.com/hashicorp/nomad/nomad/structs"
)

func TestConsulFingerprint(t *testing.T) {
	fp := NewConsulFingerprint(testLogger())
	node := &structs.Node{
		Attributes: make(map[string]string),
	}

	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintln(w, mockConsulResponse)
	}))
	defer ts.Close()

	consulConfig := &config.Config{
		Options: map[string]string{
			// Split off "http://"
			"consul.address": ts.URL[7:],
		},
	}

	ok, err := fp.Fingerprint(consulConfig, node)
	if err != nil {
		t.Fatalf("Failed to fingerprint: %s", err)
	}
	if !ok {
		t.Fatalf("Failed to apply node attributes")
	}

	assertNodeAttributeContains(t, node, "consul.server")
	assertNodeAttributeContains(t, node, "consul.version")
	assertNodeAttributeContains(t, node, "consul.revision")
	assertNodeAttributeContains(t, node, "consul.name")
	assertNodeAttributeContains(t, node, "consul.datacenter")

	expectedLink := "vagrant.consul2"
	if node.Links["consul"] != expectedLink {
		t.Errorf("Expected consul link: %s\nFound links: %#v", expectedLink, node.Links)
	}
}

// Taken from tryconsul using consul release 0.5.2
const mockConsulResponse = `
{
  "Config": {
    "Bootstrap": false,
    "BootstrapExpect": 3,
    "Server": true,
    "Datacenter": "vagrant",
    "DataDir": "/var/lib/consul",
    "DNSRecursor": "",
    "DNSRecursors": [],
    "DNSConfig": {
      "NodeTTL": 0,
      "ServiceTTL": null,
      "AllowStale": false,
      "EnableTruncate": false,
      "MaxStale": 5000000000,
      "OnlyPassing": false
    },
    "Domain": "consul.",
    "LogLevel": "INFO",
    "NodeName": "consul2",
    "ClientAddr": "0.0.0.0",
    "BindAddr": "0.0.0.0",
    "AdvertiseAddr": "172.16.59.133",
    "AdvertiseAddrWan": "172.16.59.133",
    "Ports": {
      "DNS": 8600,
      "HTTP": 8500,
      "HTTPS": -1,
      "RPC": 8400,
      "SerfLan": 8301,
      "SerfWan": 8302,
      "Server": 8300
    },
    "Addresses": {
      "DNS": "",
      "HTTP": "",
      "HTTPS": "",
      "RPC": ""
    },
    "LeaveOnTerm": false,
    "SkipLeaveOnInt": false,
    "StatsiteAddr": "",
    "StatsitePrefix": "consul",
    "StatsdAddr": "",
    "Protocol": 2,
    "EnableDebug": false,
    "VerifyIncoming": false,
    "VerifyOutgoing": false,
    "VerifyServerHostname": false,
    "CAFile": "",
    "CertFile": "",
    "KeyFile": "",
    "ServerName": "",
    "StartJoin": [],
    "StartJoinWan": [],
    "RetryJoin": [],
    "RetryMaxAttempts": 0,
    "RetryIntervalRaw": "",
    "RetryJoinWan": [],
    "RetryMaxAttemptsWan": 0,
    "RetryIntervalWanRaw": "",
    "UiDir": "/opt/consul-ui",
    "PidFile": "",
    "EnableSyslog": true,
    "SyslogFacility": "LOCAL0",
    "RejoinAfterLeave": false,
    "CheckUpdateInterval": 300000000000,
    "ACLDatacenter": "",
    "ACLTTL": 30000000000,
    "ACLTTLRaw": "",
    "ACLDefaultPolicy": "allow",
    "ACLDownPolicy": "extend-cache",
    "Watches": null,
    "DisableRemoteExec": false,
    "DisableUpdateCheck": false,
    "DisableAnonymousSignature": false,
    "HTTPAPIResponseHeaders": null,
    "AtlasInfrastructure": "",
    "AtlasJoin": false,
    "Revision": "9a9cc9341bb487651a0399e3fc5e1e8a42e62dd9+CHANGES",
    "Version": "0.5.2",
    "VersionPrerelease": "",
    "UnixSockets": {
      "Usr": "",
      "Grp": "",
      "Perms": ""
    },
    "SessionTTLMin": 0,
    "SessionTTLMinRaw": ""
  },
  "Member": {
    "Name": "consul2",
    "Addr": "172.16.59.133",
    "Port": 8301,
    "Tags": {
      "build": "0.5.2:9a9cc934",
      "dc": "vagrant",
      "expect": "3",
      "port": "8300",
      "role": "consul",
      "vsn": "2",
      "vsn_max": "2",
      "vsn_min": "1"
    },
    "Status": 1,
    "ProtocolMin": 1,
    "ProtocolMax": 2,
    "ProtocolCur": 2,
    "DelegateMin": 2,
    "DelegateMax": 4,
    "DelegateCur": 4
  }
}
`
<|endoftext|>


<commit_before>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"net/http"
	"time"
)

// Magic Transit IPsec Tunnel Error messages.
const (
	errMagicTransitIPsecTunnelNotModified = "When trying to modify IPsec tunnel, API returned modified: false"
	errMagicTransitIPsecTunnelNotDeleted  = "When trying to delete IPsec tunnel, API returned deleted: false"
)

// MagicTransitIPsecTunnel contains information about an IPsec tunnel.
type MagicTransitIPsecTunnel struct {
	ID                 string     `json:"id,omitempty"`
	CreatedOn          *time.Time `json:"created_on,omitempty"`
	ModifiedOn         *time.Time `json:"modified_on,omitempty"`
	Name               string     `json:"name"`
	CustomerEndpoint   string     `json:"customer_endpoint"`
	CloudflareEndpoint string     `json:"cloudflare_endpoint"`
	InterfaceAddress   string     `json:"interface_address"`
	Description        string     `json:"description,omitempty"`
}

// ListMagicTransitIPsecTunnelsResponse contains a response including IPsec tunnels.
type ListMagicTransitIPsecTunnelsResponse struct {
	Response
	Result struct {
		IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"`
	} `json:"result"`
}

// GetMagicTransitIPsecTunnelResponse contains a response including zero or one IPsec tunnels.
type GetMagicTransitIPsecTunnelResponse struct {
	Response
	Result struct {
		IPsecTunnel MagicTransitIPsecTunnel `json:"ipsec_tunnel"`
	} `json:"result"`
}

// CreateMagicTransitIPsecTunnelsRequest is an array of IPsec tunnels to create.
type CreateMagicTransitIPsecTunnelsRequest struct {
	IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"`
}

// UpdateMagicTransitIPsecTunnelResponse contains a response after updating an IPsec Tunnel.
type UpdateMagicTransitIPsecTunnelResponse struct {
	Response
	Result struct {
		Modified            bool                    `json:"modified"`
		ModifiedIPsecTunnel MagicTransitIPsecTunnel `json:"modified_ipsec_tunnel"`
	} `json:"result"`
}

// DeleteMagicTransitIPsecTunnelResponse contains a response after deleting an IPsec Tunnel.
type DeleteMagicTransitIPsecTunnelResponse struct {
	Response
	Result struct {
		Deleted            bool                    `json:"deleted"`
		DeletedIPsecTunnel MagicTransitIPsecTunnel `json:"deleted_ipsec_tunnel"`
	} `json:"result"`
}

// ListMagicTransitIPsecTunnels lists all IPsec tunnels for a given account
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-list-ipsec-tunnels
func (api *API) ListMagicTransitIPsecTunnels(ctx context.Context) ([]MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", api.AccountID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	result := ListMagicTransitIPsecTunnelsResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return []MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result.IPsecTunnels, nil
}

// GetMagicTransitIPsecTunnel returns zero or one IPsec tunnel
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-ipsec-tunnel-details
func (api *API) GetMagicTransitIPsecTunnel(ctx context.Context, id string) (MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", api.AccountID, id)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	result := GetMagicTransitIPsecTunnelResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result.IPsecTunnel, nil
}

// CreateMagicTransitIPsecTunnels creates one or more IPsec tunnels
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-create-ipsec-tunnels
func (api *API) CreateMagicTransitIPsecTunnels(ctx context.Context, tunnels []MagicTransitIPsecTunnel) ([]MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", api.AccountID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, CreateMagicTransitIPsecTunnelsRequest{
		IPsecTunnels: tunnels,
	})

	if err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	result := ListMagicTransitIPsecTunnelsResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return []MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result.IPsecTunnels, nil
}

// UpdateMagicTransitIPsecTunnel updates an IPsec tunnel
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-update-ipsec-tunnel
func (api *API) UpdateMagicTransitIPsecTunnel(ctx context.Context, id string, tunnel MagicTransitIPsecTunnel) (MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", api.AccountID, id)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, tunnel)

	if err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	result := UpdateMagicTransitIPsecTunnelResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	if !result.Result.Modified {
		return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotModified)
	}

	return result.Result.ModifiedIPsecTunnel, nil
}

// DeleteMagicTransitIPsecTunnel deletes an IPsec Tunnel
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-delete-ipsec-tunnel
func (api *API) DeleteMagicTransitIPsecTunnel(ctx context.Context, id string) (MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", api.AccountID, id)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)

	if err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	result := DeleteMagicTransitIPsecTunnelResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	if !result.Result.Deleted {
		return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotDeleted)
	}

	return result.Result.DeletedIPsecTunnel, nil
}
<commit_msg>changing method signatures to expect accountID<commit_after>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"net/http"
	"time"
)

// Magic Transit IPsec Tunnel Error messages.
const (
	errMagicTransitIPsecTunnelNotModified = "When trying to modify IPsec tunnel, API returned modified: false"
	errMagicTransitIPsecTunnelNotDeleted  = "When trying to delete IPsec tunnel, API returned deleted: false"
)

// MagicTransitIPsecTunnel contains information about an IPsec tunnel.
type MagicTransitIPsecTunnel struct {
	ID                 string     `json:"id,omitempty"`
	CreatedOn          *time.Time `json:"created_on,omitempty"`
	ModifiedOn         *time.Time `json:"modified_on,omitempty"`
	Name               string     `json:"name"`
	CustomerEndpoint   string     `json:"customer_endpoint"`
	CloudflareEndpoint string     `json:"cloudflare_endpoint"`
	InterfaceAddress   string     `json:"interface_address"`
	Description        string     `json:"description,omitempty"`
}

// ListMagicTransitIPsecTunnelsResponse contains a response including IPsec tunnels.
type ListMagicTransitIPsecTunnelsResponse struct {
	Response
	Result struct {
		IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"`
	} `json:"result"`
}

// GetMagicTransitIPsecTunnelResponse contains a response including zero or one IPsec tunnels.
type GetMagicTransitIPsecTunnelResponse struct {
	Response
	Result struct {
		IPsecTunnel MagicTransitIPsecTunnel `json:"ipsec_tunnel"`
	} `json:"result"`
}

// CreateMagicTransitIPsecTunnelsRequest is an array of IPsec tunnels to create.
type CreateMagicTransitIPsecTunnelsRequest struct {
	IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"`
}

// UpdateMagicTransitIPsecTunnelResponse contains a response after updating an IPsec Tunnel.
type UpdateMagicTransitIPsecTunnelResponse struct {
	Response
	Result struct {
		Modified            bool                    `json:"modified"`
		ModifiedIPsecTunnel MagicTransitIPsecTunnel `json:"modified_ipsec_tunnel"`
	} `json:"result"`
}

// DeleteMagicTransitIPsecTunnelResponse contains a response after deleting an IPsec Tunnel.
type DeleteMagicTransitIPsecTunnelResponse struct {
	Response
	Result struct {
		Deleted            bool                    `json:"deleted"`
		DeletedIPsecTunnel MagicTransitIPsecTunnel `json:"deleted_ipsec_tunnel"`
	} `json:"result"`
}

// ListMagicTransitIPsecTunnels lists all IPsec tunnels for a given account
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-list-ipsec-tunnels
func (api *API) ListMagicTransitIPsecTunnels(ctx context.Context, account_id string) ([]MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", account_id)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	result := ListMagicTransitIPsecTunnelsResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return []MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result.IPsecTunnels, nil
}

// GetMagicTransitIPsecTunnel returns zero or one IPsec tunnel
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-ipsec-tunnel-details
func (api *API) GetMagicTransitIPsecTunnel(ctx context.Context, account_id string, id string) (MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", account_id, id)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	result := GetMagicTransitIPsecTunnelResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result.IPsecTunnel, nil
}

// CreateMagicTransitIPsecTunnels creates one or more IPsec tunnels
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-create-ipsec-tunnels
func (api *API) CreateMagicTransitIPsecTunnels(ctx context.Context, account_id string, tunnels []MagicTransitIPsecTunnel) ([]MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", account_id)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, CreateMagicTransitIPsecTunnelsRequest{
		IPsecTunnels: tunnels,
	})

	if err != nil {
		return []MagicTransitIPsecTunnel{}, err
	}

	result := ListMagicTransitIPsecTunnelsResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return []MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result.IPsecTunnels, nil
}

// UpdateMagicTransitIPsecTunnel updates an IPsec tunnel
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-update-ipsec-tunnel
func (api *API) UpdateMagicTransitIPsecTunnel(ctx context.Context, account_id string, id string, tunnel MagicTransitIPsecTunnel) (MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", account_id, id)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, tunnel)

	if err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	result := UpdateMagicTransitIPsecTunnelResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	if !result.Result.Modified {
		return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotModified)
	}

	return result.Result.ModifiedIPsecTunnel, nil
}

// DeleteMagicTransitIPsecTunnel deletes an IPsec Tunnel
//
// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-delete-ipsec-tunnel
func (api *API) DeleteMagicTransitIPsecTunnel(ctx context.Context, account_id string, id string) (MagicTransitIPsecTunnel, error) {
	if err := api.checkAccountID(); err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", account_id, id)
	res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)

	if err != nil {
		return MagicTransitIPsecTunnel{}, err
	}

	result := DeleteMagicTransitIPsecTunnelResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError)
	}

	if !result.Result.Deleted {
		return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotDeleted)
	}

	return result.Result.DeletedIPsecTunnel, nil
}
<|endoftext|>


<commit_before>package engines

import (
	"bytes"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/imdario/mergo"
	imagefile "github.com/thoas/picfit/image"
	"golang.org/x/image/bmp"
	"golang.org/x/image/tiff"
	"image"
	"image/draw"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math"
	"strconv"
	"time"
)

type GoImageEngine struct {
	DefaultFormat  string
	Format         string
	DefaultQuality int
}

type ImageTransformation func(img image.Image) *image.NRGBA

var FlipTransformations = map[string]ImageTransformation{
	"h": imaging.FlipH,
	"v": imaging.FlipV,
}

var RotateTransformations = map[int]ImageTransformation{
	90:  imaging.Rotate90,
	270: imaging.Rotate270,
	180: imaging.Rotate180,
}

type Result struct {
	Paletted *image.Paletted
	Image    *image.NRGBA
	Position int
}

type Transformation func(img image.Image, width int, height int, filter imaging.ResampleFilter) *image.NRGBA

func scalingFactor(srcWidth int, srcHeight int, destWidth int, destHeight int) float64 {
	return math.Max(float64(destWidth)/float64(srcWidth), float64(destHeight)/float64(srcHeight))
}

func ImageSize(e image.Image) (int, int) {
	return e.Bounds().Max.X, e.Bounds().Max.Y
}

func (e *GoImageEngine) Scale(img image.Image, dstWidth int, dstHeight int, upscale bool, trans Transformation) *image.NRGBA {
	width, height := ImageSize(img)

	factor := scalingFactor(width, height, dstWidth, dstHeight)

	if factor < 1 || upscale {
		return trans(img, dstWidth, dstHeight, imaging.Lanczos)
	}

	return imaging.Clone(img)
}

func (e *GoImageEngine) TransformGIF(img *imagefile.ImageFile, width int, height int, options *Options, trans Transformation) ([]byte, error) {
	g, err := gif.DecodeAll(bytes.NewReader(img.Source))

	if err != nil {
		return nil, err
	}

	length := len(g.Image)
	done := make(chan *Result)
	images := make([]*image.Paletted, length)
	processed := 0

	for i := range g.Image {
		go func(paletted *image.Paletted, width int, height int, position int, trans Transformation, options *Options) {
			done <- &Result{
				Image:    e.Scale(paletted, width, height, options.Upscale, trans),
				Position: position,
				Paletted: image.NewPaletted(image.Rect(0, 0, width, height), paletted.Palette),
			}
		}(g.Image[i], width, height, i, trans, options)
	}

	for {
		select {
		case result := <-done:
			draw.Draw(result.Paletted, image.Rect(0, 0, width, height), result.Image, image.Pt(0, 0), draw.Src)

			images[result.Position] = result.Paletted

			processed++
		case <-time.After(time.Second * 5):
			break
		}

		if processed == length {
			break
		}
	}

	close(done)

	g.Image = images

	buf := &bytes.Buffer{}

	err = gif.EncodeAll(buf, g)

	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func (e *GoImageEngine) Resize(img *imagefile.ImageFile, width int, height int, options *Options) ([]byte, error) {
	if options.Format == imaging.GIF {
		content, err := e.TransformGIF(img, width, height, options, imaging.Resize)

		if err != nil {
			return nil, err
		}

		return content, nil
	}

	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	return e.resize(image, width, height, options)
}

func (e *GoImageEngine) resize(img image.Image, width int, height int, options *Options) ([]byte, error) {
	return e.ToBytes(e.Scale(img, width, height, options.Upscale, imaging.Resize), options.Format, options.Quality)
}

func (e *GoImageEngine) Source(img *imagefile.ImageFile) (image.Image, error) {
	return imaging.Decode(bytes.NewReader(img.Source))
}

func (e *GoImageEngine) Rotate(img *imagefile.ImageFile, deg int, options *Options) ([]byte, error) {
	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	transform, ok := RotateTransformations[deg]

	if !ok {
		return nil, fmt.Errorf("Invalid rotate transformation degree=%d is not supported", deg)
	}

	return e.ToBytes(transform(image), options.Format, options.Quality)
}

func (e *GoImageEngine) Flip(img *imagefile.ImageFile, pos string, options *Options) ([]byte, error) {
	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	transform, ok := FlipTransformations[pos]

	if !ok {
		return nil, fmt.Errorf("Invalid flip transformation, %s is not supported", pos)
	}

	return e.ToBytes(transform(image), options.Format, options.Quality)
}

func (e *GoImageEngine) Thumbnail(img *imagefile.ImageFile, width int, height int, options *Options) ([]byte, error) {
	if options.Format == imaging.GIF {
		content, err := e.TransformGIF(img, width, height, options, imaging.Thumbnail)

		if err != nil {
			return nil, err
		}

		return content, nil
	}

	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	return e.thumbnail(image, width, height, options)
}

func (e *GoImageEngine) thumbnail(img image.Image, width int, height int, options *Options) ([]byte, error) {
	return e.ToBytes(e.Scale(img, width, height, options.Upscale, imaging.Thumbnail), options.Format, options.Quality)
}

func (e *GoImageEngine) Transform(img *imagefile.ImageFile, operation *Operation, qs map[string]string) (*imagefile.ImageFile, error) {
	params := map[string]string{
		"upscale": "1",
		"h":       "0",
		"w":       "0",
		"deg":     "90",
	}

	err := mergo.Merge(&qs, params)

	if err != nil {
		return nil, err
	}

	var quality int
	var format string

	q, ok := qs["q"]

	if ok {
		quality, err := strconv.Atoi(q)

		if err != nil {
			return nil, err
		}

		if quality > 100 {
			return nil, fmt.Errorf("Quality should be <= 100")
		}
	} else {
		quality = e.DefaultQuality
	}

	format, ok = qs["fmt"]
	filepath := img.Filepath

	if ok {
		if _, ok := ContentTypes[format]; !ok {
			return nil, fmt.Errorf("Unknown format %s", format)
		}

	}

	if format == "" && e.Format != "" {
		format = e.Format
	}

	if format == "" {
		format = img.Format()
	}

	if format == "" {
		format = e.DefaultFormat
	}

	if format != img.Format() {
		index := len(filepath) - len(img.Format())

		filepath = filepath[:index] + format
	}

	file := &imagefile.ImageFile{
		Source:   img.Source,
		Key:      img.Key,
		Headers:  img.Headers,
		Filepath: filepath,
	}

	options := &Options{
		Quality: quality,
		Format:  Formats[format],
	}

	switch operation {
	case Flip:
		pos, ok := qs["pos"]

		if !ok {
			return nil, fmt.Errorf("Parameter \"pos\" not found in query string")
		}

		content, err := e.Flip(img, pos, options)

		if err != nil {
			return nil, err
		}

		file.Processed = content

		return file, err
	case Rotate:
		deg, err := strconv.Atoi(qs["deg"])

		if err != nil {
			return nil, err
		}

		content, err := e.Rotate(img, deg, options)

		if err != nil {
			return nil, err
		}

		file.Processed = content

		return file, err
	case Thumbnail, Resize:
		var upscale bool
		var w int
		var h int

		if upscale, err = strconv.ParseBool(qs["upscale"]); err != nil {
			return nil, err
		}

		if w, err = strconv.Atoi(qs["w"]); err != nil {
			return nil, err
		}

		if h, err = strconv.Atoi(qs["h"]); err != nil {
			return nil, err
		}

		options.Upscale = upscale

		switch operation {
		case Resize:
			content, err := e.Resize(img, w, h, options)

			if err != nil {
				return nil, err
			}

			file.Processed = content

			return file, err
		case Thumbnail:
			content, err := e.Thumbnail(img, w, h, options)

			if err != nil {
				return nil, err
			}

			file.Processed = content

			return file, err
		}
	}

	return nil, fmt.Errorf("Operation not found for %s", operation)
}

func (e *GoImageEngine) ToBytes(img image.Image, format imaging.Format, quality int) ([]byte, error) {
	buf := &bytes.Buffer{}

	var err error

	err = encode(buf, img, format, quality)

	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func encode(w io.Writer, img image.Image, format imaging.Format, quality int) error {
	var err error
	switch format {
	case imaging.JPEG:
		var rgba *image.RGBA
		if nrgba, ok := img.(*image.NRGBA); ok {
			if nrgba.Opaque() {
				rgba = &image.RGBA{
					Pix:    nrgba.Pix,
					Stride: nrgba.Stride,
					Rect:   nrgba.Rect,
				}
			}
		}
		if rgba != nil {
			err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: quality})
		} else {
			err = jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
		}

	case imaging.PNG:
		err = png.Encode(w, img)
	case imaging.GIF:
		err = gif.Encode(w, img, &gif.Options{NumColors: 256})
	case imaging.TIFF:
		err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
	case imaging.BMP:
		err = bmp.Encode(w, img)
	default:
		err = imaging.ErrUnsupportedFormat
	}
	return err
}
<commit_msg>Fix gif operations with ratio<commit_after>package engines

import (
	"bytes"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/imdario/mergo"
	imagefile "github.com/thoas/picfit/image"
	"golang.org/x/image/bmp"
	"golang.org/x/image/tiff"
	"image"
	"image/draw"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math"
	"strconv"
	"time"
)

type GoImageEngine struct {
	DefaultFormat  string
	Format         string
	DefaultQuality int
}

type ImageTransformation func(img image.Image) *image.NRGBA

var FlipTransformations = map[string]ImageTransformation{
	"h": imaging.FlipH,
	"v": imaging.FlipV,
}

var RotateTransformations = map[int]ImageTransformation{
	90:  imaging.Rotate90,
	270: imaging.Rotate270,
	180: imaging.Rotate180,
}

type Result struct {
	Image    image.Image
	Position int
	Paletted *image.Paletted
}

type Transformation func(img image.Image, width int, height int, filter imaging.ResampleFilter) *image.NRGBA

func scalingFactor(srcWidth int, srcHeight int, destWidth int, destHeight int) float64 {
	return math.Max(float64(destWidth)/float64(srcWidth), float64(destHeight)/float64(srcHeight))
}

func scalingFactorImage(img image.Image, dstWidth int, dstHeight int) float64 {
	width, height := ImageSize(img)

	return scalingFactor(width, height, dstWidth, dstHeight)
}

func ImageSize(e image.Image) (int, int) {
	return e.Bounds().Max.X, e.Bounds().Max.Y
}

func (e *GoImageEngine) Scale(img image.Image, dstWidth int, dstHeight int, upscale bool, trans Transformation) image.Image {
	factor := scalingFactorImage(img, dstWidth, dstHeight)

	if factor < 1 || upscale {
		return trans(img, dstWidth, dstHeight, imaging.Lanczos)
	}

	return img
}

func (e *GoImageEngine) TransformGIF(img *imagefile.ImageFile, width int, height int, options *Options, trans Transformation) ([]byte, error) {
	first, err := gif.Decode(bytes.NewReader(img.Source))

	if err != nil {
		return nil, err
	}

	factor := scalingFactorImage(first, width, height)

	if factor > 1 && !options.Upscale {
		return img.Source, nil
	}

	g, err := gif.DecodeAll(bytes.NewReader(img.Source))

	if err != nil {
		return nil, err
	}

	length := len(g.Image)
	done := make(chan *Result)
	images := make([]*image.Paletted, length)
	processed := 0

	for i := range g.Image {
		go func(paletted *image.Paletted, width int, height int, position int, trans Transformation, options *Options) {
			done <- &Result{
				Image:    e.Scale(paletted, width, height, options.Upscale, trans),
				Position: position,
				Paletted: paletted,
			}
		}(g.Image[i], width, height, i, trans, options)
	}

	for {
		select {
		case result := <-done:
			bounds := result.Image.Bounds()

			palette := image.NewPaletted(image.Rect(0, 0, bounds.Max.X, bounds.Max.Y), result.Paletted.Palette)

			draw.Draw(palette, bounds, result.Image, bounds.Min, draw.Src)

			images[result.Position] = palette

			processed++
		case <-time.After(time.Second * 5):
			break
		}

		if processed == length {
			break
		}
	}

	close(done)

	g.Image = images

	buf := &bytes.Buffer{}

	err = gif.EncodeAll(buf, g)

	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func (e *GoImageEngine) Resize(img *imagefile.ImageFile, width int, height int, options *Options) ([]byte, error) {
	if options.Format == imaging.GIF {
		content, err := e.TransformGIF(img, width, height, options, imaging.Resize)

		if err != nil {
			return nil, err
		}

		return content, nil
	}

	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	return e.resize(image, width, height, options)
}

func (e *GoImageEngine) resize(img image.Image, width int, height int, options *Options) ([]byte, error) {
	return e.ToBytes(e.Scale(img, width, height, options.Upscale, imaging.Resize), options.Format, options.Quality)
}

func (e *GoImageEngine) Source(img *imagefile.ImageFile) (image.Image, error) {
	return imaging.Decode(bytes.NewReader(img.Source))
}

func (e *GoImageEngine) Rotate(img *imagefile.ImageFile, deg int, options *Options) ([]byte, error) {
	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	transform, ok := RotateTransformations[deg]

	if !ok {
		return nil, fmt.Errorf("Invalid rotate transformation degree=%d is not supported", deg)
	}

	return e.ToBytes(transform(image), options.Format, options.Quality)
}

func (e *GoImageEngine) Flip(img *imagefile.ImageFile, pos string, options *Options) ([]byte, error) {
	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	transform, ok := FlipTransformations[pos]

	if !ok {
		return nil, fmt.Errorf("Invalid flip transformation, %s is not supported", pos)
	}

	return e.ToBytes(transform(image), options.Format, options.Quality)
}

func (e *GoImageEngine) Thumbnail(img *imagefile.ImageFile, width int, height int, options *Options) ([]byte, error) {
	if options.Format == imaging.GIF {
		content, err := e.TransformGIF(img, width, height, options, imaging.Thumbnail)

		if err != nil {
			return nil, err
		}

		return content, nil
	}

	image, err := e.Source(img)

	if err != nil {
		return nil, err
	}

	return e.thumbnail(image, width, height, options)
}

func (e *GoImageEngine) thumbnail(img image.Image, width int, height int, options *Options) ([]byte, error) {
	return e.ToBytes(e.Scale(img, width, height, options.Upscale, imaging.Thumbnail), options.Format, options.Quality)
}

func (e *GoImageEngine) Transform(img *imagefile.ImageFile, operation *Operation, qs map[string]string) (*imagefile.ImageFile, error) {
	params := map[string]string{
		"upscale": "1",
		"h":       "0",
		"w":       "0",
		"deg":     "90",
	}

	err := mergo.Merge(&qs, params)

	if err != nil {
		return nil, err
	}

	var quality int
	var format string

	q, ok := qs["q"]

	if ok {
		quality, err := strconv.Atoi(q)

		if err != nil {
			return nil, err
		}

		if quality > 100 {
			return nil, fmt.Errorf("Quality should be <= 100")
		}
	} else {
		quality = e.DefaultQuality
	}

	format, ok = qs["fmt"]
	filepath := img.Filepath

	if ok {
		if _, ok := ContentTypes[format]; !ok {
			return nil, fmt.Errorf("Unknown format %s", format)
		}

	}

	if format == "" && e.Format != "" {
		format = e.Format
	}

	if format == "" {
		format = img.Format()
	}

	if format == "" {
		format = e.DefaultFormat
	}

	if format != img.Format() {
		index := len(filepath) - len(img.Format())

		filepath = filepath[:index] + format
	}

	file := &imagefile.ImageFile{
		Source:   img.Source,
		Key:      img.Key,
		Headers:  img.Headers,
		Filepath: filepath,
	}

	options := &Options{
		Quality: quality,
		Format:  Formats[format],
	}

	switch operation {
	case Flip:
		pos, ok := qs["pos"]

		if !ok {
			return nil, fmt.Errorf("Parameter \"pos\" not found in query string")
		}

		content, err := e.Flip(img, pos, options)

		if err != nil {
			return nil, err
		}

		file.Processed = content

		return file, err
	case Rotate:
		deg, err := strconv.Atoi(qs["deg"])

		if err != nil {
			return nil, err
		}

		content, err := e.Rotate(img, deg, options)

		if err != nil {
			return nil, err
		}

		file.Processed = content

		return file, err
	case Thumbnail, Resize:
		var upscale bool
		var w int
		var h int

		if upscale, err = strconv.ParseBool(qs["upscale"]); err != nil {
			return nil, err
		}

		if w, err = strconv.Atoi(qs["w"]); err != nil {
			return nil, err
		}

		if h, err = strconv.Atoi(qs["h"]); err != nil {
			return nil, err
		}

		options.Upscale = upscale

		switch operation {
		case Resize:
			content, err := e.Resize(img, w, h, options)

			if err != nil {
				return nil, err
			}

			file.Processed = content

			return file, err
		case Thumbnail:
			content, err := e.Thumbnail(img, w, h, options)

			if err != nil {
				return nil, err
			}

			file.Processed = content

			return file, err
		}
	}

	return nil, fmt.Errorf("Operation not found for %s", operation)
}

func (e *GoImageEngine) ToBytes(img image.Image, format imaging.Format, quality int) ([]byte, error) {
	buf := &bytes.Buffer{}

	var err error

	err = encode(buf, img, format, quality)

	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func encode(w io.Writer, img image.Image, format imaging.Format, quality int) error {
	var err error
	switch format {
	case imaging.JPEG:
		var rgba *image.RGBA
		if nrgba, ok := img.(*image.NRGBA); ok {
			if nrgba.Opaque() {
				rgba = &image.RGBA{
					Pix:    nrgba.Pix,
					Stride: nrgba.Stride,
					Rect:   nrgba.Rect,
				}
			}
		}
		if rgba != nil {
			err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: quality})
		} else {
			err = jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
		}

	case imaging.PNG:
		err = png.Encode(w, img)
	case imaging.GIF:
		err = gif.Encode(w, img, &gif.Options{NumColors: 256})
	case imaging.TIFF:
		err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
	case imaging.BMP:
		err = bmp.Encode(w, img)
	default:
		err = imaging.ErrUnsupportedFormat
	}
	return err
}
<|endoftext|>


<commit_before>package management

import (
	"encoding/base64"
	"encoding/pem"
	"encoding/xml"
	"fmt"
	"io/ioutil"

	"github.com/Azure/go-pkcs12"
)

// ClientFromPublishSettingsFile reads a publish settings file downloaded from https://manage.windowsazure.com/publishsettings.
// If subscriptionId is left empty, the first subscription in the file is used.
func ClientFromPublishSettingsFile(filePath, subscriptionId string) (client Client, err error) {
	return ClientFromPublishSettingsFileWithConfig(filePath, subscriptionId,
		ClientConfig{ManagementURL: defaultAzureManagementURL})
}

// ClientFromPublishSettingsFileWithConfig reads a publish settings file downloaded from https://manage.windowsazure.com/publishsettings.
// If subscriptionId is left empty, the first subscription in the file is used.
func ClientFromPublishSettingsFileWithConfig(filePath, subscriptionId string, config ClientConfig) (client Client, err error) {
	if filePath == "" {
		return client, fmt.Errorf(errParamNotSpecified, "filePath")
	}

	publishSettingsContent, err := ioutil.ReadFile(filePath)
	if err != nil {
		return client, err
	}

	publishData := publishData{}
	if err = xml.Unmarshal(publishSettingsContent, &publishData); err != nil {
		return client, err
	}

	for _, profile := range publishData.PublishProfiles {
		for _, sub := range profile.Subscriptions {
			if sub.Id == subscriptionId || subscriptionId == "" {
				base64Cert := sub.ManagementCertificate
				if base64Cert == "" {
					base64Cert = profile.ManagementCertificate
				}

				pfxData, err := base64.StdEncoding.DecodeString(base64Cert)
				if err != nil {
					return client, err
				}

				pems, err := pkcs12.ConvertToPEM(pfxData, "")

				cert := []byte{}
				for _, b := range pems {
					cert = append(cert, pem.EncodeToMemory(b)...)
				}

				managementURL := sub.ServiceManagementUrl
				if config.ManagementURL != "" {
					managementURL = config.ManagementURL
				}

				return makeClient(subscriptionId, cert, managementURL)
			}
		}
	}

	return client, fmt.Errorf("could not find subscription '%s' in '%s'", subscriptionId, filePath)
}

type publishSettings struct {
	SubscriptionID   string
	SubscriptionCert []byte
	SubscriptionKey  []byte
}

type publishData struct {
	XMLName         xml.Name         `xml:"PublishData"`
	PublishProfiles []publishProfile `xml:"PublishProfile"`
}

type publishProfile struct {
	XMLName               xml.Name       `xml:"PublishProfile"`
	SchemaVersion         string         `xml:",attr"`
	PublishMethod         string         `xml:",attr"`
	Url                   string         `xml:",attr"`
	ManagementCertificate string         `xml:",attr"`
	Subscriptions         []subscription `xml:"Subscription"`
}

type subscription struct {
	XMLName               xml.Name `xml:"Subscription"`
	ServiceManagementUrl  string   `xml:",attr"`
	Id                    string   `xml:",attr"`
	Name                  string   `xml:",attr"`
	ManagementCertificate string   `xml:",attr"`
}
<commit_msg>If not supplying a subscription ID, it should use the first one found<commit_after>package management

import (
	"encoding/base64"
	"encoding/pem"
	"encoding/xml"
	"fmt"
	"io/ioutil"

	"github.com/Azure/go-pkcs12"
)

// ClientFromPublishSettingsFile reads a publish settings file downloaded from https://manage.windowsazure.com/publishsettings.
// If subscriptionId is left empty, the first subscription in the file is used.
func ClientFromPublishSettingsFile(filePath, subscriptionId string) (client Client, err error) {
	return ClientFromPublishSettingsFileWithConfig(filePath, subscriptionId,
		ClientConfig{ManagementURL: defaultAzureManagementURL})
}

// ClientFromPublishSettingsFileWithConfig reads a publish settings file downloaded from https://manage.windowsazure.com/publishsettings.
// If subscriptionId is left empty, the first subscription in the file is used.
func ClientFromPublishSettingsFileWithConfig(filePath, subscriptionId string, config ClientConfig) (client Client, err error) {
	if filePath == "" {
		return client, fmt.Errorf(errParamNotSpecified, "filePath")
	}

	publishSettingsContent, err := ioutil.ReadFile(filePath)
	if err != nil {
		return client, err
	}

	publishData := publishData{}
	if err = xml.Unmarshal(publishSettingsContent, &publishData); err != nil {
		return client, err
	}

	for _, profile := range publishData.PublishProfiles {
		for _, sub := range profile.Subscriptions {
			if sub.Id == subscriptionId || subscriptionId == "" {
				base64Cert := sub.ManagementCertificate
				if base64Cert == "" {
					base64Cert = profile.ManagementCertificate
				}

				pfxData, err := base64.StdEncoding.DecodeString(base64Cert)
				if err != nil {
					return client, err
				}

				pems, err := pkcs12.ConvertToPEM(pfxData, "")

				cert := []byte{}
				for _, b := range pems {
					cert = append(cert, pem.EncodeToMemory(b)...)
				}

				managementURL := sub.ServiceManagementUrl
				if config.ManagementURL != "" {
					managementURL = config.ManagementURL
				}

				return makeClient(sub.Id, cert, managementURL)
			}
		}
	}

	return client, fmt.Errorf("could not find subscription '%s' in '%s'", subscriptionId, filePath)
}

type publishSettings struct {
	SubscriptionID   string
	SubscriptionCert []byte
	SubscriptionKey  []byte
}

type publishData struct {
	XMLName         xml.Name         `xml:"PublishData"`
	PublishProfiles []publishProfile `xml:"PublishProfile"`
}

type publishProfile struct {
	XMLName               xml.Name       `xml:"PublishProfile"`
	SchemaVersion         string         `xml:",attr"`
	PublishMethod         string         `xml:",attr"`
	Url                   string         `xml:",attr"`
	ManagementCertificate string         `xml:",attr"`
	Subscriptions         []subscription `xml:"Subscription"`
}

type subscription struct {
	XMLName               xml.Name `xml:"Subscription"`
	ServiceManagementUrl  string   `xml:",attr"`
	Id                    string   `xml:",attr"`
	Name                  string   `xml:",attr"`
	ManagementCertificate string   `xml:",attr"`
}
<|endoftext|>


<commit_before>package common

import (
	"github.com/op/go-logging"
	"os"
)

// SetupLogging - verbosity 0=error, 1=info, 2=debug
func SetupLogging(verbosity int) {
	errBackend := logging.NewLogBackend(os.Stderr, "", 0)
	errFormat := logging.MustStringFormatter(
		`%{color}%{shortfunc} ▶ %{level:.5s} %{color:reset} %{message}`,
	)
	errFormatter := logging.NewBackendFormatter(errBackend, errFormat)
	errLeveled := logging.AddModuleLevel(errFormatter)
	errLeveled.SetLevel(logging.ERROR, "")

	if verbosity >= 1 {
		infoBackend := logging.NewLogBackend(os.Stdout, "", 0)
		infoFormat := logging.MustStringFormatter(
			`%{color}%{message}%{color:reset}`,
		)
		infoFormatter := logging.NewBackendFormatter(infoBackend, infoFormat)
		infoLeveled := logging.AddModuleLevel(notToExceedLevel(logging.WARNING, infoFormatter))
		infoLeveled.SetLevel(logging.INFO, "")

		if verbosity >= 2 {
			debugBackend := logging.NewLogBackend(os.Stdout, "", 0)
			debugFormat := logging.MustStringFormatter(
				`%{color}%{time:15:04:05.000} %{module} ▶ %{id:03x}%{color:reset} %{message}`,
			)
			debugFormatter := logging.NewBackendFormatter(debugBackend, debugFormat)
			debugLeveled := logging.AddModuleLevel(notToExceedLevel(logging.DEBUG, debugFormatter))
			debugLeveled.SetLevel(logging.DEBUG, "")

			logging.SetBackend(debugLeveled, infoLeveled, errLeveled)
		} else {
			logging.SetBackend(infoLeveled, errLeveled)
		}
	} else {
		logging.SetBackend(errLeveled)
	}

}

func notToExceedLevel(notToExceed logging.Level, delegate logging.Backend) logging.Backend {
	return &notToExceedLevelBackend{
		delegate:         delegate,
		notToExceedLevel: notToExceed,
	}
}

type notToExceedLevelBackend struct {
	delegate         logging.Backend
	notToExceedLevel logging.Level
}

func (b *notToExceedLevelBackend) Log(l logging.Level, i int, r *logging.Record) error {
	if l < b.notToExceedLevel {
		return nil
	}
	return b.delegate.Log(l, i, r)
}
<commit_msg>send warning to STDERR<commit_after>package common

import (
	"github.com/op/go-logging"
	"os"
)

// SetupLogging - verbosity 0=error, 1=info, 2=debug
func SetupLogging(verbosity int) {
	errBackend := logging.NewLogBackend(os.Stderr, "", 0)
	errFormat := logging.MustStringFormatter(
		`%{color}%{shortfunc} ▶ %{level:.5s} %{color:reset} %{message}`,
	)
	errFormatter := logging.NewBackendFormatter(errBackend, errFormat)
	errLeveled := logging.AddModuleLevel(errFormatter)
	errLeveled.SetLevel(logging.ERROR, "")

	warnFormat := logging.MustStringFormatter(
		`%{color}%{message}%{color:reset}`,
	)
	warnFormatter := logging.NewBackendFormatter(errBackend, warnFormat)
	warnLeveled := logging.AddModuleLevel(notToExceedLevel(logging.WARNING, warnFormatter))
	warnLeveled.SetLevel(logging.WARNING, "")

	if verbosity >= 1 {
		infoBackend := logging.NewLogBackend(os.Stdout, "", 0)
		infoFormat := logging.MustStringFormatter(
			`%{color}%{message}%{color:reset}`,
		)
		infoFormatter := logging.NewBackendFormatter(infoBackend, infoFormat)
		infoLeveled := logging.AddModuleLevel(notToExceedLevel(logging.INFO, infoFormatter))
		infoLeveled.SetLevel(logging.INFO, "")

		if verbosity >= 2 {
			debugBackend := logging.NewLogBackend(os.Stdout, "", 0)
			debugFormat := logging.MustStringFormatter(
				`%{color}%{time:15:04:05.000} %{module} ▶ %{id:03x}%{color:reset} %{message}`,
			)
			debugFormatter := logging.NewBackendFormatter(debugBackend, debugFormat)
			debugLeveled := logging.AddModuleLevel(notToExceedLevel(logging.DEBUG, debugFormatter))
			debugLeveled.SetLevel(logging.DEBUG, "")

			logging.SetBackend(debugLeveled, infoLeveled, warnLeveled, errLeveled)
		} else {
			logging.SetBackend(infoLeveled, warnLeveled, errLeveled)
		}
	} else {
		logging.SetBackend(warnLeveled, errLeveled)
	}

}

func notToExceedLevel(notToExceed logging.Level, delegate logging.Backend) logging.Backend {
	return &notToExceedLevelBackend{
		delegate:         delegate,
		notToExceedLevel: notToExceed,
	}
}

type notToExceedLevelBackend struct {
	delegate         logging.Backend
	notToExceedLevel logging.Level
}

func (b *notToExceedLevelBackend) Log(l logging.Level, i int, r *logging.Record) error {
	if l < b.notToExceedLevel {
		return nil
	}
	return b.delegate.Log(l, i, r)
}
<|endoftext|>


<commit_before>package jet

import (
	"reflect"
	"testing"
)

func TestColumnToFieldName(t *testing.T) {
	if x := columnToFieldName("column_one"); x != "ColumnOne" {
		t.Fatal(x)
	}
	if x := columnToFieldName("a"); x != "A" {
		t.Fatal(x)
	}
	if x := columnToFieldName("ab"); x != "Ab" {
		t.Fatal(x)
	}
	if x := columnToFieldName("Already_Camel"); x != "AlreadyCamel" {
		t.Fatal(x)
	}
}

func TestUnpackStruct(t *testing.T) {
	m := map[string]interface{}{
		"ab_c": int64(9),
		"c_d":  "hello",
		"e":    "unsettable",
		"f":    []uint8("uint8str"),
		"g":    []uint8("uint8data"),
	}
	type out struct {
		AbC int64
		CD  string
		e   string
		F   string
		G   []byte
	}

	// Unpack struct
	var v out
	err := mapper{m}.unpack(v)
	if err == nil {
		t.Fatal("should return error")
	}
	err = mapper{m}.unpack(&v)
	if err != nil {
		t.Fatal(err.Error())
	}
	if x := v.AbC; x != 9 {
		t.Fatal(x)
	}
	if x := v.CD; x != "hello" {
		t.Fatal(x)
	}
	if x := v.e; x != "" {
		t.Fatal(x)
	}
	if x := v.F; x != "uint8str" {
		t.Fatal(x)
	}
	if x := v.G; string(x) != "uint8data" {
		t.Fatal(x)
	}
}

func TestUnpackMap(t *testing.T) {
	m := map[string]interface{}{
		"ab_c": int64(9),
		"c_d":  "hello",
		"e":    "unsettable",
	}
	type out struct {
		AbC int64
		CD  string
		e   string
	}
	var m2 map[string]interface{}
	err := mapper{m}.unpack(&m2)
	if err != nil {
		t.Fatal(err.Error())
	}
	if !reflect.DeepEqual(m, m2) {
		t.Fatalf("%v\n\n%v\n", m, m2)
	}
}

func TestUnpackStructSlice(t *testing.T) {
	m := map[string]interface{}{
		"A": int64(1),
		"B": "hello",
	}
	m2 := map[string]interface{}{
		"A": int64(2),
		"B": "hello2",
	}
	type out struct {
		A int64
		B string
	}

	// Unpack struct slice
	var v []out
	err := mapper{m}.unpack(v)
	if err == nil {
		t.Fatal("should return error")
	}
	err = mapper{m}.unpack(&v)
	if err != nil {
		t.Fatal(err.Error())
	}
	err = mapper{m2}.unpack(&v)
	if err != nil {
		t.Fatal(err.Error())
	}
	if x := len(v); x != 2 {
		t.Fatal(x)
	}
	if x := v[0].A; x != 1 {
		t.Fatal(x)
	}
	if x := v[1].A; x != 2 {
		t.Fatal(x)
	}
	if x := v[0].B; x != "hello" {
		t.Fatal(x)
	}
	if x := v[1].B; x != "hello2" {
		t.Fatal(x)
	}
}
<commit_msg>unpack to anonymous struct slice<commit_after>package jet

import (
	"reflect"
	"testing"
)

func TestColumnToFieldName(t *testing.T) {
	if x := columnToFieldName("column_one"); x != "ColumnOne" {
		t.Fatal(x)
	}
	if x := columnToFieldName("a"); x != "A" {
		t.Fatal(x)
	}
	if x := columnToFieldName("ab"); x != "Ab" {
		t.Fatal(x)
	}
	if x := columnToFieldName("Already_Camel"); x != "AlreadyCamel" {
		t.Fatal(x)
	}
}

func TestUnpackStruct(t *testing.T) {
	m := map[string]interface{}{
		"ab_c": int64(9),
		"c_d":  "hello",
		"e":    "unsettable",
		"f":    []uint8("uint8str"),
		"g":    []uint8("uint8data"),
	}
	type out struct {
		AbC int64
		CD  string
		e   string
		F   string
		G   []byte
	}

	// Unpack struct
	var v out
	err := mapper{m}.unpack(v)
	if err == nil {
		t.Fatal("should return error")
	}
	err = mapper{m}.unpack(&v)
	if err != nil {
		t.Fatal(err)
	}
	if x := v.AbC; x != 9 {
		t.Fatal(x)
	}
	if x := v.CD; x != "hello" {
		t.Fatal(x)
	}
	if x := v.e; x != "" {
		t.Fatal(x)
	}
	if x := v.F; x != "uint8str" {
		t.Fatal(x)
	}
	if x := v.G; string(x) != "uint8data" {
		t.Fatal(x)
	}
}

func TestUnpackMap(t *testing.T) {
	m := map[string]interface{}{
		"ab_c": int64(9),
		"c_d":  "hello",
		"e":    "unsettable",
	}
	type out struct {
		AbC int64
		CD  string
		e   string
	}
	var m2 map[string]interface{}
	err := mapper{m}.unpack(&m2)
	if err != nil {
		t.Fatal(err.Error())
	}
	if !reflect.DeepEqual(m, m2) {
		t.Fatalf("%v\n\n%v\n", m, m2)
	}
}

func TestUnpackStructSlice(t *testing.T) {
	m := map[string]interface{}{
		"A": int64(1),
		"B": "hello",
	}
	m2 := map[string]interface{}{
		"A": int64(2),
		"B": "hello2",
	}
	// Unpack struct slice
	var v []struct {
		A int64
		B string
	}
	err := mapper{m}.unpack(v)
	if err == nil {
		t.Fatal("should return error")
	}
	err = mapper{m}.unpack(&v)
	if err != nil {
		t.Fatal(err.Error())
	}
	err = mapper{m2}.unpack(&v)
	if err != nil {
		t.Fatal(err.Error())
	}
	if x := len(v); x != 2 {
		t.Fatal(x)
	}
	if x := v[0].A; x != 1 {
		t.Fatal(x)
	}
	if x := v[1].A; x != 2 {
		t.Fatal(x)
	}
	if x := v[0].B; x != "hello" {
		t.Fatal(x)
	}
	if x := v[1].B; x != "hello2" {
		t.Fatal(x)
	}
}
<|endoftext|>


<commit_before>package marvel

import (
	"testing"
)

func TestRequest(t *testing.T) {
	r, err := NewClient("d96b5157cfc7a60cbfaa715dc23c3eb1", "ccbc72b222419e2a4e40b4027f3bcb356142651b").Series(2258, CommonRequest{})
	if err != nil {
		t.Errorf("error: %v", err)
		return
	}
	for _, iss := range r.Data.Results {
		t.Logf(iss.Modified.Parse().String())
		t.Logf(iss.Thumbnail.URL(PortraitIncredible))
	}
}
<commit_msg>flags for api creds in tests<commit_after>package marvel

import (
	"flag"
	"testing"
)

var (
	apiKey = flag.String("pub", "", "Public API key")
	secret = flag.String("priv", "", "Private API secret")
)

func TestRequest(t *testing.T) {
	flag.Parse()

	r, err := NewClient(*apiKey, *secret).Series(2258, CommonRequest{})
	if err != nil {
		t.Errorf("error: %v", err)
		return
	}
	for _, iss := range r.Data.Results {
		t.Logf(iss.Modified.Parse().String())
		t.Logf(iss.Thumbnail.URL(PortraitIncredible))
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// 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 main

import (
	"context"
	"crypto/tls"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/websocket"

	huproxy "github.com/google/huproxy/lib"
)

var (
	writeTimeout = flag.Duration("write_timeout", 10*time.Second, "Write timeout")
	basicAuth    = flag.String("auth", "", "HTTP Basic Auth in @<filename> or <username>:<password> format.")
	verbose      = flag.Bool("verbose", false, "Verbose.")
	insecure     = flag.Bool("insecure_conn", false, "Skip certificate validation")
)

func secretString(s string) (string, error) {
	if strings.HasPrefix(s, "@") {
		fn := s[1:]
		st, err := os.Stat(fn)
		if err != nil {
			return "", err
		}
		p := st.Mode() & os.ModePerm
		if p&0177 > 0 {
			return "", fmt.Errorf("valid permissions for %q is %0o, was %0o", fn, 0600, p)
		}
		b, err := ioutil.ReadFile(fn)
		return strings.TrimSpace(string(b)), err
	}
	return s, nil
}

func dialError(url string, resp *http.Response, err error) {
	if resp != nil {
		extra := ""
		if *verbose {
			b, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Printf("Failed to read HTTP body: %v", err)
			}
			extra = "Body:\n" + string(b)
		}
		log.Fatalf("%s: HTTP error: %d %s\n%s", err, resp.StatusCode, resp.Status, extra)

	}
	log.Fatalf("Dial to %q fail: %v", url, err)
}

func main() {
	flag.Parse()

	if flag.NArg() != 1 {
		log.Fatalf("Want exactly one arg")
	}
	url := flag.Arg(0)

	if *verbose {
		log.Printf("huproxyclient %s", huproxy.Version)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	var tlsConfig *tls.Config
	if *insecure {
		tlsConfig = &tls.Config{InsecureSkipVerify: true}
	} else {
		tlsConfig = nil
	}
	dialer := websocket.Dialer{
		TLSClientConfig: tlsConfig,
	}
	head := map[string][]string{}

	// Add basic auth.
	if *basicAuth != "" {
		ss, err := secretString(*basicAuth)
		if err != nil {
			log.Fatalf("Error reading secret string %q: %v", *basicAuth, err)
		}
		a := base64.StdEncoding.EncodeToString([]byte(ss))
		head["Authorization"] = []string{
			"Basic " + a,
		}
	}

	conn, resp, err := dialer.Dial(url, head)
	if err != nil {
		dialError(url, resp, err)
	}
	defer conn.Close()

	// websocket -> stdout
	go func() {
		for {
			mt, r, err := conn.NextReader()
			if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
				return
			}
			if err != nil {
				log.Fatal(err)
			}
			if mt != websocket.BinaryMessage {
				log.Fatal("blah")
			}
			if _, err := io.Copy(os.Stdout, r); err != nil {
				log.Printf("Reading from websocket: %v", err)
				cancel()
			}
		}
	}()

	// stdin -> websocket
	// TODO: NextWriter() seems to be broken.
	if err := huproxy.File2WS(ctx, cancel, os.Stdin, conn); err == io.EOF {
		if err := conn.WriteControl(websocket.CloseMessage,
			websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""),
			time.Now().Add(*writeTimeout)); err == websocket.ErrCloseSent {
		} else if err != nil {
			log.Printf("Error sending close message: %v", err)
		}
	} else if err != nil {
		log.Printf("reading from stdin: %v", err)
		cancel()
	}

	if ctx.Err() != nil {
		os.Exit(1)
	}
}
<commit_msg>Add Thomas's review code<commit_after>// Copyright 2017 Google Inc.
//
// 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 main

import (
	"context"
	"crypto/tls"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/gorilla/websocket"

	huproxy "github.com/google/huproxy/lib"
)

var (
	writeTimeout = flag.Duration("write_timeout", 10*time.Second, "Write timeout")
	basicAuth    = flag.String("auth", "", "HTTP Basic Auth in @<filename> or <username>:<password> format.")
	verbose      = flag.Bool("verbose", false, "Verbose.")
	insecure     = flag.Bool("insecure_conn", false, "Skip certificate validation")
)

func secretString(s string) (string, error) {
	if strings.HasPrefix(s, "@") {
		fn := s[1:]
		st, err := os.Stat(fn)
		if err != nil {
			return "", err
		}
		p := st.Mode() & os.ModePerm
		if p&0177 > 0 {
			return "", fmt.Errorf("valid permissions for %q is %0o, was %0o", fn, 0600, p)
		}
		b, err := ioutil.ReadFile(fn)
		return strings.TrimSpace(string(b)), err
	}
	return s, nil
}

func dialError(url string, resp *http.Response, err error) {
	if resp != nil {
		extra := ""
		if *verbose {
			b, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Printf("Failed to read HTTP body: %v", err)
			}
			extra = "Body:\n" + string(b)
		}
		log.Fatalf("%s: HTTP error: %d %s\n%s", err, resp.StatusCode, resp.Status, extra)

	}
	log.Fatalf("Dial to %q fail: %v", url, err)
}

func main() {
	flag.Parse()

	if flag.NArg() != 1 {
		log.Fatalf("Want exactly one arg")
	}
	url := flag.Arg(0)

	if *verbose {
		log.Printf("huproxyclient %s", huproxy.Version)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	dialer := websocket.Dialer{}
	if *insecure {
		dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}
	head := map[string][]string{}

	// Add basic auth.
	if *basicAuth != "" {
		ss, err := secretString(*basicAuth)
		if err != nil {
			log.Fatalf("Error reading secret string %q: %v", *basicAuth, err)
		}
		a := base64.StdEncoding.EncodeToString([]byte(ss))
		head["Authorization"] = []string{
			"Basic " + a,
		}
	}

	conn, resp, err := dialer.Dial(url, head)
	if err != nil {
		dialError(url, resp, err)
	}
	defer conn.Close()

	// websocket -> stdout
	go func() {
		for {
			mt, r, err := conn.NextReader()
			if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
				return
			}
			if err != nil {
				log.Fatal(err)
			}
			if mt != websocket.BinaryMessage {
				log.Fatal("blah")
			}
			if _, err := io.Copy(os.Stdout, r); err != nil {
				log.Printf("Reading from websocket: %v", err)
				cancel()
			}
		}
	}()

	// stdin -> websocket
	// TODO: NextWriter() seems to be broken.
	if err := huproxy.File2WS(ctx, cancel, os.Stdin, conn); err == io.EOF {
		if err := conn.WriteControl(websocket.CloseMessage,
			websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""),
			time.Now().Add(*writeTimeout)); err == websocket.ErrCloseSent {
		} else if err != nil {
			log.Printf("Error sending close message: %v", err)
		}
	} else if err != nil {
		log.Printf("reading from stdin: %v", err)
		cancel()
	}

	if ctx.Err() != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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 networking

const (
	// GroupName is the name for the networking API group.
	GroupName = "networking.internal.knative.dev"

	// IngressClassAnnotationKey is the annotation for the
	// explicit class of ClusterIngress that a particular resource has
	// opted into. For example,
	//
	//    networking.knative.dev/ingress.class: some-network-impl
	//
	// This uses a different domain because unlike the resource, it is
	// user-facing.
	//
	// The parent resource may use its own annotations to choose the
	// annotation value for the ClusterIngress it uses.  Based on such
	// value a different reconciliation logic may be used (for examples,
	// Istio-based ClusterIngress will reconcile into a VirtualService).
	IngressClassAnnotationKey = "networking.knative.dev/ingress.class"

	// ClusterIngressLabelKey is the label key attached to underlying network programming
	// resources to indicate which ClusterIngress triggered their creation.
	ClusterIngressLabelKey = GroupName + "/clusteringress"

	// SKSLabelKey is the label key that SKS Controller attaches to the
	// underlying resources it controls.
	SKSLabelKey = GroupName + "/serverlessservice"

	// ServiceTypeKey is the label key attached to a service specifying the type of service.
	// e.g. Public, Metrics
	ServiceTypeKey = GroupName + "/serviceType"

	// OriginSecretNameLabelKey is the label key attached to the TLS secret to indicate
	// the name of the origin secret that the TLS secret is copied from.
	OriginSecretNameLabelKey = GroupName + "/originSecretName"

	// OriginSecretNamespaceLabelKey is the label key attached to the TLS secret
	// to indicate the namespace of the origin secret that the TLS secret is copied from.
	OriginSecretNamespaceLabelKey = GroupName + "/originSecretNamespace"
)

// ServiceType is the enumeration type for the Kubernetes services
// that we have in our system, classified by usage purpose.
type ServiceType string

const (
	// ServiceTypePrivate is the label value for internal only services
	// for user applications.
	ServiceTypePrivate ServiceType = "Private"
	// ServiceTypePublic is the label value for externally reachable
	// services for user applications.
	ServiceTypePublic ServiceType = "Public"
	// ServiceTypeMetrics is the label value for Metrics services. Such services
	// are used for meric scraping.
	ServiceTypeMetrics ServiceType = "Metrics"
)

// Pseudo-constants
var (
	// DefaultRetryCount will be set if Attempts not specified.
	DefaultRetryCount = 3
)
<commit_msg>Support reconciling ceritificate based on certificate class annotation (#4759)<commit_after>/*
Copyright 2018 The Knative 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 networking

const (
	// GroupName is the name for the networking API group.
	GroupName = "networking.internal.knative.dev"

	// IngressClassAnnotationKey is the annotation for the
	// explicit class of ClusterIngress that a particular resource has
	// opted into. For example,
	//
	//    networking.knative.dev/ingress.class: some-network-impl
	//
	// This uses a different domain because unlike the resource, it is
	// user-facing.
	//
	// The parent resource may use its own annotations to choose the
	// annotation value for the ClusterIngress it uses.  Based on such
	// value a different reconciliation logic may be used (for examples,
	// Istio-based ClusterIngress will reconcile into a VirtualService).
	IngressClassAnnotationKey = "networking.knative.dev/ingress.class"

	// ClusterIngressLabelKey is the label key attached to underlying network programming
	// resources to indicate which ClusterIngress triggered their creation.
	ClusterIngressLabelKey = GroupName + "/clusteringress"

	// SKSLabelKey is the label key that SKS Controller attaches to the
	// underlying resources it controls.
	SKSLabelKey = GroupName + "/serverlessservice"

	// ServiceTypeKey is the label key attached to a service specifying the type of service.
	// e.g. Public, Metrics
	ServiceTypeKey = GroupName + "/serviceType"

	// OriginSecretNameLabelKey is the label key attached to the TLS secret to indicate
	// the name of the origin secret that the TLS secret is copied from.
	OriginSecretNameLabelKey = GroupName + "/originSecretName"

	// OriginSecretNamespaceLabelKey is the label key attached to the TLS secret
	// to indicate the namespace of the origin secret that the TLS secret is copied from.
	OriginSecretNamespaceLabelKey = GroupName + "/originSecretNamespace"

	// CertificateClassAnnotationKey is the annotation for the
	// explicit class of Certificate that a particular resource has
	// opted into. For example,
	//
	//    networking.internal.knative.dev/certificate.class: some-network-impl
	//
	// This uses a different domain because unlike the resource, it is
	// user-facing.
	//
	// The parent resource may use its own annotations to choose the
	// annotation value for the Certificate it uses.  Based on such
	// value a different reconciliation logic may be used (for examples,
	// Cert-Manager-based Certificate will reconcile into a Cert-Manager Certificate).
	CertificateClassAnnotationKey = GroupName + "/certificate.class"
)

// ServiceType is the enumeration type for the Kubernetes services
// that we have in our system, classified by usage purpose.
type ServiceType string

const (
	// ServiceTypePrivate is the label value for internal only services
	// for user applications.
	ServiceTypePrivate ServiceType = "Private"
	// ServiceTypePublic is the label value for externally reachable
	// services for user applications.
	ServiceTypePublic ServiceType = "Public"
	// ServiceTypeMetrics is the label value for Metrics services. Such services
	// are used for meric scraping.
	ServiceTypeMetrics ServiceType = "Metrics"
)

// Pseudo-constants
var (
	// DefaultRetryCount will be set if Attempts not specified.
	DefaultRetryCount = 3
)
<|endoftext|>