<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package options

import (
	"fmt"
	"net"
	"strings"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apiserver/pkg/server"
	utilfeature "k8s.io/apiserver/pkg/util/feature"

	"github.com/spf13/pflag"
)

// ServerRunOptions contains the options while running a generic api server.
type ServerRunOptions struct {
	AdvertiseAddress net.IP

	CorsAllowedOriginList       []string
	HSTSDirectives              []string
	ExternalHost                string
	MaxRequestsInFlight         int
	MaxMutatingRequestsInFlight int
	RequestTimeout              time.Duration
	GoawayChance                float64
	LivezGracePeriod            time.Duration
	MinRequestTimeout           int
	ShutdownDelayDuration       time.Duration
	// We intentionally did not add a flag for this option. Users of the
	// apiserver library can wire it to a flag.
	JSONPatchMaxCopyBytes int64
	// The limit on the request body size that would be accepted and
	// decoded in a write request. 0 means no limit.
	// We intentionally did not add a flag for this option. Users of the
	// apiserver library can wire it to a flag.
	MaxRequestBodyBytes       int64
	EnablePriorityAndFairness bool

	// ShutdownSendRetryAfter dictates when to initiate shutdown of the HTTP
	// Server during the graceful termination of the apiserver. If true, we wait
	// for non longrunning requests in flight to be drained and then initiate a
	// shutdown of the HTTP Server. If false, we initiate a shutdown of the HTTP
	// Server as soon as ShutdownDelayDuration has elapsed.
	// If enabled, after ShutdownDelayDuration elapses, any incoming request is
	// rejected with a 429 status code and a 'Retry-After' response.
	ShutdownSendRetryAfter bool
}

func NewServerRunOptions() *ServerRunOptions {
	defaults := server.NewConfig(serializer.CodecFactory{})
	return &ServerRunOptions{
		MaxRequestsInFlight:         defaults.MaxRequestsInFlight,
		MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
		RequestTimeout:              defaults.RequestTimeout,
		LivezGracePeriod:            defaults.LivezGracePeriod,
		MinRequestTimeout:           defaults.MinRequestTimeout,
		ShutdownDelayDuration:       defaults.ShutdownDelayDuration,
		JSONPatchMaxCopyBytes:       defaults.JSONPatchMaxCopyBytes,
		MaxRequestBodyBytes:         defaults.MaxRequestBodyBytes,
		EnablePriorityAndFairness:   true,
		ShutdownSendRetryAfter:      false,
	}
}

// ApplyTo applies the run options to the method receiver and returns self
func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
	c.CorsAllowedOriginList = s.CorsAllowedOriginList
	c.HSTSDirectives = s.HSTSDirectives
	c.ExternalAddress = s.ExternalHost
	c.MaxRequestsInFlight = s.MaxRequestsInFlight
	c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
	c.LivezGracePeriod = s.LivezGracePeriod
	c.RequestTimeout = s.RequestTimeout
	c.GoawayChance = s.GoawayChance
	c.MinRequestTimeout = s.MinRequestTimeout
	c.ShutdownDelayDuration = s.ShutdownDelayDuration
	c.JSONPatchMaxCopyBytes = s.JSONPatchMaxCopyBytes
	c.MaxRequestBodyBytes = s.MaxRequestBodyBytes
	c.PublicAddress = s.AdvertiseAddress
	c.ShutdownSendRetryAfter = s.ShutdownSendRetryAfter

	return nil
}

// DefaultAdvertiseAddress sets the field AdvertiseAddress if unset. The field will be set based on the SecureServingOptions.
func (s *ServerRunOptions) DefaultAdvertiseAddress(secure *SecureServingOptions) error {
	if secure == nil {
		return nil
	}

	if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
		hostIP, err := secure.DefaultExternalAddress()
		if err != nil {
			return fmt.Errorf("Unable to find suitable network address.error='%v'. "+
				"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err)
		}
		s.AdvertiseAddress = hostIP
	}

	return nil
}

// Validate checks validation of ServerRunOptions
func (s *ServerRunOptions) Validate() []error {
	errors := []error{}

	if s.LivezGracePeriod < 0 {
		errors = append(errors, fmt.Errorf("--livez-grace-period can not be a negative value"))
	}

	if s.MaxRequestsInFlight < 0 {
		errors = append(errors, fmt.Errorf("--max-requests-inflight can not be negative value"))
	}
	if s.MaxMutatingRequestsInFlight < 0 {
		errors = append(errors, fmt.Errorf("--max-mutating-requests-inflight can not be negative value"))
	}

	if s.RequestTimeout.Nanoseconds() < 0 {
		errors = append(errors, fmt.Errorf("--request-timeout can not be negative value"))
	}

	if s.GoawayChance < 0 || s.GoawayChance > 0.02 {
		errors = append(errors, fmt.Errorf("--goaway-chance can not be less than 0 or greater than 0.02"))
	}

	if s.MinRequestTimeout < 0 {
		errors = append(errors, fmt.Errorf("--min-request-timeout can not be negative value"))
	}

	if s.ShutdownDelayDuration < 0 {
		errors = append(errors, fmt.Errorf("--shutdown-delay-duration can not be negative value"))
	}

	if s.JSONPatchMaxCopyBytes < 0 {
		errors = append(errors, fmt.Errorf("ServerRunOptions.JSONPatchMaxCopyBytes can not be negative value"))
	}

	if s.MaxRequestBodyBytes < 0 {
		errors = append(errors, fmt.Errorf("ServerRunOptions.MaxRequestBodyBytes can not be negative value"))
	}

	if err := validateHSTSDirectives(s.HSTSDirectives); err != nil {
		errors = append(errors, err)
	}
	return errors
}

func validateHSTSDirectives(hstsDirectives []string) error {
	// HSTS Headers format: Strict-Transport-Security:max-age=expireTime [;includeSubDomains] [;preload]
	// See https://tools.ietf.org/html/rfc6797#section-6.1 for more information
	allErrors := []error{}
	for _, hstsDirective := range hstsDirectives {
		if len(strings.TrimSpace(hstsDirective)) == 0 {
			allErrors = append(allErrors, fmt.Errorf("empty value in strict-transport-security-directives"))
			continue
		}
		if hstsDirective != "includeSubDomains" && hstsDirective != "preload" {
			maxAgeDirective := strings.Split(hstsDirective, "=")
			if len(maxAgeDirective) != 2 || maxAgeDirective[0] != "max-age" {
				allErrors = append(allErrors, fmt.Errorf("--strict-transport-security-directives invalid, allowed values: max-age=expireTime, includeSubDomains, preload. see https://tools.ietf.org/html/rfc6797#section-6.1 for more information"))
			}
		}
	}
	return errors.NewAggregate(allErrors)
}

// AddUniversalFlags adds flags for a specific APIServer to the specified FlagSet
func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
	// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
	// arrange these text blocks sensibly. Grrr.

	fs.IPVar(&s.AdvertiseAddress, "advertise-address", s.AdvertiseAddress, ""+
		"The IP address on which to advertise the apiserver to members of the cluster. This "+
		"address must be reachable by the rest of the cluster. If blank, the --bind-address "+
		"will be used. If --bind-address is unspecified, the host's default interface will "+
		"be used.")

	fs.StringSliceVar(&s.CorsAllowedOriginList, "cors-allowed-origins", s.CorsAllowedOriginList, ""+
		"List of allowed origins for CORS, comma separated.  An allowed origin can be a regular "+
		"expression to support subdomain matching. If this list is empty CORS will not be enabled.")

	fs.StringSliceVar(&s.HSTSDirectives, "strict-transport-security-directives", s.HSTSDirectives, ""+
		"List of directives for HSTS, comma separated. If this list is empty, then HSTS directives will not "+
		"be added. Example: 'max-age=31536000,includeSubDomains,preload'")

	fs.StringVar(&s.ExternalHost, "external-hostname", s.ExternalHost,
		"The hostname to use when generating externalized URLs for this master (e.g. Swagger API Docs or OpenID Discovery).")

	deprecatedMasterServiceNamespace := metav1.NamespaceDefault
	fs.StringVar(&deprecatedMasterServiceNamespace, "master-service-namespace", deprecatedMasterServiceNamespace, ""+
		"DEPRECATED: the namespace from which the Kubernetes master services should be injected into pods.")

	fs.IntVar(&s.MaxRequestsInFlight, "max-requests-inflight", s.MaxRequestsInFlight, ""+
		"This and --max-mutating-requests-inflight are summed to determine the server's total concurrency limit "+
		"(which must be positive) if --enable-priority-and-fairness is true. "+
		"Otherwise, this flag limits the maximum number of non-mutating requests in flight, "+
		"or a zero value disables the limit completely.")

	fs.IntVar(&s.MaxMutatingRequestsInFlight, "max-mutating-requests-inflight", s.MaxMutatingRequestsInFlight, ""+
		"This and --max-requests-inflight are summed to determine the server's total concurrency limit "+
		"(which must be positive) if --enable-priority-and-fairness is true. "+
		"Otherwise, this flag limits the maximum number of mutating requests in flight, "+
		"or a zero value disables the limit completely.")

	fs.DurationVar(&s.RequestTimeout, "request-timeout", s.RequestTimeout, ""+
		"An optional field indicating the duration a handler must keep a request open before timing "+
		"it out. This is the default request timeout for requests but may be overridden by flags such as "+
		"--min-request-timeout for specific types of requests.")

	fs.Float64Var(&s.GoawayChance, "goaway-chance", s.GoawayChance, ""+
		"To prevent HTTP/2 clients from getting stuck on a single apiserver, randomly close a connection (GOAWAY). "+
		"The client's other in-flight requests won't be affected, and the client will reconnect, likely landing on a different apiserver after going through the load balancer again. "+
		"This argument sets the fraction of requests that will be sent a GOAWAY. Clusters with single apiservers, or which don't use a load balancer, should NOT enable this. "+
		"Min is 0 (off), Max is .02 (1/50 requests); .001 (1/1000) is a recommended starting point.")

	fs.DurationVar(&s.LivezGracePeriod, "livez-grace-period", s.LivezGracePeriod, ""+
		"This option represents the maximum amount of time it should take for apiserver to complete its startup sequence "+
		"and become live. From apiserver's start time to when this amount of time has elapsed, /livez will assume "+
		"that unfinished post-start hooks will complete successfully and therefore return true.")

	fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+
		"An optional field indicating the minimum number of seconds a handler must keep "+
		"a request open before timing it out. Currently only honored by the watch request "+
		"handler, which picks a randomized value above this number as the connection timeout, "+
		"to spread out load.")

	fs.BoolVar(&s.EnablePriorityAndFairness, "enable-priority-and-fairness", s.EnablePriorityAndFairness, ""+
		"If true and the APIPriorityAndFairness feature gate is enabled, replace the max-in-flight handler with an enhanced one that queues and dispatches with priority and fairness")

	fs.DurationVar(&s.ShutdownDelayDuration, "shutdown-delay-duration", s.ShutdownDelayDuration, ""+
		"Time to delay the termination. During that time the server keeps serving requests normally. The endpoints /healthz and /livez "+
		"will return success, but /readyz immediately returns failure. Graceful termination starts after this delay "+
		"has elapsed. This can be used to allow load balancer to stop sending traffic to this server.")

	fs.BoolVar(&s.ShutdownSendRetryAfter, "shutdown-send-retry-after", s.ShutdownSendRetryAfter, ""+
		"If true the HTTP Server will continue listening until all non long running request(s) in flight have been drained, "+
		"during this window all incoming requests will be rejected with a status code 429 and a 'Retry-After' response header, "+
		"in addition 'Connection: close' response header is set in order to tear down the TCP connection when idle.")

	utilfeature.DefaultMutableFeatureGate.AddFlag(fs)
}
<commit_msg>kube-apiserver: mark unused master-service-namespace flag as deprecated<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package options

import (
	"fmt"
	"net"
	"strings"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apiserver/pkg/server"
	utilfeature "k8s.io/apiserver/pkg/util/feature"

	"github.com/spf13/pflag"
)

// ServerRunOptions contains the options while running a generic api server.
type ServerRunOptions struct {
	AdvertiseAddress net.IP

	CorsAllowedOriginList       []string
	HSTSDirectives              []string
	ExternalHost                string
	MaxRequestsInFlight         int
	MaxMutatingRequestsInFlight int
	RequestTimeout              time.Duration
	GoawayChance                float64
	LivezGracePeriod            time.Duration
	MinRequestTimeout           int
	ShutdownDelayDuration       time.Duration
	// We intentionally did not add a flag for this option. Users of the
	// apiserver library can wire it to a flag.
	JSONPatchMaxCopyBytes int64
	// The limit on the request body size that would be accepted and
	// decoded in a write request. 0 means no limit.
	// We intentionally did not add a flag for this option. Users of the
	// apiserver library can wire it to a flag.
	MaxRequestBodyBytes       int64
	EnablePriorityAndFairness bool

	// ShutdownSendRetryAfter dictates when to initiate shutdown of the HTTP
	// Server during the graceful termination of the apiserver. If true, we wait
	// for non longrunning requests in flight to be drained and then initiate a
	// shutdown of the HTTP Server. If false, we initiate a shutdown of the HTTP
	// Server as soon as ShutdownDelayDuration has elapsed.
	// If enabled, after ShutdownDelayDuration elapses, any incoming request is
	// rejected with a 429 status code and a 'Retry-After' response.
	ShutdownSendRetryAfter bool
}

func NewServerRunOptions() *ServerRunOptions {
	defaults := server.NewConfig(serializer.CodecFactory{})
	return &ServerRunOptions{
		MaxRequestsInFlight:         defaults.MaxRequestsInFlight,
		MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
		RequestTimeout:              defaults.RequestTimeout,
		LivezGracePeriod:            defaults.LivezGracePeriod,
		MinRequestTimeout:           defaults.MinRequestTimeout,
		ShutdownDelayDuration:       defaults.ShutdownDelayDuration,
		JSONPatchMaxCopyBytes:       defaults.JSONPatchMaxCopyBytes,
		MaxRequestBodyBytes:         defaults.MaxRequestBodyBytes,
		EnablePriorityAndFairness:   true,
		ShutdownSendRetryAfter:      false,
	}
}

// ApplyTo applies the run options to the method receiver and returns self
func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
	c.CorsAllowedOriginList = s.CorsAllowedOriginList
	c.HSTSDirectives = s.HSTSDirectives
	c.ExternalAddress = s.ExternalHost
	c.MaxRequestsInFlight = s.MaxRequestsInFlight
	c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
	c.LivezGracePeriod = s.LivezGracePeriod
	c.RequestTimeout = s.RequestTimeout
	c.GoawayChance = s.GoawayChance
	c.MinRequestTimeout = s.MinRequestTimeout
	c.ShutdownDelayDuration = s.ShutdownDelayDuration
	c.JSONPatchMaxCopyBytes = s.JSONPatchMaxCopyBytes
	c.MaxRequestBodyBytes = s.MaxRequestBodyBytes
	c.PublicAddress = s.AdvertiseAddress
	c.ShutdownSendRetryAfter = s.ShutdownSendRetryAfter

	return nil
}

// DefaultAdvertiseAddress sets the field AdvertiseAddress if unset. The field will be set based on the SecureServingOptions.
func (s *ServerRunOptions) DefaultAdvertiseAddress(secure *SecureServingOptions) error {
	if secure == nil {
		return nil
	}

	if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
		hostIP, err := secure.DefaultExternalAddress()
		if err != nil {
			return fmt.Errorf("Unable to find suitable network address.error='%v'. "+
				"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err)
		}
		s.AdvertiseAddress = hostIP
	}

	return nil
}

// Validate checks validation of ServerRunOptions
func (s *ServerRunOptions) Validate() []error {
	errors := []error{}

	if s.LivezGracePeriod < 0 {
		errors = append(errors, fmt.Errorf("--livez-grace-period can not be a negative value"))
	}

	if s.MaxRequestsInFlight < 0 {
		errors = append(errors, fmt.Errorf("--max-requests-inflight can not be negative value"))
	}
	if s.MaxMutatingRequestsInFlight < 0 {
		errors = append(errors, fmt.Errorf("--max-mutating-requests-inflight can not be negative value"))
	}

	if s.RequestTimeout.Nanoseconds() < 0 {
		errors = append(errors, fmt.Errorf("--request-timeout can not be negative value"))
	}

	if s.GoawayChance < 0 || s.GoawayChance > 0.02 {
		errors = append(errors, fmt.Errorf("--goaway-chance can not be less than 0 or greater than 0.02"))
	}

	if s.MinRequestTimeout < 0 {
		errors = append(errors, fmt.Errorf("--min-request-timeout can not be negative value"))
	}

	if s.ShutdownDelayDuration < 0 {
		errors = append(errors, fmt.Errorf("--shutdown-delay-duration can not be negative value"))
	}

	if s.JSONPatchMaxCopyBytes < 0 {
		errors = append(errors, fmt.Errorf("ServerRunOptions.JSONPatchMaxCopyBytes can not be negative value"))
	}

	if s.MaxRequestBodyBytes < 0 {
		errors = append(errors, fmt.Errorf("ServerRunOptions.MaxRequestBodyBytes can not be negative value"))
	}

	if err := validateHSTSDirectives(s.HSTSDirectives); err != nil {
		errors = append(errors, err)
	}
	return errors
}

func validateHSTSDirectives(hstsDirectives []string) error {
	// HSTS Headers format: Strict-Transport-Security:max-age=expireTime [;includeSubDomains] [;preload]
	// See https://tools.ietf.org/html/rfc6797#section-6.1 for more information
	allErrors := []error{}
	for _, hstsDirective := range hstsDirectives {
		if len(strings.TrimSpace(hstsDirective)) == 0 {
			allErrors = append(allErrors, fmt.Errorf("empty value in strict-transport-security-directives"))
			continue
		}
		if hstsDirective != "includeSubDomains" && hstsDirective != "preload" {
			maxAgeDirective := strings.Split(hstsDirective, "=")
			if len(maxAgeDirective) != 2 || maxAgeDirective[0] != "max-age" {
				allErrors = append(allErrors, fmt.Errorf("--strict-transport-security-directives invalid, allowed values: max-age=expireTime, includeSubDomains, preload. see https://tools.ietf.org/html/rfc6797#section-6.1 for more information"))
			}
		}
	}
	return errors.NewAggregate(allErrors)
}

// AddUniversalFlags adds flags for a specific APIServer to the specified FlagSet
func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
	// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
	// arrange these text blocks sensibly. Grrr.

	fs.IPVar(&s.AdvertiseAddress, "advertise-address", s.AdvertiseAddress, ""+
		"The IP address on which to advertise the apiserver to members of the cluster. This "+
		"address must be reachable by the rest of the cluster. If blank, the --bind-address "+
		"will be used. If --bind-address is unspecified, the host's default interface will "+
		"be used.")

	fs.StringSliceVar(&s.CorsAllowedOriginList, "cors-allowed-origins", s.CorsAllowedOriginList, ""+
		"List of allowed origins for CORS, comma separated.  An allowed origin can be a regular "+
		"expression to support subdomain matching. If this list is empty CORS will not be enabled.")

	fs.StringSliceVar(&s.HSTSDirectives, "strict-transport-security-directives", s.HSTSDirectives, ""+
		"List of directives for HSTS, comma separated. If this list is empty, then HSTS directives will not "+
		"be added. Example: 'max-age=31536000,includeSubDomains,preload'")

	fs.StringVar(&s.ExternalHost, "external-hostname", s.ExternalHost,
		"The hostname to use when generating externalized URLs for this master (e.g. Swagger API Docs or OpenID Discovery).")

	deprecatedMasterServiceNamespace := metav1.NamespaceDefault
	fs.StringVar(&deprecatedMasterServiceNamespace, "master-service-namespace", deprecatedMasterServiceNamespace, ""+
		"DEPRECATED: the namespace from which the Kubernetes master services should be injected into pods.")
	fs.MarkDeprecated("master-service-namespace", "This flag will be removed in v1.27")

	fs.IntVar(&s.MaxRequestsInFlight, "max-requests-inflight", s.MaxRequestsInFlight, ""+
		"This and --max-mutating-requests-inflight are summed to determine the server's total concurrency limit "+
		"(which must be positive) if --enable-priority-and-fairness is true. "+
		"Otherwise, this flag limits the maximum number of non-mutating requests in flight, "+
		"or a zero value disables the limit completely.")

	fs.IntVar(&s.MaxMutatingRequestsInFlight, "max-mutating-requests-inflight", s.MaxMutatingRequestsInFlight, ""+
		"This and --max-requests-inflight are summed to determine the server's total concurrency limit "+
		"(which must be positive) if --enable-priority-and-fairness is true. "+
		"Otherwise, this flag limits the maximum number of mutating requests in flight, "+
		"or a zero value disables the limit completely.")

	fs.DurationVar(&s.RequestTimeout, "request-timeout", s.RequestTimeout, ""+
		"An optional field indicating the duration a handler must keep a request open before timing "+
		"it out. This is the default request timeout for requests but may be overridden by flags such as "+
		"--min-request-timeout for specific types of requests.")

	fs.Float64Var(&s.GoawayChance, "goaway-chance", s.GoawayChance, ""+
		"To prevent HTTP/2 clients from getting stuck on a single apiserver, randomly close a connection (GOAWAY). "+
		"The client's other in-flight requests won't be affected, and the client will reconnect, likely landing on a different apiserver after going through the load balancer again. "+
		"This argument sets the fraction of requests that will be sent a GOAWAY. Clusters with single apiservers, or which don't use a load balancer, should NOT enable this. "+
		"Min is 0 (off), Max is .02 (1/50 requests); .001 (1/1000) is a recommended starting point.")

	fs.DurationVar(&s.LivezGracePeriod, "livez-grace-period", s.LivezGracePeriod, ""+
		"This option represents the maximum amount of time it should take for apiserver to complete its startup sequence "+
		"and become live. From apiserver's start time to when this amount of time has elapsed, /livez will assume "+
		"that unfinished post-start hooks will complete successfully and therefore return true.")

	fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+
		"An optional field indicating the minimum number of seconds a handler must keep "+
		"a request open before timing it out. Currently only honored by the watch request "+
		"handler, which picks a randomized value above this number as the connection timeout, "+
		"to spread out load.")

	fs.BoolVar(&s.EnablePriorityAndFairness, "enable-priority-and-fairness", s.EnablePriorityAndFairness, ""+
		"If true and the APIPriorityAndFairness feature gate is enabled, replace the max-in-flight handler with an enhanced one that queues and dispatches with priority and fairness")

	fs.DurationVar(&s.ShutdownDelayDuration, "shutdown-delay-duration", s.ShutdownDelayDuration, ""+
		"Time to delay the termination. During that time the server keeps serving requests normally. The endpoints /healthz and /livez "+
		"will return success, but /readyz immediately returns failure. Graceful termination starts after this delay "+
		"has elapsed. This can be used to allow load balancer to stop sending traffic to this server.")

	fs.BoolVar(&s.ShutdownSendRetryAfter, "shutdown-send-retry-after", s.ShutdownSendRetryAfter, ""+
		"If true the HTTP Server will continue listening until all non long running request(s) in flight have been drained, "+
		"during this window all incoming requests will be rejected with a status code 429 and a 'Retry-After' response header, "+
		"in addition 'Connection: close' response header is set in order to tear down the TCP connection when idle.")

	utilfeature.DefaultMutableFeatureGate.AddFlag(fs)
}
<|endoftext|>


<commit_before>// Copyright 2015-2017 trivago GmbH
//
// 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 core

import (
	"testing"
	"time"

	"github.com/trivago/tgo/ttesting"
)

func getMockMessage(data string) *Message {
	msg := &Message{
		prevStreamID: 2,
		source:       nil,
		timestamp:    time.Now(),
		sequence:     4,
	}

	msg.data.payload = []byte(data)
	msg.data.streamID = 1

	return msg
}

func TestMessageEnqueue(t *testing.T) {
	expect := ttesting.NewExpect(t)
	msgString := "Test for Enqueue()"
	msg := getMockMessage(msgString)
	buffer := NewMessageQueue(0)

	expect.Equal(MessageStateDiscard, buffer.Push(msg, -1))

	go func() {
		expect.Equal(MessageStateOk, buffer.Push(msg, 0))
	}()

	retMsg, _ := buffer.Pop()
	expect.Equal(msgString, retMsg.String())

	retStatus := buffer.Push(msg, 10*time.Millisecond)
	expect.Equal(MessageStateTimeout, retStatus)

	go func() {
		expect.Equal(MessageStateOk, buffer.Push(msg, 1*time.Second))
	}()

	retMsg, _ = buffer.Pop()
	expect.Equal(msgString, retMsg.String())
}
<commit_msg>added new message unit tests<commit_after>// Copyright 2015-2017 trivago GmbH
//
// 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 core

import (
	"testing"
	"time"

	"github.com/trivago/tgo/ttesting"
)

func getMockMessage(data string) *Message {
	msg := &Message{
		prevStreamID: 2,
		source:       nil,
		timestamp:    time.Now(),
		sequence:     4,
	}

	msg.data.payload = []byte(data)
	msg.data.streamID = 1

	return msg
}

func TestMessageEnqueue(t *testing.T) {
	expect := ttesting.NewExpect(t)
	msgString := "Test for Enqueue()"
	msg := getMockMessage(msgString)
	buffer := NewMessageQueue(0)

	expect.Equal(MessageStateDiscard, buffer.Push(msg, -1))

	go func() {
		expect.Equal(MessageStateOk, buffer.Push(msg, 0))
	}()

	retMsg, _ := buffer.Pop()
	expect.Equal(msgString, retMsg.String())

	retStatus := buffer.Push(msg, 10*time.Millisecond)
	expect.Equal(MessageStateTimeout, retStatus)

	go func() {
		expect.Equal(MessageStateOk, buffer.Push(msg, 1*time.Second))
	}()

	retMsg, _ = buffer.Pop()
	expect.Equal(msgString, retMsg.String())
}

func TestMessageInstantiate(t *testing.T) {
	expect := ttesting.NewExpect(t)
	msgString := "Test for instantiate"

	msg := NewMessage(nil, []byte(msgString), 1, 1)

	expect.Equal(msgString, string(msg.data.payload))
	expect.Equal(MessageStreamID(1), msg.data.streamID)
	expect.Equal(msgString, string(msg.orig.payload))
	expect.Equal(MessageStreamID(1), msg.orig.streamID)
}

func TestMessageOriginalDataIntegrity(t *testing.T) {
	expect := ttesting.NewExpect(t)
	msgString := "Test for original data integrity"
	msgUpdateString := "Test for original data integrity - UPDATE"

	msg := NewMessage(nil, []byte(msgString), 1, 1)

	msg.SetStreamID(MessageStreamID(10))
	msg.Store([]byte(msgUpdateString))

	expect.Equal(msgUpdateString, string(msg.data.payload))
	expect.Equal(MessageStreamID(10), msg.data.streamID)
	expect.Equal(msgString, string(msg.orig.payload))
	expect.Equal(MessageStreamID(1), msg.orig.streamID)
	expect.Equal(MessageStreamID(1), msg.prevStreamID)
}
<|endoftext|>


<commit_before>package models

import (
	"regexp"
	log "github.com/Sirupsen/logrus"
	"time"
	"errors"
	"fmt"
	"strings"
	"encoding/json"
)

type ResponseDelay struct {
	UrlPattern string `json:"urlpattern"`
	HttpMethod string `json:"httpmethod"`
	Delay      int `json:"delay"`
}

type ResponseDelayJson struct {
	Data *ResponseDelayList `json:"data"`
}

type ResponseDelayList []ResponseDelay

type ResponseDelays interface {
	Json() []byte
	GetDelay(url, httpMethod string) (*ResponseDelay)
	Len() int
}

func ValidateResponseDelayJson(j ResponseDelayJson) (err error) {
	if j.Data != nil {
		for _, delay := range *j.Data {
			if delay.UrlPattern != "" && delay.Delay != 0 {
				if _, err := regexp.Compile(delay.UrlPattern); err != nil {
					return errors.New(fmt.Sprintf("Response delay entry skipped due to invalid pattern : %s", delay.UrlPattern))
				}
			} else {
				return errors.New(fmt.Sprintf("Config error - Missing values found in: %v", delay))
			}
		}
	}
	return nil
}

func (this *ResponseDelay) Execute() {
	// apply the delay - must be called from goroutine handling the request
	log.Info("Pausing before sending the response to simulate delays")
	time.Sleep(time.Duration(this.Delay) * time.Millisecond)
	log.Info("Response delay completed")
}

func (this *ResponseDelayList) GetDelay(url, httpMethod string) (*ResponseDelay) {
	for _, val := range *this {
		match := regexp.MustCompile(val.UrlPattern).MatchString(url)
		if match {
			if val.HttpMethod == "" || strings.EqualFold(val.HttpMethod, httpMethod) {
				log.Info("Found response delay setting for this request host: ", val)
				return &val
			}
		}
	}
	return nil
}

func (this *ResponseDelayList) Json() []byte {
	resp := ResponseDelayJson{
		Data: this,
	}
	b, _ := json.Marshal(resp)
	return b
}

func (this *ResponseDelayList) Len() int {
	return this.Len()
}<commit_msg>accidentally make a recursive call with Len :(<commit_after>package models

import (
	"regexp"
	log "github.com/Sirupsen/logrus"
	"time"
	"errors"
	"fmt"
	"strings"
	"encoding/json"
)

type ResponseDelay struct {
	UrlPattern string `json:"urlpattern"`
	HttpMethod string `json:"httpmethod"`
	Delay      int `json:"delay"`
}

type ResponseDelayJson struct {
	Data *ResponseDelayList `json:"data"`
}

type ResponseDelayList []ResponseDelay

type ResponseDelays interface {
	Json() []byte
	GetDelay(url, httpMethod string) (*ResponseDelay)
	Len() int
}

func ValidateResponseDelayJson(j ResponseDelayJson) (err error) {
	if j.Data != nil {
		for _, delay := range *j.Data {
			if delay.UrlPattern != "" && delay.Delay != 0 {
				if _, err := regexp.Compile(delay.UrlPattern); err != nil {
					return errors.New(fmt.Sprintf("Response delay entry skipped due to invalid pattern : %s", delay.UrlPattern))
				}
			} else {
				return errors.New(fmt.Sprintf("Config error - Missing values found in: %v", delay))
			}
		}
	}
	return nil
}

func (this *ResponseDelay) Execute() {
	// apply the delay - must be called from goroutine handling the request
	log.Info("Pausing before sending the response to simulate delays")
	time.Sleep(time.Duration(this.Delay) * time.Millisecond)
	log.Info("Response delay completed")
}

func (this *ResponseDelayList) GetDelay(url, httpMethod string) (*ResponseDelay) {
	for _, val := range *this {
		match := regexp.MustCompile(val.UrlPattern).MatchString(url)
		if match {
			if val.HttpMethod == "" || strings.EqualFold(val.HttpMethod, httpMethod) {
				log.Info("Found response delay setting for this request host: ", val)
				return &val
			}
		}
	}
	return nil
}

func (this *ResponseDelayList) Json() []byte {
	resp := ResponseDelayJson{
		Data: this,
	}
	b, _ := json.Marshal(resp)
	return b
}

func (this *ResponseDelayList) Len() int {
	return len(this)
}<|endoftext|>


<commit_before>// +build js

package http

import (
	"bufio"
	"bytes"
	"errors"
	"io/ioutil"
	"net/textproto"
	"strconv"

	"github.com/gopherjs/gopherjs/js"
)

var DefaultTransport = func() RoundTripper {
	switch {
	case js.Global.Get("fetch") != js.Undefined && js.Global.Get("ReadableStream") != js.Undefined: // ReadableStream is used as a check for support of streaming response bodies, see https://fetch.spec.whatwg.org/#streams.
		return &fetchTransport{}
	case js.Global.Get("XMLHttpRequest") != js.Undefined:
		return &XHRTransport{}
	default:
		return noTransport{}
	}
}()

// noTransport is used when neither Fetch API nor XMLHttpRequest API are available. It always fails.
type noTransport struct{}

func (noTransport) RoundTrip(req *Request) (*Response, error) {
	return nil, errors.New("net/http: neither of Fetch nor XMLHttpRequest APIs is available")
}

type XHRTransport struct {
	inflight map[*Request]*js.Object
}

func (t *XHRTransport) RoundTrip(req *Request) (*Response, error) {
	xhr := js.Global.Get("XMLHttpRequest").New()

	if t.inflight == nil {
		t.inflight = map[*Request]*js.Object{}
	}
	t.inflight[req] = xhr
	defer delete(t.inflight, req)

	respCh := make(chan *Response)
	errCh := make(chan error)

	xhr.Set("onload", func() {
		header, _ := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(xhr.Call("getAllResponseHeaders").String() + "\n")))).ReadMIMEHeader()
		body := js.Global.Get("Uint8Array").New(xhr.Get("response")).Interface().([]byte)

		contentLength := int64(-1)
		switch req.Method {
		case "HEAD":
			if l, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
				contentLength = l
			}
		default:
			contentLength = int64(len(body))
		}

		respCh <- &Response{
			Status:        xhr.Get("status").String() + " " + xhr.Get("statusText").String(),
			StatusCode:    xhr.Get("status").Int(),
			Header:        Header(header),
			ContentLength: contentLength,
			Body:          ioutil.NopCloser(bytes.NewReader(body)),
			Request:       req,
		}
	})

	xhr.Set("onerror", func(e *js.Object) {
		errCh <- errors.New("net/http: XMLHttpRequest failed")
	})

	xhr.Set("onabort", func(e *js.Object) {
		errCh <- errors.New("net/http: request canceled")
	})

	xhr.Call("open", req.Method, req.URL.String())
	xhr.Set("responseType", "arraybuffer") // has to be after "open" until https://bugzilla.mozilla.org/show_bug.cgi?id=1110761 is resolved
	for key, values := range req.Header {
		for _, value := range values {
			xhr.Call("setRequestHeader", key, value)
		}
	}
	var body []byte
	if req.Body != nil {
		var err error
		body, err = ioutil.ReadAll(req.Body)
		if err != nil {
			req.Body.Close() // RoundTrip must always close the body, including on errors.
			return nil, err
		}
		req.Body.Close()
	}
	xhr.Call("send", body)

	select {
	case resp := <-respCh:
		return resp, nil
	case err := <-errCh:
		return nil, err
	}
}

func (t *XHRTransport) CancelRequest(req *Request) {
	if xhr, ok := t.inflight[req]; ok {
		xhr.Call("abort")
	}
}
<commit_msg>Don't send an empty body when there is no body<commit_after>// +build js

package http

import (
	"bufio"
	"bytes"
	"errors"
	"io/ioutil"
	"net/textproto"
	"strconv"

	"github.com/gopherjs/gopherjs/js"
)

var DefaultTransport = func() RoundTripper {
	switch {
	case js.Global.Get("fetch") != js.Undefined && js.Global.Get("ReadableStream") != js.Undefined: // ReadableStream is used as a check for support of streaming response bodies, see https://fetch.spec.whatwg.org/#streams.
		return &fetchTransport{}
	case js.Global.Get("XMLHttpRequest") != js.Undefined:
		return &XHRTransport{}
	default:
		return noTransport{}
	}
}()

// noTransport is used when neither Fetch API nor XMLHttpRequest API are available. It always fails.
type noTransport struct{}

func (noTransport) RoundTrip(req *Request) (*Response, error) {
	return nil, errors.New("net/http: neither of Fetch nor XMLHttpRequest APIs is available")
}

type XHRTransport struct {
	inflight map[*Request]*js.Object
}

func (t *XHRTransport) RoundTrip(req *Request) (*Response, error) {
	xhr := js.Global.Get("XMLHttpRequest").New()

	if t.inflight == nil {
		t.inflight = map[*Request]*js.Object{}
	}
	t.inflight[req] = xhr
	defer delete(t.inflight, req)

	respCh := make(chan *Response)
	errCh := make(chan error)

	xhr.Set("onload", func() {
		header, _ := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(xhr.Call("getAllResponseHeaders").String() + "\n")))).ReadMIMEHeader()
		body := js.Global.Get("Uint8Array").New(xhr.Get("response")).Interface().([]byte)

		contentLength := int64(-1)
		switch req.Method {
		case "HEAD":
			if l, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
				contentLength = l
			}
		default:
			contentLength = int64(len(body))
		}

		respCh <- &Response{
			Status:        xhr.Get("status").String() + " " + xhr.Get("statusText").String(),
			StatusCode:    xhr.Get("status").Int(),
			Header:        Header(header),
			ContentLength: contentLength,
			Body:          ioutil.NopCloser(bytes.NewReader(body)),
			Request:       req,
		}
	})

	xhr.Set("onerror", func(e *js.Object) {
		errCh <- errors.New("net/http: XMLHttpRequest failed")
	})

	xhr.Set("onabort", func(e *js.Object) {
		errCh <- errors.New("net/http: request canceled")
	})

	xhr.Call("open", req.Method, req.URL.String())
	xhr.Set("responseType", "arraybuffer") // has to be after "open" until https://bugzilla.mozilla.org/show_bug.cgi?id=1110761 is resolved
	for key, values := range req.Header {
		for _, value := range values {
			xhr.Call("setRequestHeader", key, value)
		}
	}
	if req.Body == nil {
		xhr.Call("send")
	} else {
		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			req.Body.Close() // RoundTrip must always close the body, including on errors.
			return nil, err
		}
		req.Body.Close()
		xhr.Call("send", body)
	}

	select {
	case resp := <-respCh:
		return resp, nil
	case err := <-errCh:
		return nil, err
	}
}

func (t *XHRTransport) CancelRequest(req *Request) {
	if xhr, ok := t.inflight[req]; ok {
		xhr.Call("abort")
	}
}
<|endoftext|>


<commit_before>package main

import (
//    "sync"
    "sync/atomic"
)

var dat atomic.Value
var dch chan byte = make(chan byte, 1)

func get_data() {
        m := make(map[string]string)
        m["abcd"] = "1234"
        dat.Store(m)
}

func update() {
    for range dch {
        get_data()
    }
}

func main() {
    get_data()
    go update()
    m := dat.Load().(map[string]string)
    println(m["abcd"])
}
<commit_msg>add link<commit_after>package main

import (
//    "sync"
    "sync/atomic"
)

//https://godoc.org/golang.org/x/exp/inotify

var dat atomic.Value
var dch chan byte = make(chan byte, 1)

func get_data() {
        m := make(map[string]string)
        m["abcd"] = "1234"
        dat.Store(m)
}

func update() {
    for range dch {
        get_data()
    }
}

func main() {
    get_data()
    go update()
    m := dat.Load().(map[string]string)
    println(m["abcd"])
}
<|endoftext|>


<commit_before>package goblet

import (
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

const (
	SAVEFILE_SUCCESS              = iota
	SAVEFILE_STATE_DIR_ERROR      = iota
	SAVEFILE_CREATE_DIR_ERROR     = iota
	SAVEFILE_FORMFILE_ERROR       = iota
	SAVEFILE_RENAME_ERROR_BY_USER = iota
	SAVEFILE_COPY_ERROR           = iota
)

func (cx *Context) SaveFileAt(path ...string) *filerSaver {
	path = append([]string{*cx.Server.UploadsDir}, path...)
	return &filerSaver{filepath.Join(path...), setName, cx.request, "", nil}
}

type filerSaver struct {
	path       string
	nameSetter func(string) (string, error)
	request    *http.Request
	key        string
	header     *multipart.FileHeader
}

func (f *filerSaver) From(key string) *filerSaver {
	f.key = key
	return f
}

func (f *filerSaver) NameBy(fn func(string) (string, error)) *filerSaver {
	f.nameSetter = fn
	return f
}

//Execute the file save process and return the result
func (f *filerSaver) Exec() (path string, status int, err error) {
	if _, err := os.Stat(f.path); err == nil {
		if err := os.MkdirAll(f.path, 0755); err == nil {
			var file multipart.File
			file, f.header, err = f.request.FormFile(f.key)
			if file != nil {
				defer file.Close()
			}
			if err == nil {
				if fname, err := f.nameSetter(f.header.Filename); err == nil {
					var fwriter *os.File
					path = filepath.Join(f.path, fname)
					fwriter, err = os.Create(path)
					if err == nil {
						defer fwriter.Close()
						_, err = io.Copy(fwriter, file)
					} else {
						status = SAVEFILE_COPY_ERROR
					}
				} else {
					status = SAVEFILE_RENAME_ERROR_BY_USER
				}
			} else {
				status = SAVEFILE_FORMFILE_ERROR
			}
		} else {
			status = SAVEFILE_CREATE_DIR_ERROR
		}
	} else {
		status = SAVEFILE_STATE_DIR_ERROR
	}
	return
}

func setName(fname string) (string, error) {
	return fname, nil
}
<commit_msg>add FullPath<commit_after>package goblet

import (
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

const (
	SAVEFILE_SUCCESS              = iota
	SAVEFILE_STATE_DIR_ERROR      = iota
	SAVEFILE_CREATE_DIR_ERROR     = iota
	SAVEFILE_FORMFILE_ERROR       = iota
	SAVEFILE_RENAME_ERROR_BY_USER = iota
	SAVEFILE_COPY_ERROR           = iota
)

func (cx *Context) SaveFileAt(path ...string) *filerSaver {
	path = append([]string{*cx.Server.UploadsDir}, path...)
	return &filerSaver{filepath.Join(path...), "", setName, cx.request, "", nil}
}

type filerSaver struct {
	path string
	//文件保存的完整路径
	FullPath   string
	nameSetter func(string) (string, error)
	request    *http.Request
	key        string
	header     *multipart.FileHeader
}

func (f *filerSaver) From(key string) *filerSaver {
	f.key = key
	return f
}

func (f *filerSaver) NameBy(fn func(string) (string, error)) *filerSaver {
	f.nameSetter = fn
	return f
}

//Execute the file save process and return the result
func (f *filerSaver) Exec() (path string, status int, err error) {
	if _, err := os.Stat(f.path); err == nil {
		if err := os.MkdirAll(f.path, 0755); err == nil {
			var file multipart.File
			file, f.header, err = f.request.FormFile(f.key)
			if file != nil {
				defer file.Close()
			}
			if err == nil {
				if fname, er := f.nameSetter(f.header.Filename); er == nil {
					var fwriter *os.File
					f.FullPath = filepath.Join(f.path, fname)
					fwriter, err = os.Create(f.FullPath)
					if err == nil {
						defer fwriter.Close()
						_, err = io.Copy(fwriter, file)
					} else {
						status = SAVEFILE_COPY_ERROR
					}
				} else {
					err = er
					status = SAVEFILE_RENAME_ERROR_BY_USER
				}
			} else {
				status = SAVEFILE_FORMFILE_ERROR
			}
		} else {
			status = SAVEFILE_CREATE_DIR_ERROR
		}
	} else {
		status = SAVEFILE_STATE_DIR_ERROR
	}
	return
}

func setName(fname string) (string, error) {
	return fname, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/mail"
	"net/textproto"
	"strings"
)

// Single email message for delivery. This struct is used both for
// unmarshalling requests and storing emails queued for delivery on disk.
type Email struct {
	From    string `json:"from"`
	To      string `json:"to"`
	Subject string `json:"subject"`
	Text    string `json:"text"`
	Html    string `json:"html"`
}

// Attempt to parse the provided JSON data and populate an Email struct.
func NewEmailFromJson(data []byte) (*Email, error) {
	var email Email
	if err := json.Unmarshal(data, &email); err != nil {
		return nil, err
	} else {
		return &email, nil
	}
}

// Attempt to extract the host from the To email address.
func (e Email) Host() (string, error) {
	if addr, err := mail.ParseAddress(e.To); err != nil {
		return "", err
	} else {
		// If ParseAddress succeeded, Address is guaranteed to contain "@"
		return strings.Split(addr.Address, "@")[1], nil
	}
}

// Add the specified content to a multipart/alternative writer.
func addPart(w *multipart.Writer, contentType string, content []byte) error {
	header := make(textproto.MIMEHeader)
	header.Add("Content-Type", contentType)
	writer, err := w.CreatePart(header)
	if err != nil {
		return err
	}
	_, err = writer.Write(content)
	return err
}

// Write the email to a Writer instance.
func (e Email) Write(w io.Writer) error {
	writer := multipart.NewWriter(w)

	// Write the headers, including the content-type and boundary
	w.Write([]byte(fmt.Sprintf("From: %s\r\n", e.From)))
	w.Write([]byte(fmt.Sprintf("To: %s\r\n", e.To)))
	w.Write([]byte(fmt.Sprintf("Subject: %s\r\n", e.Subject)))
	w.Write([]byte(fmt.Sprintf("Content-Type: multipart/alternative; boundary=\"%s\"", writer.Boundary())))
	w.Write([]byte("\r\n"))

	// Write the parts of the message
	if err := addPart(writer, "text/plain", []byte(e.Text)); err != nil {
		return err
	}
	if err := addPart(writer, "text/html", []byte(e.Html)); err != nil {
		return err
	}
	if err := writer.Close(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Removed file that was no longer used.<commit_after><|endoftext|>


<commit_before>package main

import (
	"bytes"
	"log"
	"net/smtp"
	"path/filepath"
	"text/template"

	"gopkg.in/alexcesaro/quotedprintable.v1"
)

var emailTemplates *template.Template

func administratorEmail() string {
	return "DigitalWhanganui <" + Config.AdminEmailAddress + ">"
}

func fromEmail() string {
	return "DigitalWhanganui <" + Config.FromEmailAddress + ">"
}

func sendMail(to, subject, template string, data map[string]string) {
	checkHeader(to)
	checkHeader(subject)

	var buf bytes.Buffer
	buf.WriteString("From: " + fromEmail() + "\r\n")
	buf.WriteString("To: " + to + "\r\n")
	buf.WriteString("Subject: " + subject + "\r\n")
	buf.WriteString("Content-Type: text/html; charset=UTF-8\r\n")
	buf.WriteString("Content-Transfer-Encoding: quoted-printable\r\n")
	buf.WriteString("\r\n")

	QPWriter := quotedprintable.NewEncoder(&buf)

	err := emailTemplates.ExecuteTemplate(QPWriter, template, data)
	if err != nil {
		panic(err)
	}
	msg := buf.Bytes()

	auth := smtp.CRAMMD5Auth(Config.SMTPUser, Config.SMTPPassword)
	err = smtp.SendMail(Config.SMTPServer, auth, Config.FromEmailAddress, []string{to}, msg)
	if err != nil {
		panic(err)
	}

}

func (l *Listing) FullAdminEmail() string {
	return l.AdminFirstName + " " + l.AdminLastName + " <" + l.AdminEmail + ">"
}

func checkHeader(header string) {
	// TODO
}

func emailErrorMsg(msg string, logger *log.Logger) {
	defer func() {
		if err := recover(); err != nil {
			logger.Println("Panic when emailing error message:", err)
		}
	}()

	sendMail("richardc+digitalwhanganui@richardc.net", "Digital Whanganui Error", "error.tmpl", map[string]string{"error": msg})
}

func initEmail() {
	pattern := filepath.Join(Config.EmailTemplateDir, "*.tmpl")
	emailTemplates = template.Must(template.ParseGlob(pattern))

}
<commit_msg>Add Email header injection check<commit_after>package main

import (
	"bytes"
	"log"
	"net/smtp"
	"path/filepath"
	"strings"
	"text/template"

	"gopkg.in/alexcesaro/quotedprintable.v1"
)

var emailTemplates *template.Template

func administratorEmail() string {
	return "DigitalWhanganui <" + Config.AdminEmailAddress + ">"
}

func fromEmail() string {
	return "DigitalWhanganui <" + Config.FromEmailAddress + ">"
}

func sendMail(to, subject, template string, data map[string]string) {
	checkHeaderText(to)
	checkHeaderText(subject)

	var buf bytes.Buffer
	buf.WriteString("From: " + fromEmail() + "\r\n")
	buf.WriteString("To: " + to + "\r\n")
	buf.WriteString("Subject: " + subject + "\r\n")
	buf.WriteString("Content-Type: text/html; charset=UTF-8\r\n")
	buf.WriteString("Content-Transfer-Encoding: quoted-printable\r\n")
	buf.WriteString("\r\n")

	QPWriter := quotedprintable.NewEncoder(&buf)

	err := emailTemplates.ExecuteTemplate(QPWriter, template, data)
	if err != nil {
		panic(err)
	}
	msg := buf.Bytes()

	auth := smtp.CRAMMD5Auth(Config.SMTPUser, Config.SMTPPassword)
	err = smtp.SendMail(Config.SMTPServer, auth, Config.FromEmailAddress, []string{to}, msg)
	if err != nil {
		panic(err)
	}

}

func (l *Listing) FullAdminEmail() string {
	return l.AdminFirstName + " " + l.AdminLastName + " <" + l.AdminEmail + ">"
}

// Ensure headerText is not being used to inject additional email headers
func checkHeaderText(headerText string) {
	if strings.ContainsAny(headerText, "\n\r") {
		panic("Possible Email header injection: " + headerText)
	}
}

func emailErrorMsg(msg string, logger *log.Logger) {
	defer func() {
		if err := recover(); err != nil {
			logger.Println("Panic when emailing error message:", err)
		}
	}()

	sendMail("richardc+digitalwhanganui@richardc.net", "Digital Whanganui Error", "error.tmpl", map[string]string{"error": msg})
}

func initEmail() {
	pattern := filepath.Join(Config.EmailTemplateDir, "*.tmpl")
	emailTemplates = template.Must(template.ParseGlob(pattern))

}
<|endoftext|>


<commit_before>//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config

package vagrant

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/hashicorp/hcl/v2/hcldec"
	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/common/bootcommand"
	"github.com/hashicorp/packer/helper/communicator"
	"github.com/hashicorp/packer/helper/config"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

// Builder implements packer.Builder and builds the actual VirtualBox
// images.
type Builder struct {
	config Config
	runner multistep.Runner
}

type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	common.HTTPConfig      `mapstructure:",squash"`
	common.ISOConfig       `mapstructure:",squash"`
	common.FloppyConfig    `mapstructure:",squash"`
	bootcommand.BootConfig `mapstructure:",squash"`

	Comm communicator.Config `mapstructure:",squash"`
	// The directory to create that will contain your output box. We always
	// create this directory and run from inside of it to prevent Vagrant init
	// collisions. If unset, it will be set to packer- plus your buildname.
	OutputDir string `mapstructure:"output_dir" required:"false"`
	// URL of the vagrant box to use, or the name of the vagrant box.
	// hashicorp/precise64, ./mylocalbox.box and https://example.com/my-box.box
	// are all valid source boxes. If your source is a .box file, whether
	// locally or from a URL like the latter example above, you will also need
	// to provide a box_name. This option is required, unless you set
	// global_id. You may only set one or the other, not both.
	SourceBox string `mapstructure:"source_path" required:"true"`
	// the global id of a Vagrant box already added to Vagrant on your system.
	// You can find the global id of your Vagrant boxes using the command
	// vagrant global-status; your global_id will be a 7-digit number and
	// letter comination that you'll find in the leftmost column of the
	// global-status output.  If you choose to use global_id instead of
	// source_box, Packer will skip the Vagrant initialize and add steps, and
	// simply launch the box directly using the global id.
	GlobalID string `mapstructure:"global_id" required:"true"`
	// The checksum for the .box file. The type of the checksum is specified
	// with checksum_type, documented below.
	Checksum string `mapstructure:"checksum" required:"false"`
	// The type of the checksum specified in checksum. Valid values are none,
	// md5, sha1, sha256, or sha512. Although the checksum will not be verified
	// when checksum_type is set to "none", this is not recommended since OVA
	// files can be very large and corruption does happen from time to time.
	ChecksumType string `mapstructure:"checksum_type" required:"false"`
	// if your source_box is a boxfile that we need to add to Vagrant, this is
	// the name to give it. If left blank, will default to "packer_" plus your
	// buildname.
	BoxName string `mapstructure:"box_name" required:"false"`
	// If true, Vagrant will automatically insert a keypair to use for SSH,
	// replacing Vagrant's default insecure key inside the machine if detected.
	// By default, Packer sets this to false.
	InsertKey bool `mapstructure:"insert_key" required:"false"`
	// The vagrant provider.
	// This parameter is required when source_path have more than one provider,
	// or when using vagrant-cloud post-processor. Defaults to unset.
	Provider string `mapstructure:"provider" required:"false"`

	Communicator string `mapstructure:"communicator"`

	// Options for the "vagrant init" command

	// What vagrantfile to use
	VagrantfileTpl string `mapstructure:"vagrantfile_template"`
	// Whether to halt, suspend, or destroy the box when the build has
	// completed. Defaults to "halt"
	TeardownMethod string `mapstructure:"teardown_method" required:"false"`
	// What box version to use when initializing Vagrant.
	BoxVersion string `mapstructure:"box_version" required:"false"`
	// a path to a golang template for a vagrantfile. Our default template can
	// be found here. The template variables available to you are
	// `{{ .BoxName }}`, `{{ .SyncedFolder }}`, and `{{.InsertKey}}`, which
	// correspond to the Packer options box_name, synced_folder, and insert_key.
	Template string `mapstructure:"template" required:"false"`

	SyncedFolder string `mapstructure:"synced_folder"`
	// Don't call "vagrant add" to add the box to your local environment; this
	// is necessary if you want to launch a box that is already added to your
	// vagrant environment.
	SkipAdd bool `mapstructure:"skip_add" required:"false"`
	// Equivalent to setting the
	// --cacert
	// option in vagrant add; defaults to unset.
	AddCACert string `mapstructure:"add_cacert" required:"false"`
	// Equivalent to setting the
	// --capath option
	// in vagrant add; defaults to unset.
	AddCAPath string `mapstructure:"add_capath" required:"false"`
	// Equivalent to setting the
	// --cert option in
	// vagrant add; defaults to unset.
	AddCert string `mapstructure:"add_cert" required:"false"`
	// Equivalent to setting the
	// --clean flag in
	// vagrant add; defaults to unset.
	AddClean bool `mapstructure:"add_clean" required:"false"`
	// Equivalent to setting the
	// --force flag in
	// vagrant add; defaults to unset.
	AddForce bool `mapstructure:"add_force" required:"false"`
	// Equivalent to setting the
	// --insecure flag in
	// vagrant add; defaults to unset.
	AddInsecure bool `mapstructure:"add_insecure" required:"false"`
	// if true, Packer will not call vagrant package to
	// package your base box into its own standalone .box file.
	SkipPackage       bool   `mapstructure:"skip_package" required:"false"`
	OutputVagrantfile string `mapstructure:"output_vagrantfile"`
	// Equivalent to setting the
	// [`--include`](https://www.vagrantup.com/docs/cli/package.html#include-x-y-z) option
	// in `vagrant package`; defaults to unset
	PackageInclude []string `mapstructure:"package_include"`

	ctx interpolate.Context
}

func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }

func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
	err := config.Decode(&b.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &b.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{
				"boot_command",
			},
		},
	}, raws...)
	if err != nil {
		return nil, nil, err
	}

	// Accumulate any errors and warnings
	var errs *packer.MultiError
	warnings := make([]string, 0)

	if b.config.OutputDir == "" {
		b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName)
	}

	if b.config.Comm.SSHTimeout == 0 {
		b.config.Comm.SSHTimeout = 10 * time.Minute
	}

	if b.config.Comm.Type != "ssh" {
		errs = packer.MultiErrorAppend(errs,
			fmt.Errorf(`The Vagrant builder currently only supports the ssh communicator"`))
	}
	// The box isn't a namespace like you'd pull from vagrant cloud
	if b.config.BoxName == "" {
		b.config.BoxName = fmt.Sprintf("packer_%s", b.config.PackerBuildName)
	}

	if b.config.SourceBox == "" {
		if b.config.GlobalID == "" {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required unless you have set global_id"))
		}
	} else {
		if b.config.GlobalID != "" {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("You may either set global_id or source_path but not both"))
		}
		if strings.HasSuffix(b.config.SourceBox, ".box") {
			if _, err := os.Stat(b.config.SourceBox); err != nil {
				packer.MultiErrorAppend(errs,
					fmt.Errorf("Source box '%s' needs to exist at time of config validation! %v", b.config.SourceBox, err))
			}
		}
	}

	if b.config.OutputVagrantfile != "" {
		b.config.OutputVagrantfile, err = filepath.Abs(b.config.OutputVagrantfile)
		if err != nil {
			packer.MultiErrorAppend(errs,
				fmt.Errorf("unable to determine absolute path for output vagrantfile: %s", err))
		}
	}

	if len(b.config.PackageInclude) > 0 {
		include := []string{}
		for i, inclFile := range b.config.PackageInclude {
			b.config.PackageInclude, err = filepath.Abs(b.config.PackageInclude)
			if err != nil {
				packer.MultiErrorAppend(errs,
					fmt.Errorf("unable to determine absolute path for file to be included: %s", inclFile))
			}
			include = append(include, inclFile)
		}
		b.config.PackageInclude = include
	}

	if b.config.TeardownMethod == "" {
		// If we're using a box that's already opened on the system, don't
		// automatically destroy it. If we open the box ourselves, then go ahead
		// and kill it by default.
		if b.config.GlobalID != "" {
			b.config.TeardownMethod = "halt"
		} else {
			b.config.TeardownMethod = "destroy"
		}
	} else {
		matches := false
		for _, name := range []string{"halt", "suspend", "destroy"} {
			if strings.ToLower(b.config.TeardownMethod) == name {
				matches = true
			}
		}
		if !matches {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf(`TeardownMethod must be "halt", "suspend", or "destroy"`))
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return nil, warnings, errs
	}

	return nil, warnings, nil
}

// Run executes a Packer build and returns a packer.Artifact representing
// a VirtualBox appliance.
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
	// Create the driver that we'll use to communicate with VirtualBox
	VagrantCWD, err := filepath.Abs(b.config.OutputDir)
	if err != nil {
		return nil, err
	}
	driver, err := NewDriver(VagrantCWD)
	if err != nil {
		return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err)
	}

	// Set up the state.
	state := new(multistep.BasicStateBag)
	state.Put("config", &b.config)
	state.Put("debug", b.config.PackerDebug)
	state.Put("driver", driver)
	state.Put("hook", hook)
	state.Put("ui", ui)

	// Build the steps.
	steps := []multistep.Step{}
	// Download if source box isn't from vagrant cloud.
	if strings.HasSuffix(b.config.SourceBox, ".box") {
		steps = append(steps, &common.StepDownload{
			Checksum:     b.config.Checksum,
			ChecksumType: b.config.ChecksumType,
			Description:  "Box",
			Extension:    "box",
			ResultKey:    "box_path",
			Url:          []string{b.config.SourceBox},
		})
	}
	steps = append(steps,
		&common.StepOutputDir{
			Force: b.config.PackerForce,
			Path:  b.config.OutputDir,
		},
		&StepCreateVagrantfile{
			Template:     b.config.Template,
			SyncedFolder: b.config.SyncedFolder,
			SourceBox:    b.config.SourceBox,
			BoxName:      b.config.BoxName,
			OutputDir:    b.config.OutputDir,
			GlobalID:     b.config.GlobalID,
			InsertKey:    b.config.InsertKey,
		},
		&StepAddBox{
			BoxVersion:   b.config.BoxVersion,
			CACert:       b.config.AddCACert,
			CAPath:       b.config.AddCAPath,
			DownloadCert: b.config.AddCert,
			Clean:        b.config.AddClean,
			Force:        b.config.AddForce,
			Insecure:     b.config.AddInsecure,
			Provider:     b.config.Provider,
			SourceBox:    b.config.SourceBox,
			BoxName:      b.config.BoxName,
			GlobalID:     b.config.GlobalID,
			SkipAdd:      b.config.SkipAdd,
		},
		&StepUp{
			TeardownMethod: b.config.TeardownMethod,
			Provider:       b.config.Provider,
			GlobalID:       b.config.GlobalID,
		},
		&StepSSHConfig{
			b.config.GlobalID,
		},
		&communicator.StepConnect{
			Config:    &b.config.Comm,
			Host:      CommHost(),
			SSHConfig: b.config.Comm.SSHConfigFunc(),
		},
		new(common.StepProvision),
		&StepPackage{
			SkipPackage: b.config.SkipPackage,
			Include:     b.config.PackageInclude,
			Vagrantfile: b.config.OutputVagrantfile,
			GlobalID:    b.config.GlobalID,
		})

	// Run the steps.
	b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
	b.runner.Run(ctx, state)

	// Report any errors.
	if rawErr, ok := state.GetOk("error"); ok {
		return nil, rawErr.(error)
	}

	// If we were interrupted or cancelled, then just exit.
	if _, ok := state.GetOk(multistep.StateCancelled); ok {
		return nil, errors.New("Build was cancelled.")
	}

	if _, ok := state.GetOk(multistep.StateHalted); ok {
		return nil, errors.New("Build was halted.")
	}

	generatedData := map[string]interface{}{"generated_data": state.Get("generated_data")}
	return NewArtifact(b.config.Provider, b.config.OutputDir, generatedData), nil
}

// Cancel.
<commit_msg>fix tests<commit_after>//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config

package vagrant

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/hashicorp/hcl/v2/hcldec"
	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/common/bootcommand"
	"github.com/hashicorp/packer/helper/communicator"
	"github.com/hashicorp/packer/helper/config"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

// Builder implements packer.Builder and builds the actual VirtualBox
// images.
type Builder struct {
	config Config
	runner multistep.Runner
}

type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	common.HTTPConfig      `mapstructure:",squash"`
	common.ISOConfig       `mapstructure:",squash"`
	common.FloppyConfig    `mapstructure:",squash"`
	bootcommand.BootConfig `mapstructure:",squash"`

	Comm communicator.Config `mapstructure:",squash"`
	// The directory to create that will contain your output box. We always
	// create this directory and run from inside of it to prevent Vagrant init
	// collisions. If unset, it will be set to packer- plus your buildname.
	OutputDir string `mapstructure:"output_dir" required:"false"`
	// URL of the vagrant box to use, or the name of the vagrant box.
	// hashicorp/precise64, ./mylocalbox.box and https://example.com/my-box.box
	// are all valid source boxes. If your source is a .box file, whether
	// locally or from a URL like the latter example above, you will also need
	// to provide a box_name. This option is required, unless you set
	// global_id. You may only set one or the other, not both.
	SourceBox string `mapstructure:"source_path" required:"true"`
	// the global id of a Vagrant box already added to Vagrant on your system.
	// You can find the global id of your Vagrant boxes using the command
	// vagrant global-status; your global_id will be a 7-digit number and
	// letter comination that you'll find in the leftmost column of the
	// global-status output.  If you choose to use global_id instead of
	// source_box, Packer will skip the Vagrant initialize and add steps, and
	// simply launch the box directly using the global id.
	GlobalID string `mapstructure:"global_id" required:"true"`
	// The checksum for the .box file. The type of the checksum is specified
	// with checksum_type, documented below.
	Checksum string `mapstructure:"checksum" required:"false"`
	// The type of the checksum specified in checksum. Valid values are none,
	// md5, sha1, sha256, or sha512. Although the checksum will not be verified
	// when checksum_type is set to "none", this is not recommended since OVA
	// files can be very large and corruption does happen from time to time.
	ChecksumType string `mapstructure:"checksum_type" required:"false"`
	// if your source_box is a boxfile that we need to add to Vagrant, this is
	// the name to give it. If left blank, will default to "packer_" plus your
	// buildname.
	BoxName string `mapstructure:"box_name" required:"false"`
	// If true, Vagrant will automatically insert a keypair to use for SSH,
	// replacing Vagrant's default insecure key inside the machine if detected.
	// By default, Packer sets this to false.
	InsertKey bool `mapstructure:"insert_key" required:"false"`
	// The vagrant provider.
	// This parameter is required when source_path have more than one provider,
	// or when using vagrant-cloud post-processor. Defaults to unset.
	Provider string `mapstructure:"provider" required:"false"`

	Communicator string `mapstructure:"communicator"`

	// Options for the "vagrant init" command

	// What vagrantfile to use
	VagrantfileTpl string `mapstructure:"vagrantfile_template"`
	// Whether to halt, suspend, or destroy the box when the build has
	// completed. Defaults to "halt"
	TeardownMethod string `mapstructure:"teardown_method" required:"false"`
	// What box version to use when initializing Vagrant.
	BoxVersion string `mapstructure:"box_version" required:"false"`
	// a path to a golang template for a vagrantfile. Our default template can
	// be found here. The template variables available to you are
	// `{{ .BoxName }}`, `{{ .SyncedFolder }}`, and `{{.InsertKey}}`, which
	// correspond to the Packer options box_name, synced_folder, and insert_key.
	Template string `mapstructure:"template" required:"false"`

	SyncedFolder string `mapstructure:"synced_folder"`
	// Don't call "vagrant add" to add the box to your local environment; this
	// is necessary if you want to launch a box that is already added to your
	// vagrant environment.
	SkipAdd bool `mapstructure:"skip_add" required:"false"`
	// Equivalent to setting the
	// --cacert
	// option in vagrant add; defaults to unset.
	AddCACert string `mapstructure:"add_cacert" required:"false"`
	// Equivalent to setting the
	// --capath option
	// in vagrant add; defaults to unset.
	AddCAPath string `mapstructure:"add_capath" required:"false"`
	// Equivalent to setting the
	// --cert option in
	// vagrant add; defaults to unset.
	AddCert string `mapstructure:"add_cert" required:"false"`
	// Equivalent to setting the
	// --clean flag in
	// vagrant add; defaults to unset.
	AddClean bool `mapstructure:"add_clean" required:"false"`
	// Equivalent to setting the
	// --force flag in
	// vagrant add; defaults to unset.
	AddForce bool `mapstructure:"add_force" required:"false"`
	// Equivalent to setting the
	// --insecure flag in
	// vagrant add; defaults to unset.
	AddInsecure bool `mapstructure:"add_insecure" required:"false"`
	// if true, Packer will not call vagrant package to
	// package your base box into its own standalone .box file.
	SkipPackage       bool   `mapstructure:"skip_package" required:"false"`
	OutputVagrantfile string `mapstructure:"output_vagrantfile"`
	// Equivalent to setting the
	// [`--include`](https://www.vagrantup.com/docs/cli/package.html#include-x-y-z) option
	// in `vagrant package`; defaults to unset
	PackageInclude []string `mapstructure:"package_include"`

	ctx interpolate.Context
}

func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }

func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
	err := config.Decode(&b.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &b.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{
				"boot_command",
			},
		},
	}, raws...)
	if err != nil {
		return nil, nil, err
	}

	// Accumulate any errors and warnings
	var errs *packer.MultiError
	warnings := make([]string, 0)

	if b.config.OutputDir == "" {
		b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName)
	}

	if b.config.Comm.SSHTimeout == 0 {
		b.config.Comm.SSHTimeout = 10 * time.Minute
	}

	if b.config.Comm.Type != "ssh" {
		errs = packer.MultiErrorAppend(errs,
			fmt.Errorf(`The Vagrant builder currently only supports the ssh communicator"`))
	}
	// The box isn't a namespace like you'd pull from vagrant cloud
	if b.config.BoxName == "" {
		b.config.BoxName = fmt.Sprintf("packer_%s", b.config.PackerBuildName)
	}

	if b.config.SourceBox == "" {
		if b.config.GlobalID == "" {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required unless you have set global_id"))
		}
	} else {
		if b.config.GlobalID != "" {
			errs = packer.MultiErrorAppend(errs, fmt.Errorf("You may either set global_id or source_path but not both"))
		}
		if strings.HasSuffix(b.config.SourceBox, ".box") {
			if _, err := os.Stat(b.config.SourceBox); err != nil {
				packer.MultiErrorAppend(errs,
					fmt.Errorf("Source box '%s' needs to exist at time of config validation! %v", b.config.SourceBox, err))
			}
		}
	}

	if b.config.OutputVagrantfile != "" {
		b.config.OutputVagrantfile, err = filepath.Abs(b.config.OutputVagrantfile)
		if err != nil {
			packer.MultiErrorAppend(errs,
				fmt.Errorf("unable to determine absolute path for output vagrantfile: %s", err))
		}
	}

	if len(b.config.PackageInclude) > 0 {
		include := []string{}
		for _, rawFile := range b.config.PackageInclude {
			inclFile, err := filepath.Abs(rawFile)
			if err != nil {
				packer.MultiErrorAppend(errs,
					fmt.Errorf("unable to determine absolute path for file to be included: %s", rawFile))
			}
			include = append(include, inclFile)
		}
		b.config.PackageInclude = include
	}

	if b.config.TeardownMethod == "" {
		// If we're using a box that's already opened on the system, don't
		// automatically destroy it. If we open the box ourselves, then go ahead
		// and kill it by default.
		if b.config.GlobalID != "" {
			b.config.TeardownMethod = "halt"
		} else {
			b.config.TeardownMethod = "destroy"
		}
	} else {
		matches := false
		for _, name := range []string{"halt", "suspend", "destroy"} {
			if strings.ToLower(b.config.TeardownMethod) == name {
				matches = true
			}
		}
		if !matches {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf(`TeardownMethod must be "halt", "suspend", or "destroy"`))
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return nil, warnings, errs
	}

	return nil, warnings, nil
}

// Run executes a Packer build and returns a packer.Artifact representing
// a VirtualBox appliance.
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
	// Create the driver that we'll use to communicate with VirtualBox
	VagrantCWD, err := filepath.Abs(b.config.OutputDir)
	if err != nil {
		return nil, err
	}
	driver, err := NewDriver(VagrantCWD)
	if err != nil {
		return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err)
	}

	// Set up the state.
	state := new(multistep.BasicStateBag)
	state.Put("config", &b.config)
	state.Put("debug", b.config.PackerDebug)
	state.Put("driver", driver)
	state.Put("hook", hook)
	state.Put("ui", ui)

	// Build the steps.
	steps := []multistep.Step{}
	// Download if source box isn't from vagrant cloud.
	if strings.HasSuffix(b.config.SourceBox, ".box") {
		steps = append(steps, &common.StepDownload{
			Checksum:     b.config.Checksum,
			ChecksumType: b.config.ChecksumType,
			Description:  "Box",
			Extension:    "box",
			ResultKey:    "box_path",
			Url:          []string{b.config.SourceBox},
		})
	}
	steps = append(steps,
		&common.StepOutputDir{
			Force: b.config.PackerForce,
			Path:  b.config.OutputDir,
		},
		&StepCreateVagrantfile{
			Template:     b.config.Template,
			SyncedFolder: b.config.SyncedFolder,
			SourceBox:    b.config.SourceBox,
			BoxName:      b.config.BoxName,
			OutputDir:    b.config.OutputDir,
			GlobalID:     b.config.GlobalID,
			InsertKey:    b.config.InsertKey,
		},
		&StepAddBox{
			BoxVersion:   b.config.BoxVersion,
			CACert:       b.config.AddCACert,
			CAPath:       b.config.AddCAPath,
			DownloadCert: b.config.AddCert,
			Clean:        b.config.AddClean,
			Force:        b.config.AddForce,
			Insecure:     b.config.AddInsecure,
			Provider:     b.config.Provider,
			SourceBox:    b.config.SourceBox,
			BoxName:      b.config.BoxName,
			GlobalID:     b.config.GlobalID,
			SkipAdd:      b.config.SkipAdd,
		},
		&StepUp{
			TeardownMethod: b.config.TeardownMethod,
			Provider:       b.config.Provider,
			GlobalID:       b.config.GlobalID,
		},
		&StepSSHConfig{
			b.config.GlobalID,
		},
		&communicator.StepConnect{
			Config:    &b.config.Comm,
			Host:      CommHost(),
			SSHConfig: b.config.Comm.SSHConfigFunc(),
		},
		new(common.StepProvision),
		&StepPackage{
			SkipPackage: b.config.SkipPackage,
			Include:     b.config.PackageInclude,
			Vagrantfile: b.config.OutputVagrantfile,
			GlobalID:    b.config.GlobalID,
		})

	// Run the steps.
	b.runner = common.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
	b.runner.Run(ctx, state)

	// Report any errors.
	if rawErr, ok := state.GetOk("error"); ok {
		return nil, rawErr.(error)
	}

	// If we were interrupted or cancelled, then just exit.
	if _, ok := state.GetOk(multistep.StateCancelled); ok {
		return nil, errors.New("Build was cancelled.")
	}

	if _, ok := state.GetOk(multistep.StateHalted); ok {
		return nil, errors.New("Build was halted.")
	}

	generatedData := map[string]interface{}{"generated_data": state.Get("generated_data")}
	return NewArtifact(b.config.Provider, b.config.OutputDir, generatedData), nil
}

// Cancel.
<|endoftext|>


<commit_before>package dispatcher

import (
	"encoding/json"
	"fmt"

	boshlog "github.com/cloudfoundry/bosh-utils/logger"

	bslcaction "github.com/maximilien/bosh-softlayer-cpi/action"
	bslcapi "github.com/maximilien/bosh-softlayer-cpi/api"
)

const (
	jsonLogTag = "json"

	jsonCloudErrorType          = "Bosh::Clouds::CloudError"
	jsonCpiErrorType            = "Bosh::Clouds::CpiError"
	jsonNotImplementedErrorType = "Bosh::Clouds::NotImplemented"
)

type Request struct {
	Method    string        `json:"method"`
	Arguments []interface{} `json:"arguments"`

	// context key is ignored
}

type Response struct {
	Result interface{}    `json:"result"`
	Error  *ResponseError `json:"error"`

	Log string `json:"log"`
}

type ResponseError struct {
	Type    string `json:"type"`
	Message string `json:"message"`

	CanRetry bool `json:"ok_to_retry"`
}

type JSON struct {
	actionFactory bslcaction.Factory
	caller        Caller
	logger        boshlog.Logger
}

func NewJSON(
	actionFactory bslcaction.Factory,
	caller Caller,
	logger boshlog.Logger,
) JSON {
	return JSON{
		actionFactory: actionFactory,
		caller:        caller,
		logger:        logger,
	}
}

func (c JSON) Dispatch(reqBytes []byte) []byte {
	var req Request

	c.logger.DebugWithDetails(jsonLogTag, "Request bytes", string(reqBytes))

	err := json.Unmarshal(reqBytes, &req)
	if err != nil {
		return c.buildCpiError("Must provide valid JSON payload")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized request", req)

	if req.Method == "" {
		return c.buildCpiError("Must provide method key")
	}

	if req.Arguments == nil {
		return c.buildCpiError("Must provide arguments key")
	}

	action, err := c.actionFactory.Create(req.Method)
	if err != nil {
		return c.buildNotImplementedError()
	}

	result, err := c.caller.Call(action, req.Arguments)
	if err != nil {
		return c.buildCloudError(err)
	}

	resp := Response{
		Result: result,
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized response", resp)

	respBytes, err := json.Marshal(resp)
	if err != nil {
		return c.buildCpiError("Failed to serialize result")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Response bytes", string(respBytes))

	return respBytes
}

func (c JSON) buildCloudError(err error) []byte {
	respErr := Response{
		Error: &ResponseError{},
	}

	if typedErr, ok := err.(bslcapi.CloudError); ok {
		respErr.Error.Type = typedErr.Type()
	} else {
		respErr.Error.Type = jsonCloudErrorType
	}

	respErr.Error.Message = err.Error()

	if typedErr, ok := err.(bslcapi.RetryableError); ok {
		respErr.Error.CanRetry = typedErr.CanRetry()
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CloudError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildCpiError(message string) []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonCpiErrorType,
			Message: message,
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CpiError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildNotImplementedError() []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonNotImplementedErrorType,
			Message: "Must call implemented method",
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "NotImplementedError response bytes", string(respErrBytes))

	return respErrBytes
}
<commit_msg>removed extra import<commit_after>package dispatcher

import (
	"encoding/json"

	boshlog "github.com/cloudfoundry/bosh-utils/logger"

	bslcaction "github.com/maximilien/bosh-softlayer-cpi/action"
	bslcapi "github.com/maximilien/bosh-softlayer-cpi/api"
)

const (
	jsonLogTag = "json"

	jsonCloudErrorType          = "Bosh::Clouds::CloudError"
	jsonCpiErrorType            = "Bosh::Clouds::CpiError"
	jsonNotImplementedErrorType = "Bosh::Clouds::NotImplemented"
)

type Request struct {
	Method    string        `json:"method"`
	Arguments []interface{} `json:"arguments"`

	// context key is ignored
}

type Response struct {
	Result interface{}    `json:"result"`
	Error  *ResponseError `json:"error"`

	Log string `json:"log"`
}

type ResponseError struct {
	Type    string `json:"type"`
	Message string `json:"message"`

	CanRetry bool `json:"ok_to_retry"`
}

type JSON struct {
	actionFactory bslcaction.Factory
	caller        Caller
	logger        boshlog.Logger
}

func NewJSON(
	actionFactory bslcaction.Factory,
	caller Caller,
	logger boshlog.Logger,
) JSON {
	return JSON{
		actionFactory: actionFactory,
		caller:        caller,
		logger:        logger,
	}
}

func (c JSON) Dispatch(reqBytes []byte) []byte {
	var req Request

	c.logger.DebugWithDetails(jsonLogTag, "Request bytes", string(reqBytes))

	err := json.Unmarshal(reqBytes, &req)
	if err != nil {
		return c.buildCpiError("Must provide valid JSON payload")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized request", req)

	if req.Method == "" {
		return c.buildCpiError("Must provide method key")
	}

	if req.Arguments == nil {
		return c.buildCpiError("Must provide arguments key")
	}

	action, err := c.actionFactory.Create(req.Method)
	if err != nil {
		return c.buildNotImplementedError()
	}

	result, err := c.caller.Call(action, req.Arguments)
	if err != nil {
		return c.buildCloudError(err)
	}

	resp := Response{
		Result: result,
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized response", resp)

	respBytes, err := json.Marshal(resp)
	if err != nil {
		return c.buildCpiError("Failed to serialize result")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Response bytes", string(respBytes))

	return respBytes
}

func (c JSON) buildCloudError(err error) []byte {
	respErr := Response{
		Error: &ResponseError{},
	}

	if typedErr, ok := err.(bslcapi.CloudError); ok {
		respErr.Error.Type = typedErr.Type()
	} else {
		respErr.Error.Type = jsonCloudErrorType
	}

	respErr.Error.Message = err.Error()

	if typedErr, ok := err.(bslcapi.RetryableError); ok {
		respErr.Error.CanRetry = typedErr.CanRetry()
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CloudError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildCpiError(message string) []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonCpiErrorType,
			Message: message,
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CpiError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildNotImplementedError() []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonNotImplementedErrorType,
			Message: "Must call implemented method",
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "NotImplementedError response bytes", string(respErrBytes))

	return respErrBytes
}
<|endoftext|>


<commit_before>package handler

import (
	"net/http"

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/jinzhu/gorm"
	"github.com/mingrammer/meetup-api/api/model"
)

func GetAllComments(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	comments := []model.Comment{}
	db.Model(&event).Related(&comments)
	w.WriteJson(&comments)
}

func CreateComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	userToken := r.Env["REMOTE_USER"]
	tokenString := userToken.(string)
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	comment := model.Comment{}
	if err := r.DecodeJsonPayload(&comment); err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	user := GetUserOr404(db, tokenString)
	comment.WriterID = user.ID
	comment.EventID = event.ID
	if err := db.Save(&comment).Error; err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteJson(&comment)
}

func GetComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	id := r.PathParam("cid")
	comment := GetCommentOr404(db, id)
	if comment == nil {
		rest.NotFound(w, r)
		return
	}
	w.WriteJson(&event)
}

func UpdateComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	id := r.PathParam("cid")
	comment := GetCommentOr404(db, id)
	if comment == nil {
		rest.NotFound(w, r)
		return
	}
	if err := r.DecodeJsonPayload(&comment); err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err := db.Save(&comment).Error; err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteJson(&comment)
}

func DeleteComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	id := r.PathParam("cid")
	comment := GetCommentOr404(db, id)
	if comment == nil {
		rest.NotFound(w, r)
		return
	}
	if err := db.Delete(&comment).Error; err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
}

// GetCommentOr404 gets a comment instance if exists, or nil otherwise
func GetCommentOr404(db *gorm.DB, id string) *model.Comment {
	comment := model.Comment{}
	if err := db.First(&comment, id).Error; err != nil {
		return nil
	}
	return &comment
}
<commit_msg>Fix a wrong saving<commit_after>package handler

import (
	"net/http"

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/jinzhu/gorm"
	"github.com/mingrammer/meetup-api/api/model"
)

func GetAllComments(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	comments := []model.Comment{}
	db.Model(&event).Related(&comments)
	w.WriteJson(&comments)
}

func CreateComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	userToken := r.Env["REMOTE_USER"]
	tokenString := userToken.(string)
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	comment := model.Comment{}
	if err := r.DecodeJsonPayload(&comment); err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	user := GetUserOr404(db, tokenString)
	comment.WriterID = user.ID
	comment.EventID = event.ID
	if err := db.Save(&comment).Error; err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteJson(&comment)
}

func GetComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	id := r.PathParam("cid")
	comment := GetCommentOr404(db, id)
	if comment == nil {
		rest.NotFound(w, r)
		return
	}
	w.WriteJson(&comment)
}

func UpdateComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	id := r.PathParam("cid")
	comment := GetCommentOr404(db, id)
	if comment == nil {
		rest.NotFound(w, r)
		return
	}
	if err := r.DecodeJsonPayload(&comment); err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err := db.Save(&comment).Error; err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteJson(&comment)
}

func DeleteComment(db *gorm.DB, w rest.ResponseWriter, r *rest.Request) {
	eventId := r.PathParam("eid")
	event := GetEventOr404(db, eventId)
	if event == nil {
		rest.NotFound(w, r)
		return
	}
	id := r.PathParam("cid")
	comment := GetCommentOr404(db, id)
	if comment == nil {
		rest.NotFound(w, r)
		return
	}
	if err := db.Delete(&comment).Error; err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
}

// GetCommentOr404 gets a comment instance if exists, or nil otherwise
func GetCommentOr404(db *gorm.DB, id string) *model.Comment {
	comment := model.Comment{}
	if err := db.First(&comment, id).Error; err != nil {
		return nil
	}
	return &comment
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"fmt"
	elastic "github.com/Rakanixu/elasticsearch/srv/proto/elasticsearch"
	"github.com/Rakanixu/micro/api/proto"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/errors"
	"golang.org/x/net/context"
	"net/http"
)

// Elasticsearch struct
type Elasticsearch struct{}

// Create API handler
func (es *Elasticsearch) Create(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var data []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	// Marshal unknown JSON (data)
	data, err = json.Marshal(input["data"])

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Create",
		&elastic.CreateRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Id:    fmt.Sprintf("%v", input["id"]),
			Data:  string(data),
		},
	)
	srvRsp := &elastic.CreateResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}

// Read API handler
func (es *Elasticsearch) Read(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var readRequest *elastic.ReadRequest

	if err = json.Unmarshal([]byte(req.Body), &readRequest); err != nil {
		return errors.InternalServerError("go.micro.api.elasticsearch", err.Error())
	}

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Read",
		readRequest,
	)
	srvRsp := &elastic.ReadResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}

// Update API handler
func (es *Elasticsearch) Update(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var data []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	// Marshal unknown JSON (data)
	data, err = json.Marshal(input["data"])

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Update",
		&elastic.UpdateRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Id:    fmt.Sprintf("%v", input["id"]),
			Data:  string(data),
		},
	)
	srvRsp := &elastic.UpdateResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}

// Delete API handler
func (es *Elasticsearch) Delete(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var deleteRequest *elastic.DeleteRequest

	if err = json.Unmarshal([]byte(req.Body), &deleteRequest); err != nil {
		return errors.InternalServerError("go.micro.api.elasticsearch", err.Error())
	}

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Delete",
		deleteRequest,
	)
	srvRsp := &elastic.DeleteResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}

// Search API handler
func (es *Elasticsearch) Search(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Search",
		&elastic.SearchRequest{
			Index:  fmt.Sprintf("%v", input["index"]),
			Type:   fmt.Sprintf("%v", input["type"]),
			Limit:  int64((input["limit"]).(float64)),
			Offset: int64((input["offset"]).(float64)),
			Query:  fmt.Sprintf("%v", input["query"]),
		},
	)
	srvRsp := &elastic.SearchResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}

// Query API handler
func (es *Elasticsearch) Query(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var query []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	query, err = json.Marshal(input["query"])

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Query",
		&elastic.QueryRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Query: string(query),
		},
	)
	srvRsp := &elastic.SearchResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}
<commit_msg>Fix import to original source<commit_after>package handler

import (
	"encoding/json"
	"fmt"
	elastic "github.com/Rakanixu/elasticsearch/srv/proto/elasticsearch"
	"github.com/micro/go-micro/client"
	"github.com/micro/go-micro/errors"
	api "github.com/micro/micro/api/proto"
	"golang.org/x/net/context"
	"net/http"
)

// Elasticsearch struct
type Elasticsearch struct{}

// Create API handler
func (es *Elasticsearch) Create(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var data []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	// Marshal unknown JSON (data)
	data, err = json.Marshal(input["data"])

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Create",
		&elastic.CreateRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Id:    fmt.Sprintf("%v", input["id"]),
			Data:  string(data),
		},
	)
	srvRsp := &elastic.CreateResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}

// Read API handler
func (es *Elasticsearch) Read(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var readRequest *elastic.ReadRequest

	if err = json.Unmarshal([]byte(req.Body), &readRequest); err != nil {
		return errors.InternalServerError("go.micro.api.elasticsearch", err.Error())
	}

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Read",
		readRequest,
	)
	srvRsp := &elastic.ReadResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}

// Update API handler
func (es *Elasticsearch) Update(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var data []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	// Marshal unknown JSON (data)
	data, err = json.Marshal(input["data"])

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Update",
		&elastic.UpdateRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Id:    fmt.Sprintf("%v", input["id"]),
			Data:  string(data),
		},
	)
	srvRsp := &elastic.UpdateResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}

// Delete API handler
func (es *Elasticsearch) Delete(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var deleteRequest *elastic.DeleteRequest

	if err = json.Unmarshal([]byte(req.Body), &deleteRequest); err != nil {
		return errors.InternalServerError("go.micro.api.elasticsearch", err.Error())
	}

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Delete",
		deleteRequest,
	)
	srvRsp := &elastic.DeleteResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = `{}`

	return nil
}

// Search API handler
func (es *Elasticsearch) Search(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Search",
		&elastic.SearchRequest{
			Index:  fmt.Sprintf("%v", input["index"]),
			Type:   fmt.Sprintf("%v", input["type"]),
			Limit:  int64((input["limit"]).(float64)),
			Offset: int64((input["offset"]).(float64)),
			Query:  fmt.Sprintf("%v", input["query"]),
		},
	)
	srvRsp := &elastic.SearchResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}

// Query API handler
func (es *Elasticsearch) Query(ctx context.Context, req *api.Request, rsp *api.Response) error {
	var err error
	var input map[string]interface{}
	var query []byte

	// Unmarshal unknown JSON
	if err = json.Unmarshal([]byte(req.Body), &input); err != nil {
		return errors.BadRequest("go.micro.api.elasticsearch", err.Error())
	}

	query, err = json.Marshal(input["query"])

	srvReq := client.NewRequest(
		"go.micro.srv.elasticsearch",
		"Elasticsearch.Query",
		&elastic.QueryRequest{
			Index: fmt.Sprintf("%v", input["index"]),
			Type:  fmt.Sprintf("%v", input["type"]),
			Query: string(query),
		},
	)
	srvRsp := &elastic.SearchResponse{}
	if err = client.Call(ctx, srvReq, srvRsp); err != nil {
		return err
	}

	rsp.StatusCode = http.StatusOK
	rsp.Body = srvRsp.Result

	return nil
}
<|endoftext|>


<commit_before>package db

import (
	"errors"
	"fmt"
	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	_ "github.com/jinzhu/gorm/dialects/postgres"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/qor/l10n"
	"github.com/qor/media"
	"github.com/qor/publish2"
	"github.com/qor/sorting"
	"github.com/qor/validations"

	"github.com/qor/qor-example/config"
)

var (
	DB *gorm.DB
)

func init() {
	var err error

	dbConfig := config.Config.DB
	if config.Config.DB.Adapter == "mysql" {
		DB, err = gorm.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?parseTime=True&loc=Local", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Name))
	} else if config.Config.DB.Adapter == "postgres" {
		DB, err = gorm.Open("postgres", fmt.Sprintf("postgres://%v:%v@%v/%v?sslmode=disable", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Name))
	} else if config.Config.DB.Adapter == "sqlite" {
		DB, err = gorm.Open("sqlite3", fmt.Sprintf("%v/%v", os.TempDir(), dbConfig.Name))
	} else {
		panic(errors.New("not supported database adapter"))
	}

	if err == nil {
		if os.Getenv("DEBUG") != "" {
			DB.LogMode(true)
		}

		l10n.RegisterCallbacks(DB)
		sorting.RegisterCallbacks(DB)
		validations.RegisterCallbacks(DB)
		media.RegisterCallbacks(DB)
		publish2.RegisterCallbacks(DB)
	} else {
		panic(err)
	}
}
<commit_msg>Set MySQL character set to UTF-8<commit_after>package db

import (
	"errors"
	"fmt"
	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	_ "github.com/jinzhu/gorm/dialects/postgres"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"github.com/qor/l10n"
	"github.com/qor/media"
	"github.com/qor/publish2"
	"github.com/qor/sorting"
	"github.com/qor/validations"

	"github.com/qor/qor-example/config"
)

var (
	DB *gorm.DB
)

func init() {
	var err error

	dbConfig := config.Config.DB
	if config.Config.DB.Adapter == "mysql" {
		DB, err = gorm.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?parseTime=True&loc=Local", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Name))
		DB = DB.Set("gorm:table_options", "CHARSET=utf8")
	} else if config.Config.DB.Adapter == "postgres" {
		DB, err = gorm.Open("postgres", fmt.Sprintf("postgres://%v:%v@%v/%v?sslmode=disable", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Name))
	} else if config.Config.DB.Adapter == "sqlite" {
		DB, err = gorm.Open("sqlite3", fmt.Sprintf("%v/%v", os.TempDir(), dbConfig.Name))
	} else {
		panic(errors.New("not supported database adapter"))
	}

	if err == nil {
		if os.Getenv("DEBUG") != "" {
			DB.LogMode(true)
		}

		l10n.RegisterCallbacks(DB)
		sorting.RegisterCallbacks(DB)
		validations.RegisterCallbacks(DB)
		media.RegisterCallbacks(DB)
		publish2.RegisterCallbacks(DB)
	} else {
		panic(err)
	}
}
<|endoftext|>