<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package yarpc

import (
	"context"

	"go.uber.org/yarpc/api/transport"
)

// CallOption defines options that may be passed in at call sites to other
// services.
//
// These may be used to add or alter the request.
type CallOption struct{ apply func(*OutboundCall) }

type callHeader struct{ k, v string }

// OutboundCall represents an outgoing call.
//
// It holds any per-call options for a request. Encoding authors may use
// OutboundCall to hydrate Requests from call-site options.
type OutboundCall struct {
	// request attributes to fill if non-nil
	headers         []callHeader
	shardKey        *string
	routingKey      *string
	routingDelegate *string

	// If non-nil, response headers should be written here.
	responseHeaders *Headers
}

// NewOutboundCall constructs a new OutboundCall with the given options.
func NewOutboundCall(options ...CallOption) *OutboundCall {
	var call OutboundCall
	for _, opt := range options {
		opt.apply(&call)
	}
	return &call
}

// WriteToRequest fills the given request with request-specific options from
// the call.
//
// The context MAY be replaced by the OutboundCall.
func (c *OutboundCall) WriteToRequest(ctx context.Context, req *transport.Request) (context.Context, error) {
	for _, h := range c.headers {
		req.Headers = req.Headers.With(h.k, h.v)
	}

	if c.shardKey != nil {
		req.ShardKey = *c.shardKey
	}
	if c.routingKey != nil {
		req.RoutingKey = *c.routingKey
	}
	if c.routingDelegate != nil {
		req.RoutingDelegate = *c.routingDelegate
	}

	// NB(abg): context and error are unused for now but we want to leave room
	// for CallOptions which can fail or modify the context.
	return ctx, nil
}

// ReadFromResponse reads information from the response for this call.
//
// This should be called only if the request is unary.
func (c *OutboundCall) ReadFromResponse(ctx context.Context, res *transport.Response) (context.Context, error) {
	// We're not using ctx right now but we may in the future.
	if c.responseHeaders != nil {
		*c.responseHeaders = Headers(res.Headers)
	}

	// NB(abg): context and error are unused for now but we want to leave room
	// for CallOptions which can fail or modify the context.
	return ctx, nil
}

// ResponseHeaders specifies that headers received in response to this request
// should be fed into the given object.
//
// 	var resHeaders yarpc.Headers
// 	resBody, err := client.SetValue(ctx, key, value, yarpc.ResponseHeaders(&resHeaders))
func ResponseHeaders(h *Headers) CallOption {
	return CallOption{func(o *OutboundCall) { o.responseHeaders = h }}
}

// TODO(abg): Example tests to document the different options

// WithHeader adds a new header to the request.
//
// 	resBody, err := client.GetValue(ctx, reqBody, yarpc.WithHeader("Token", "10"))
func WithHeader(k, v string) CallOption {
	return CallOption{func(o *OutboundCall) {
		o.headers = append(o.headers, callHeader{k: k, v: v})
	}}
}

// WithShardKey sets the shard key for the request.
func WithShardKey(sk string) CallOption {
	return CallOption{func(o *OutboundCall) { o.shardKey = &sk }}
}

// WithRoutingKey sets the routing key for the request.
func WithRoutingKey(rk string) CallOption {
	return CallOption{func(o *OutboundCall) { o.routingKey = &rk }}
}

// WithRoutingDelegate sets the routing delegate for the request.
func WithRoutingDelegate(rd string) CallOption {
	return CallOption{func(o *OutboundCall) { o.routingDelegate = &rd }}
}
<commit_msg>Add InboundCall to expose metadata on context<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package yarpc

import (
	"context"
	"errors"
	"sort"

	"go.uber.org/yarpc/api/transport"
)

type keyValuePair struct{ k, v string }

// CallOption defines options that may be passed in at call sites to other
// services.
//
// These may be used to add or alter the request.
type CallOption struct{ apply func(*OutboundCall) }

// OutboundCall is an outgoing call. It holds per-call options for a request.
//
// Encoding authors may use OutboundCall to provide a CallOption-based request
// customization mechanism, including returning response headers through
// ResponseHeaders.
type OutboundCall struct {
	// request attributes to fill if non-nil
	headers         []keyValuePair
	shardKey        *string
	routingKey      *string
	routingDelegate *string

	// If non-nil, response headers should be written here.
	responseHeaders *Headers
}

// NewOutboundCall constructs a new OutboundCall with the given options.
func NewOutboundCall(options ...CallOption) *OutboundCall {
	var call OutboundCall
	for _, opt := range options {
		opt.apply(&call)
	}
	return &call
}

// WriteToRequest fills the given request with request-specific options from
// the call.
//
// The context MAY be replaced by the OutboundCall.
func (c *OutboundCall) WriteToRequest(ctx context.Context, req *transport.Request) (context.Context, error) {
	for _, h := range c.headers {
		req.Headers = req.Headers.With(h.k, h.v)
	}

	if c.shardKey != nil {
		req.ShardKey = *c.shardKey
	}
	if c.routingKey != nil {
		req.RoutingKey = *c.routingKey
	}
	if c.routingDelegate != nil {
		req.RoutingDelegate = *c.routingDelegate
	}

	// NB(abg): context and error are unused for now but we want to leave room
	// for CallOptions which can fail or modify the context.
	return ctx, nil
}

// ReadFromResponse reads information from the response for this call.
//
// This should be called only if the request is unary.
func (c *OutboundCall) ReadFromResponse(ctx context.Context, res *transport.Response) (context.Context, error) {
	// We're not using ctx right now but we may in the future.
	if c.responseHeaders != nil {
		*c.responseHeaders = Headers(res.Headers)
	}

	// NB(abg): context and error are unused for now but we want to leave room
	// for CallOptions which can fail or modify the context.
	return ctx, nil
}

// ResponseHeaders specifies that headers received in response to this request
// should be fed into the given object.
//
// 	var resHeaders yarpc.Headers
// 	resBody, err := client.SetValue(ctx, key, value, yarpc.ResponseHeaders(&resHeaders))
func ResponseHeaders(h *Headers) CallOption {
	return CallOption{func(o *OutboundCall) { o.responseHeaders = h }}
}

// TODO(abg): Example tests to document the different options

// WithHeader adds a new header to the request.
//
// 	resBody, err := client.GetValue(ctx, reqBody, yarpc.WithHeader("Token", "10"))
func WithHeader(k, v string) CallOption {
	return CallOption{func(o *OutboundCall) {
		o.headers = append(o.headers, keyValuePair{k: k, v: v})
	}}
}

// WithShardKey sets the shard key for the request.
func WithShardKey(sk string) CallOption {
	return CallOption{func(o *OutboundCall) { o.shardKey = &sk }}
}

// WithRoutingKey sets the routing key for the request.
func WithRoutingKey(rk string) CallOption {
	return CallOption{func(o *OutboundCall) { o.routingKey = &rk }}
}

// WithRoutingDelegate sets the routing delegate for the request.
func WithRoutingDelegate(rd string) CallOption {
	return CallOption{func(o *OutboundCall) { o.routingDelegate = &rd }}
}

// InboundCall is an incoming call. It holds information about the inbound
// call and its response.
//
// Encoding authors may use InboundCall to provide information about the
// incoming request on the Context and receive response headers through
// WriteResponseHeader.
type InboundCall struct {
	resHeaders []keyValuePair
	req        *transport.Request
}

type inboundCallKey struct{} // context key for *InboundCall

// NewInboundCall builds a new InboundCall with the given context.
//
// A new context is returned and must be used in place of the original.
func NewInboundCall(ctx context.Context) (context.Context, *InboundCall) {
	call := &InboundCall{}
	return context.WithValue(ctx, inboundCallKey{}, call), call
}

// getInboundCall returns the inbound call on this context or nil.
func getInboundCall(ctx context.Context) (*InboundCall, bool) {
	call, ok := ctx.Value(inboundCallKey{}).(*InboundCall)
	return call, ok
}

// ReadFromRequest reads information from the given request.
//
// This information may be queried on the context using functions like Caller,
// Service, Procedure, etc.
func (ic *InboundCall) ReadFromRequest(req *transport.Request) error {
	// TODO(abg): Maybe we should copy attributes over so that changes to the
	// Request don't change the output.
	ic.req = req
	return nil
}

// WriteToResponse writes response information from the InboundCall onto the
// given ResponseWriter.
//
// If used, this must be called before writing the response body to the
// ResponseWriter.
func (ic *InboundCall) WriteToResponse(resw transport.ResponseWriter) error {
	var headers transport.Headers
	for _, h := range ic.resHeaders {
		headers = headers.With(h.k, h.v)
	}

	if headers.Len() > 0 {
		resw.AddHeaders(headers)
	}

	return nil
}

// WriteResponseHeader writes headers to the response of this request context.
//
// The operation will fail if the context is not an inbound request context.
func WriteResponseHeader(ctx context.Context, k, v string) error {
	call, ok := getInboundCall(ctx)
	if !ok {
		return errors.New("failed to write response header: " +
			"make sure that the context is a inbound request context")
	}

	call.resHeaders = append(call.resHeaders, keyValuePair{k: k, v: v})
	return nil
}

// Caller returns the name of the service making this request.
func Caller(ctx context.Context) string {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.Caller
	}
	return ""
}

// Service returns the name of the service being called.
func Service(ctx context.Context) string {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.Service
	}
	return ""
}

// Procedure returns the name of the procedure being called.
func Procedure(ctx context.Context) string {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.Procedure
	}
	return ""
}

// Encoding returns the encoding for this request.
func Encoding(ctx context.Context) transport.Encoding {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.Encoding
	}
	return ""
}

// Header returns the value of the given request header provided with the
// request.
func Header(ctx context.Context, k string) string {
	call, ok := getInboundCall(ctx)
	if !ok {
		return ""
	}

	if v, ok := call.req.Headers.Get(k); ok {
		return v
	}

	return ""
}

// HeaderNames returns a sorted list of the names of user defined headers
// provided with this request.
func HeaderNames(ctx context.Context) []string {
	call, ok := getInboundCall(ctx)
	if !ok {
		return nil
	}

	var names []string
	for k := range call.req.Headers.Items() {
		names = append(names, k)
	}
	sort.Strings(names)
	return names
}

// ShardKey returns the shard key for this request.
func ShardKey(ctx context.Context) string {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.ShardKey
	}
	return ""
}

// RoutingKey returns the routing key for this request.
func RoutingKey(ctx context.Context) string {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.RoutingKey
	}
	return ""
}

// RoutingDelegate returns the routing delegate for this request.
func RoutingDelegate(ctx context.Context) string {
	if call, ok := getInboundCall(ctx); ok {
		return call.req.RoutingDelegate
	}
	return ""
}
<|endoftext|>


<commit_before><commit_msg>small change to example import<commit_after><|endoftext|>


<commit_before>package cfroutes

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/bbs/models"
	"github.com/cloudfoundry-incubator/receptor"
)

const CF_ROUTER = "cf-router"

type CFRoutes []CFRoute

type CFRoute struct {
	Hostnames []string `json:"hostnames"`
	Port      uint32   `json:"port"`
}

func (c CFRoutes) RoutingInfo() models.Routes {
	data, _ := json.Marshal(c)
	routingInfo := json.RawMessage(data)
	return models.Routes{
		CF_ROUTER: &routingInfo,
	}
}

func CFRoutesFromRoutingInfo(routingInfo models.Routes) (CFRoutes, error) {
	if routingInfo == nil {
		return nil, nil
	}

	routes := routingInfo
	data, found := routes[CF_ROUTER]
	if !found {
		return nil, nil
	}

	if data == nil {
		return nil, nil
	}

	cfRoutes := CFRoutes{}
	err := json.Unmarshal(*data, &cfRoutes)

	return cfRoutes, err
}

// Old CF Route Stuff for the receptor
type LegacyCFRoutes []LegacyCFRoute

type LegacyCFRoute struct {
	Hostnames []string `json:"hostnames"`
	Port      uint16   `json:"port"`
}

func (c LegacyCFRoutes) LegacyRoutingInfo() receptor.RoutingInfo {
	data, _ := json.Marshal(c)
	routingInfo := json.RawMessage(data)
	return receptor.RoutingInfo{
		CF_ROUTER: &routingInfo,
	}
}

func LegacyCFRoutesFromLegacyRoutingInfo(routingInfo receptor.RoutingInfo) (LegacyCFRoutes, error) {
	if routingInfo == nil {
		return nil, nil
	}

	data, found := routingInfo[CF_ROUTER]
	if !found {
		return nil, nil
	}

	if data == nil {
		return nil, nil
	}

	routes := LegacyCFRoutes{}
	err := json.Unmarshal(*data, &routes)

	return routes, err
}
<commit_msg>Remove receptor references and usage<commit_after>package cfroutes

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/bbs/models"
)

const CF_ROUTER = "cf-router"

type CFRoutes []CFRoute

type CFRoute struct {
	Hostnames []string `json:"hostnames"`
	Port      uint32   `json:"port"`
}

func (c CFRoutes) RoutingInfo() models.Routes {
	data, _ := json.Marshal(c)
	routingInfo := json.RawMessage(data)
	return models.Routes{
		CF_ROUTER: &routingInfo,
	}
}

func CFRoutesFromRoutingInfo(routingInfo models.Routes) (CFRoutes, error) {
	if routingInfo == nil {
		return nil, nil
	}

	routes := routingInfo
	data, found := routes[CF_ROUTER]
	if !found {
		return nil, nil
	}

	if data == nil {
		return nil, nil
	}

	cfRoutes := CFRoutes{}
	err := json.Unmarshal(*data, &cfRoutes)

	return cfRoutes, err
}
<|endoftext|>


<commit_before>package grim

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	defaultGrimQueueName      = "grim-queue"
	defaultConfigRoot         = "/etc/grim"
	defaultResultRoot         = "/var/log/grim"
	defaultWorkspaceRoot      = "/var/tmp/grim"
	configFileName            = "config.json"
	buildScriptName           = "build.sh"
	repoBuildScriptName       = "grim_build.sh"
	repoHiddenBuildScriptName = ".grim_build.sh"
)

type config struct {
	GrimQueueName   *string
	ResultRoot      *string
	WorkspaceRoot   *string
	AWSRegion       *string
	AWSKey          *string
	AWSSecret       *string
	GitHubToken     *string
	PathToCloneIn   *string
	SnsTopicName    *string
	HipChatRoom     *string
	HipChatToken    *string
	GrimServerID    *string
	PendingTemplate *string
	ErrorTemplate   *string
	SuccessTemplate *string
	FailureTemplate *string
}

type effectiveConfig struct {
	grimQueueName   string
	resultRoot      string
	workspaceRoot   string
	awsRegion       string
	awsKey          string
	awsSecret       string
	gitHubToken     string
	pathToCloneIn   string
	snsTopicName    string
	hipChatRoom     string
	hipChatToken    string
	grimServerID    string
	pendingTemplate string
	errorTemplate   string
	successTemplate string
	failureTemplate string
}

type repo struct {
	owner, name string
}

func getEffectiveConfigRoot(configRootPtr *string) string {
	if stringPtrNotEmpty(configRootPtr) {
		return *configRootPtr
	}

	return defaultConfigRoot
}

func getAllConfiguredRepos(configRoot string) []repo {
	var repos []repo

	repoPattern := filepath.Join(configRoot, "*/*")
	matches, err := filepath.Glob(repoPattern)
	if err != nil {
		return repos
	}

	for _, match := range matches {
		fi, fiErr := os.Stat(match)
		if fiErr != nil || !fi.Mode().IsDir() {
			continue
		}

		rel, relErr := filepath.Rel(configRoot, match)
		if relErr != nil {
			continue
		}

		owner, name := filepath.Split(rel)
		if owner != "" && name != "" {
			repos = append(repos, repo{filepath.Clean(owner), filepath.Clean(name)})
		}
	}

	return repos
}

func getEffectiveGlobalConfig(configRoot string) (*effectiveConfig, error) {
	var err error
	var global *config

	if global, err = loadGlobalConfig(configRoot); err == nil {
		ec := buildGlobalEffectiveConfig(global)

		if err = validateEffectiveConfig(ec); err == nil {
			return &ec, nil
		}
	}

	return nil, err
}

func getEffectiveConfig(configRoot, owner, repo string) (*effectiveConfig, error) {
	var err error
	var global, local *config

	if global, err = loadGlobalConfig(configRoot); err == nil {
		if local, err = loadLocalConfig(configRoot, owner, repo); err == nil {
			ec := buildLocalEffectiveConfig(buildGlobalEffectiveConfig(global), local, owner, repo)

			if err = validateLocalEffectiveConfig(ec); err != nil {
				return nil, err
			}

			if err = validateEffectiveConfig(ec); err == nil {
				return &ec, nil
			}

		}
	}

	return nil, err
}

func buildGlobalEffectiveConfig(global *config) effectiveConfig {
	return effectiveConfig{
		grimQueueName:   firstNonEmptyStringPtr(global.GrimQueueName, &defaultGrimQueueName),
		resultRoot:      firstNonEmptyStringPtr(global.ResultRoot, &defaultResultRoot),
		workspaceRoot:   firstNonEmptyStringPtr(global.WorkspaceRoot, &defaultWorkspaceRoot),
		grimServerID:    firstNonEmptyStringPtr(global.GrimServerID, global.GrimQueueName, &defaultGrimQueueName),
		awsRegion:       firstNonEmptyStringPtr(global.AWSRegion),
		awsKey:          firstNonEmptyStringPtr(global.AWSKey),
		awsSecret:       firstNonEmptyStringPtr(global.AWSSecret),
		gitHubToken:     firstNonEmptyStringPtr(global.GitHubToken),
		hipChatRoom:     firstNonEmptyStringPtr(global.HipChatRoom),
		hipChatToken:    firstNonEmptyStringPtr(global.HipChatToken),
		pendingTemplate: firstNonEmptyStringPtr(global.PendingTemplate, templateForStartandSuccess("Starting")),
		errorTemplate:   firstNonEmptyStringPtr(global.ErrorTemplate, templateForFailureandError("Error during")),
		failureTemplate: firstNonEmptyStringPtr(global.FailureTemplate, templateForFailureandError("Failure during")),
		successTemplate: firstNonEmptyStringPtr(global.SuccessTemplate, templateForStartandSuccess("Success after")),
	}
}

func buildLocalEffectiveConfig(global effectiveConfig, local *config, owner, repo string) effectiveConfig {
	return effectiveConfig{
		grimQueueName:   global.grimQueueName,
		resultRoot:      global.resultRoot,
		workspaceRoot:   global.workspaceRoot,
		awsRegion:       global.awsRegion,
		awsKey:          global.awsKey,
		awsSecret:       global.awsSecret,
		grimServerID:    global.grimServerID,
		gitHubToken:     firstNonEmptyStringPtr(local.GitHubToken, &global.gitHubToken),
		hipChatRoom:     firstNonEmptyStringPtr(local.HipChatRoom, &global.hipChatRoom),
		hipChatToken:    firstNonEmptyStringPtr(local.HipChatToken, &global.hipChatToken),
		pendingTemplate: firstNonEmptyStringPtr(local.PendingTemplate, &global.pendingTemplate),
		successTemplate: firstNonEmptyStringPtr(local.SuccessTemplate, &global.successTemplate),
		errorTemplate:   firstNonEmptyStringPtr(local.ErrorTemplate, &global.errorTemplate),
		failureTemplate: firstNonEmptyStringPtr(local.FailureTemplate, &global.failureTemplate),
		pathToCloneIn:   firstNonEmptyStringPtr(local.PathToCloneIn),
		snsTopicName:    firstNonEmptyStringPtr(local.SnsTopicName, defaultTopicName(owner, repo)),
	}
}

func defaultTopicName(owner, repo string) *string {
	snsTopicName := fmt.Sprintf("grim-%v-%v-repo-topic", owner, repo)
	return &snsTopicName
}

func templateForStartandSuccess(preamble string) *string {
	s := fmt.Sprintf("%s build of {{.Owner}}/{{.Repo}} initiated by a {{.EventName}} to {{.Target}} by {{.UserName}} ({{.Workspace}})", preamble)
	return &s
}

func templateForFailureandError(preamble string) *string {
	s := fmt.Sprintf("%s build of {{.Owner}}/{{.Repo}} initiated by a {{.EventName}} to {{.Target}} by {{.UserName}} ({{.logDir}})", preamble)
	return &s
}

func validateEffectiveConfig(ec effectiveConfig) error {
	if ec.awsRegion == "" {
		return fmt.Errorf("AWS region is required")
	} else if ec.awsKey == "" {
		return fmt.Errorf("AWS key is required")
	} else if ec.awsSecret == "" {
		return fmt.Errorf("AWS secret is required")
	}

	return nil
}

func validateLocalEffectiveConfig(ec effectiveConfig) error {
	if ec.snsTopicName == "" {
		return fmt.Errorf("Must have a Sns topic name!")
	}

	if strings.Contains(ec.snsTopicName, ".") {
		return fmt.Errorf("Cannot have . in sns topic name.  Default topic names can be set in the build config file using the SnsTopicName parameter.")
	}

	return nil
}

func loadGlobalConfig(configRoot string) (*config, error) {
	return loadConfig(filepath.Join(configRoot, configFileName))
}

func loadLocalConfig(configRoot, owner, repo string) (*config, error) {
	return loadConfig(filepath.Join(configRoot, owner, repo, configFileName))
}

func loadConfig(path string) (*config, error) {
	c := new(config)

	if bs, err := ioutil.ReadFile(path); err != nil {
		return nil, err
	} else if err := json.Unmarshal(bs, c); err != nil {
		return nil, err
	}

	return c, nil
}

func firstNonEmptyStringPtr(strPtrs ...*string) string {
	for _, strPtr := range strPtrs {
		if stringPtrNotEmpty(strPtr) {
			return *strPtr
		}
	}

	return ""
}

func stringPtrNotEmpty(strPtr *string) bool {
	return strPtr != nil && *strPtr != ""
}
<commit_msg>Logdir typo fix<commit_after>package grim

// Copyright 2015 MediaMath <http://www.mediamath.com>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	defaultGrimQueueName      = "grim-queue"
	defaultConfigRoot         = "/etc/grim"
	defaultResultRoot         = "/var/log/grim"
	defaultWorkspaceRoot      = "/var/tmp/grim"
	configFileName            = "config.json"
	buildScriptName           = "build.sh"
	repoBuildScriptName       = "grim_build.sh"
	repoHiddenBuildScriptName = ".grim_build.sh"
)

type config struct {
	GrimQueueName   *string
	ResultRoot      *string
	WorkspaceRoot   *string
	AWSRegion       *string
	AWSKey          *string
	AWSSecret       *string
	GitHubToken     *string
	PathToCloneIn   *string
	SnsTopicName    *string
	HipChatRoom     *string
	HipChatToken    *string
	GrimServerID    *string
	PendingTemplate *string
	ErrorTemplate   *string
	SuccessTemplate *string
	FailureTemplate *string
}

type effectiveConfig struct {
	grimQueueName   string
	resultRoot      string
	workspaceRoot   string
	awsRegion       string
	awsKey          string
	awsSecret       string
	gitHubToken     string
	pathToCloneIn   string
	snsTopicName    string
	hipChatRoom     string
	hipChatToken    string
	grimServerID    string
	pendingTemplate string
	errorTemplate   string
	successTemplate string
	failureTemplate string
}

type repo struct {
	owner, name string
}

func getEffectiveConfigRoot(configRootPtr *string) string {
	if stringPtrNotEmpty(configRootPtr) {
		return *configRootPtr
	}

	return defaultConfigRoot
}

func getAllConfiguredRepos(configRoot string) []repo {
	var repos []repo

	repoPattern := filepath.Join(configRoot, "*/*")
	matches, err := filepath.Glob(repoPattern)
	if err != nil {
		return repos
	}

	for _, match := range matches {
		fi, fiErr := os.Stat(match)
		if fiErr != nil || !fi.Mode().IsDir() {
			continue
		}

		rel, relErr := filepath.Rel(configRoot, match)
		if relErr != nil {
			continue
		}

		owner, name := filepath.Split(rel)
		if owner != "" && name != "" {
			repos = append(repos, repo{filepath.Clean(owner), filepath.Clean(name)})
		}
	}

	return repos
}

func getEffectiveGlobalConfig(configRoot string) (*effectiveConfig, error) {
	var err error
	var global *config

	if global, err = loadGlobalConfig(configRoot); err == nil {
		ec := buildGlobalEffectiveConfig(global)

		if err = validateEffectiveConfig(ec); err == nil {
			return &ec, nil
		}
	}

	return nil, err
}

func getEffectiveConfig(configRoot, owner, repo string) (*effectiveConfig, error) {
	var err error
	var global, local *config

	if global, err = loadGlobalConfig(configRoot); err == nil {
		if local, err = loadLocalConfig(configRoot, owner, repo); err == nil {
			ec := buildLocalEffectiveConfig(buildGlobalEffectiveConfig(global), local, owner, repo)

			if err = validateLocalEffectiveConfig(ec); err != nil {
				return nil, err
			}

			if err = validateEffectiveConfig(ec); err == nil {
				return &ec, nil
			}

		}
	}

	return nil, err
}

func buildGlobalEffectiveConfig(global *config) effectiveConfig {
	return effectiveConfig{
		grimQueueName:   firstNonEmptyStringPtr(global.GrimQueueName, &defaultGrimQueueName),
		resultRoot:      firstNonEmptyStringPtr(global.ResultRoot, &defaultResultRoot),
		workspaceRoot:   firstNonEmptyStringPtr(global.WorkspaceRoot, &defaultWorkspaceRoot),
		grimServerID:    firstNonEmptyStringPtr(global.GrimServerID, global.GrimQueueName, &defaultGrimQueueName),
		awsRegion:       firstNonEmptyStringPtr(global.AWSRegion),
		awsKey:          firstNonEmptyStringPtr(global.AWSKey),
		awsSecret:       firstNonEmptyStringPtr(global.AWSSecret),
		gitHubToken:     firstNonEmptyStringPtr(global.GitHubToken),
		hipChatRoom:     firstNonEmptyStringPtr(global.HipChatRoom),
		hipChatToken:    firstNonEmptyStringPtr(global.HipChatToken),
		pendingTemplate: firstNonEmptyStringPtr(global.PendingTemplate, templateForStartandSuccess("Starting")),
		errorTemplate:   firstNonEmptyStringPtr(global.ErrorTemplate, templateForFailureandError("Error during")),
		failureTemplate: firstNonEmptyStringPtr(global.FailureTemplate, templateForFailureandError("Failure during")),
		successTemplate: firstNonEmptyStringPtr(global.SuccessTemplate, templateForStartandSuccess("Success after")),
	}
}

func buildLocalEffectiveConfig(global effectiveConfig, local *config, owner, repo string) effectiveConfig {
	return effectiveConfig{
		grimQueueName:   global.grimQueueName,
		resultRoot:      global.resultRoot,
		workspaceRoot:   global.workspaceRoot,
		awsRegion:       global.awsRegion,
		awsKey:          global.awsKey,
		awsSecret:       global.awsSecret,
		grimServerID:    global.grimServerID,
		gitHubToken:     firstNonEmptyStringPtr(local.GitHubToken, &global.gitHubToken),
		hipChatRoom:     firstNonEmptyStringPtr(local.HipChatRoom, &global.hipChatRoom),
		hipChatToken:    firstNonEmptyStringPtr(local.HipChatToken, &global.hipChatToken),
		pendingTemplate: firstNonEmptyStringPtr(local.PendingTemplate, &global.pendingTemplate),
		successTemplate: firstNonEmptyStringPtr(local.SuccessTemplate, &global.successTemplate),
		errorTemplate:   firstNonEmptyStringPtr(local.ErrorTemplate, &global.errorTemplate),
		failureTemplate: firstNonEmptyStringPtr(local.FailureTemplate, &global.failureTemplate),
		pathToCloneIn:   firstNonEmptyStringPtr(local.PathToCloneIn),
		snsTopicName:    firstNonEmptyStringPtr(local.SnsTopicName, defaultTopicName(owner, repo)),
	}
}

func defaultTopicName(owner, repo string) *string {
	snsTopicName := fmt.Sprintf("grim-%v-%v-repo-topic", owner, repo)
	return &snsTopicName
}

func templateForStartandSuccess(preamble string) *string {
	s := fmt.Sprintf("%s build of {{.Owner}}/{{.Repo}} initiated by a {{.EventName}} to {{.Target}} by {{.UserName}} ({{.Workspace}})", preamble)
	return &s
}

func templateForFailureandError(preamble string) *string {
	s := fmt.Sprintf("%s build of {{.Owner}}/{{.Repo}} initiated by a {{.EventName}} to {{.Target}} by {{.UserName}} ({{.LogDir}})", preamble)
	return &s
}

func validateEffectiveConfig(ec effectiveConfig) error {
	if ec.awsRegion == "" {
		return fmt.Errorf("AWS region is required")
	} else if ec.awsKey == "" {
		return fmt.Errorf("AWS key is required")
	} else if ec.awsSecret == "" {
		return fmt.Errorf("AWS secret is required")
	}

	return nil
}

func validateLocalEffectiveConfig(ec effectiveConfig) error {
	if ec.snsTopicName == "" {
		return fmt.Errorf("Must have a Sns topic name!")
	}

	if strings.Contains(ec.snsTopicName, ".") {
		return fmt.Errorf("Cannot have . in sns topic name.  Default topic names can be set in the build config file using the SnsTopicName parameter.")
	}

	return nil
}

func loadGlobalConfig(configRoot string) (*config, error) {
	return loadConfig(filepath.Join(configRoot, configFileName))
}

func loadLocalConfig(configRoot, owner, repo string) (*config, error) {
	return loadConfig(filepath.Join(configRoot, owner, repo, configFileName))
}

func loadConfig(path string) (*config, error) {
	c := new(config)

	if bs, err := ioutil.ReadFile(path); err != nil {
		return nil, err
	} else if err := json.Unmarshal(bs, c); err != nil {
		return nil, err
	}

	return c, nil
}

func firstNonEmptyStringPtr(strPtrs ...*string) string {
	for _, strPtr := range strPtrs {
		if stringPtrNotEmpty(strPtr) {
			return *strPtr
		}
	}

	return ""
}

func stringPtrNotEmpty(strPtr *string) bool {
	return strPtr != nil && *strPtr != ""
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"errors"

	"github.com/cihub/seelog"
)

const (
	CFG_DEFAULT_TIMEOUT               = time.Second
	CFG_DEFAULT_FAILED_SLEEP          = time.Second
	CFG_MIN_FAILED_SLEEP              = time.Second
	CFG_DEFAULT_PROCESSING_TIMEOUT    = 10 * time.Second
	CFG_MIN_PROCESSING_TIMEOUT        = 10 * time.Second
	CFG_MIN_STAT_SERVER_PORT          = 8000
	CFG_MAX_STAT_SERVER_PORT          = 10000
	CFG_DEFAULT_MASTER_OFFSET_STORAGE = "zookeeper"
	DEFAULT_LOG_COLLECT_RATIO         = 20
)

type MapConfig map[string]interface{}
type ArbiterConfig MapConfig
type OffsetStorageConfig MapConfig
type TransporterConfig MapConfig

type OffsetMangerConfig struct {
	StorageName   string                         `json:"storage_name"`
	StorageConfig OffsetStorageConfig            `json:"storage_config"`
	SlaveStorage  map[string]OffsetStorageConfig `json:"slave_storage_config"`
}

type CallbackItemConfig struct {
	WorkerNum            int                `json:"worker_num"`
	Url                  string             `json:"url"`
	RetryTimes           int                `json:"retry_times"`
	TimeoutStr           string             `json:"timeout"`
	Timeout              time.Duration      `json:"null,omitempty"`
	BypassFailed         bool               `json:"bypass_failed"`
	FailedSleepStr       string             `json:"failed_sleep"`
	FailedSleep          time.Duration      `json:"null,omitempty"`
	Topics               []string           `json:"topics"`
	Zookeepers           []string           `json:"zookeepers"`
	ZkPath               string             `json:"zk_path"`
	Serializer           string             `json:"serializer"`
	ContentType          string             `json:"content_type"`
	LogCollectRatio      int                `json:"log_collect_ratio"`
	OffsetConfig         OffsetMangerConfig `json:"offset"`
	ArbiterName          string             `json:"arbiter_name"`
	ArbiterConfig        ArbiterConfig      `json:"arbiter_config"`
	TransporterName      string             `json:"transporter_name"`
	TransporterConfig    TransporterConfig  `json:"transporter_config"`
	InitialFromOldest    bool               `json:"initial_from_oldest"`
	ProcessingTimeout    time.Duration      `json:"null,omitempty"`
	ProcessingTimeoutStr string             `json:"processing_timeout"`
}

type ServiceConfig struct {
	LogFile            string               `json:"log_file"`
	Callbacks          []CallbackItemConfig `json:"consumer_groups"`
	ConnectionPoolSize int                  `json:"connection_pool_size"`
	StatServerPort     int                  `json:"stat_server_port"`
}

func LoadConfigFile(configFile string) (*ServiceConfig, error) {
	var c *ServiceConfig
	path := configFile
	fi, err := os.Open(path)
	defer fi.Close()
	if err != nil {
		return nil, err
	}

	fd, err := ioutil.ReadAll(fi)
	err = json.Unmarshal([]byte(fd), &c)
	if err != nil {
		return nil, err
	}

	if c.ConnectionPoolSize <= 0 {
		c.ConnectionPoolSize = http.DefaultMaxIdleConnsPerHost
	}

	if c.StatServerPort != 0 && (c.StatServerPort < CFG_MIN_STAT_SERVER_PORT || c.StatServerPort >= CFG_MAX_STAT_SERVER_PORT) {
		seelog.Warnf("server config stat_server_port should be greater than %d and lower than %d",
			CFG_MIN_STAT_SERVER_PORT, CFG_MAX_STAT_SERVER_PORT)
		c.StatServerPort = 0
	}

	for i, _ := range c.Callbacks {
		callback := &c.Callbacks[i]

		if "" == callback.Url {
			seelog.Errorf("callback config url should not be empty")
			return nil, errors.New("callback url is empty")
		}

		if 0 == len(callback.Topics) {
			seelog.Errorf("callback config topics should not be empty")
			return nil, errors.New("callback config topics is empty")
		}

		if 0 == len(callback.Zookeepers) {
			seelog.Errorf("callback config zookeepers should not be empty")
			return nil, errors.New("callback config zookeepers is empty")
		}

		callback.Timeout, err = time.ParseDuration(callback.TimeoutStr)
		if err != nil {
			seelog.Warnf("callback config timeout error(%s),using default.config value:%s",
				err.Error(), callback.TimeoutStr)

			callback.Timeout = CFG_DEFAULT_TIMEOUT
			callback.TimeoutStr = fmt.Sprintf("%dms", CFG_DEFAULT_TIMEOUT/time.Millisecond)
		}

		callback.FailedSleep, err = time.ParseDuration(callback.FailedSleepStr)
		if err != nil {
			seelog.Warnf("callback config failed_sleep error(%s),using default.config value:%s",
				err.Error(), callback.FailedSleepStr)

			callback.FailedSleep = CFG_DEFAULT_FAILED_SLEEP
			callback.FailedSleepStr = fmt.Sprintf("%dms", CFG_DEFAULT_FAILED_SLEEP/time.Millisecond)
		}

		if callback.FailedSleep < CFG_MIN_FAILED_SLEEP {
			seelog.Warnf("callback config failed_sleep too small,using min.config value:%s,%s",
				callback.FailedSleep, callback.FailedSleepStr)

			callback.FailedSleep = CFG_MIN_FAILED_SLEEP
			callback.FailedSleepStr = fmt.Sprintf("%dms", CFG_MIN_FAILED_SLEEP/time.Millisecond)
		}

		callback.ProcessingTimeout, err = time.ParseDuration(callback.ProcessingTimeoutStr)
		if err != nil {
			seelog.Warnf("callback config processing_timeout error(%s),using default.config value:%s",
				err.Error(), callback.ProcessingTimeoutStr)

			callback.ProcessingTimeout = CFG_DEFAULT_PROCESSING_TIMEOUT
		}

		if callback.ProcessingTimeout < CFG_MIN_PROCESSING_TIMEOUT {
			seelog.Warnf("callback config processing_timeout too small,using min.config value:%s,%s",
				callback.ProcessingTimeout, callback.ProcessingTimeoutStr)

			callback.ProcessingTimeout = CFG_MIN_PROCESSING_TIMEOUT
			callback.ProcessingTimeoutStr = fmt.Sprintf("%dms", CFG_MIN_PROCESSING_TIMEOUT/time.Millisecond)
		}

		if callback.OffsetConfig.StorageName == "" {
			callback.OffsetConfig.StorageName = CFG_DEFAULT_MASTER_OFFSET_STORAGE
		}

		if callback.LogCollectRatio <= 0 {
			callback.LogCollectRatio = DEFAULT_LOG_COLLECT_RATIO
		}

	}

	return c, nil
}
<commit_msg>Add arbiter/transporter default config<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"errors"

	"github.com/cihub/seelog"
)

const (
	CFG_DEFAULT_TIMEOUT               = time.Second
	CFG_DEFAULT_FAILED_SLEEP          = time.Second
	CFG_MIN_FAILED_SLEEP              = time.Second
	CFG_DEFAULT_PROCESSING_TIMEOUT    = 10 * time.Second
	CFG_MIN_PROCESSING_TIMEOUT        = 10 * time.Second
	CFG_MIN_STAT_SERVER_PORT          = 8000
	CFG_MAX_STAT_SERVER_PORT          = 10000
	CFG_DEFAULT_MASTER_OFFSET_STORAGE = "zookeeper"
	CFG_DEFAULT_ARBITER               = "sequential"
	CFG_DEFAULT_TRANSPORTER           = "http"
	DEFAULT_LOG_COLLECT_RATIO         = 20
)

type MapConfig map[string]interface{}
type ArbiterConfig MapConfig
type OffsetStorageConfig MapConfig
type TransporterConfig MapConfig

type OffsetMangerConfig struct {
	StorageName   string                         `json:"storage_name"`
	StorageConfig OffsetStorageConfig            `json:"storage_config"`
	SlaveStorage  map[string]OffsetStorageConfig `json:"slave_storage_config"`
}

type CallbackItemConfig struct {
	WorkerNum            int                `json:"worker_num"`
	Url                  string             `json:"url"`
	RetryTimes           int                `json:"retry_times"`
	TimeoutStr           string             `json:"timeout"`
	Timeout              time.Duration      `json:"null,omitempty"`
	BypassFailed         bool               `json:"bypass_failed"`
	FailedSleepStr       string             `json:"failed_sleep"`
	FailedSleep          time.Duration      `json:"null,omitempty"`
	Topics               []string           `json:"topics"`
	Zookeepers           []string           `json:"zookeepers"`
	ZkPath               string             `json:"zk_path"`
	Serializer           string             `json:"serializer"`
	ContentType          string             `json:"content_type"`
	LogCollectRatio      int                `json:"log_collect_ratio"`
	OffsetConfig         OffsetMangerConfig `json:"offset"`
	ArbiterName          string             `json:"arbiter_name"`
	ArbiterConfig        ArbiterConfig      `json:"arbiter_config"`
	TransporterName      string             `json:"transporter_name"`
	TransporterConfig    TransporterConfig  `json:"transporter_config"`
	InitialFromOldest    bool               `json:"initial_from_oldest"`
	ProcessingTimeout    time.Duration      `json:"null,omitempty"`
	ProcessingTimeoutStr string             `json:"processing_timeout"`
}

type ServiceConfig struct {
	LogFile            string               `json:"log_file"`
	Callbacks          []CallbackItemConfig `json:"consumer_groups"`
	ConnectionPoolSize int                  `json:"connection_pool_size"`
	StatServerPort     int                  `json:"stat_server_port"`
}

func LoadConfigFile(configFile string) (*ServiceConfig, error) {
	var c *ServiceConfig
	path := configFile
	fi, err := os.Open(path)
	defer fi.Close()
	if err != nil {
		return nil, err
	}

	fd, err := ioutil.ReadAll(fi)
	err = json.Unmarshal([]byte(fd), &c)
	if err != nil {
		return nil, err
	}

	if c.ConnectionPoolSize <= 0 {
		c.ConnectionPoolSize = http.DefaultMaxIdleConnsPerHost
	}

	if c.StatServerPort != 0 && (c.StatServerPort < CFG_MIN_STAT_SERVER_PORT || c.StatServerPort >= CFG_MAX_STAT_SERVER_PORT) {
		seelog.Warnf("server config stat_server_port should be greater than %d and lower than %d",
			CFG_MIN_STAT_SERVER_PORT, CFG_MAX_STAT_SERVER_PORT)
		c.StatServerPort = 0
	}

	for i, _ := range c.Callbacks {
		callback := &c.Callbacks[i]

		if "" == callback.Url {
			seelog.Errorf("callback config url should not be empty")
			return nil, errors.New("callback url is empty")
		}

		if 0 == len(callback.Topics) {
			seelog.Errorf("callback config topics should not be empty")
			return nil, errors.New("callback config topics is empty")
		}

		if 0 == len(callback.Zookeepers) {
			seelog.Errorf("callback config zookeepers should not be empty")
			return nil, errors.New("callback config zookeepers is empty")
		}

		callback.Timeout, err = time.ParseDuration(callback.TimeoutStr)
		if err != nil {
			seelog.Warnf("callback config timeout error(%s),using default.config value:%s",
				err.Error(), callback.TimeoutStr)

			callback.Timeout = CFG_DEFAULT_TIMEOUT
			callback.TimeoutStr = fmt.Sprintf("%dms", CFG_DEFAULT_TIMEOUT/time.Millisecond)
		}

		callback.FailedSleep, err = time.ParseDuration(callback.FailedSleepStr)
		if err != nil {
			seelog.Warnf("callback config failed_sleep error(%s),using default.config value:%s",
				err.Error(), callback.FailedSleepStr)

			callback.FailedSleep = CFG_DEFAULT_FAILED_SLEEP
			callback.FailedSleepStr = fmt.Sprintf("%dms", CFG_DEFAULT_FAILED_SLEEP/time.Millisecond)
		}

		if callback.FailedSleep < CFG_MIN_FAILED_SLEEP {
			seelog.Warnf("callback config failed_sleep too small,using min.config value:%s,%s",
				callback.FailedSleep, callback.FailedSleepStr)

			callback.FailedSleep = CFG_MIN_FAILED_SLEEP
			callback.FailedSleepStr = fmt.Sprintf("%dms", CFG_MIN_FAILED_SLEEP/time.Millisecond)
		}

		callback.ProcessingTimeout, err = time.ParseDuration(callback.ProcessingTimeoutStr)
		if err != nil {
			seelog.Warnf("callback config processing_timeout error(%s),using default.config value:%s",
				err.Error(), callback.ProcessingTimeoutStr)

			callback.ProcessingTimeout = CFG_DEFAULT_PROCESSING_TIMEOUT
		}

		if callback.ProcessingTimeout < CFG_MIN_PROCESSING_TIMEOUT {
			seelog.Warnf("callback config processing_timeout too small,using min.config value:%s,%s",
				callback.ProcessingTimeout, callback.ProcessingTimeoutStr)

			callback.ProcessingTimeout = CFG_MIN_PROCESSING_TIMEOUT
			callback.ProcessingTimeoutStr = fmt.Sprintf("%dms", CFG_MIN_PROCESSING_TIMEOUT/time.Millisecond)
		}

		if callback.OffsetConfig.StorageName == "" {
			callback.OffsetConfig.StorageName = CFG_DEFAULT_MASTER_OFFSET_STORAGE
		}

		if callback.ArbiterName == "" {
			callback.ArbiterName = CFG_DEFAULT_ARBITER
		}

		if callback.TransporterName == "" {
			callback.TransporterName = CFG_DEFAULT_TRANSPORTER
		}

		if callback.LogCollectRatio <= 0 {
			callback.LogCollectRatio = DEFAULT_LOG_COLLECT_RATIO
		}

	}

	return c, nil
}
<|endoftext|>


<commit_before>package config

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

	"github.com/spf13/viper"
)

// TablesConf informations
type TablesConf struct {
	Name        string   `mapstructure:"name"`
	Permissions []string `mapstructure:"permissions"`
	Fields      []string `mapstructure:"fields"`
}

// AccessConf informations
type AccessConf struct {
	Restrict bool
	Tables   []TablesConf
}

// Prest basic config
type Prest struct {
	// HTTPPort Declare which http port the PREST used
	HTTPPort        int
	PGHost          string
	PGPort          int
	PGUser          string
	PGPass          string
	PGDatabase      string
	PGMaxIdleConn   int
	PGMAxOpenConn   int
	PGConnTimeout   int
	JWTKey          string
	MigrationsPath  string
	QueriesPath     string
	AccessConf      AccessConf
	CORSAllowOrigin []string
	Debug           bool
}

// PrestConf config variable
var PrestConf *Prest

func viperCfg() {
	filePath := os.Getenv("PREST_CONF")
	dir, file := path.Split(filePath)
	file = strings.TrimSuffix(file, filepath.Ext(file))
	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvPrefix("PREST")
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(replacer)
	viper.AddConfigPath(dir)
	viper.SetConfigName(file)
	viper.SetConfigType("toml")
	viper.SetDefault("http.port", 3000)
	viper.SetDefault("pg.host", "127.0.0.1")
	viper.SetDefault("pg.port", 5432)
	viper.SetDefault("pg.maxidleconn", 10)
	viper.SetDefault("pg.maxopenconn", 10)
	viper.SetDefault("pg.conntimeout", 10)
	viper.SetDefault("debug", false)

	user, err := user.Current()
	if err != nil {
		log.Println("{viperCfg}", err)
	}

	viper.SetDefault("queries.location", filepath.Join(user.HomeDir, "queries"))
}

// Parse pREST config
func Parse(cfg *Prest) (err error) {
	err = viper.ReadInConfig()
	if err != nil {
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			fmt.Println("Running without config file")
		default:
			return
		}
	}
	cfg.HTTPPort = viper.GetInt("http.port")
	cfg.PGHost = viper.GetString("pg.host")
	cfg.PGPort = viper.GetInt("pg.port")
	cfg.PGUser = viper.GetString("pg.user")
	cfg.PGPass = viper.GetString("pg.pass")
	cfg.PGDatabase = viper.GetString("pg.database")
	cfg.PGMaxIdleConn = viper.GetInt("pg.maxidleconn")
	cfg.PGMAxOpenConn = viper.GetInt("pg.maxopenconn")
	cfg.PGConnTimeout = viper.GetInt("pg.conntimeout")
	cfg.JWTKey = viper.GetString("jwt.key")
	cfg.MigrationsPath = viper.GetString("migrations")
	cfg.AccessConf.Restrict = viper.GetBool("access.restrict")
	cfg.QueriesPath = viper.GetString("queries.location")
	cfg.CORSAllowOrigin = viper.GetStringSlice("cors.alloworigin")
	cfg.Debug = viper.GetBool("debug")

	var t []TablesConf
	err = viper.UnmarshalKey("access.tables", &t)
	if err != nil {
		return err
	}

	cfg.AccessConf.Tables = t

	return
}

// Load configuration
func Load() {
	viperCfg()
	PrestConf = &Prest{}
	err := Parse(PrestConf)
	if err != nil {
		panic(err)
	}

	if !PrestConf.AccessConf.Restrict {
		fmt.Println("You are running pREST in public mode.")
	}

	if PrestConf.Debug {
		fmt.Println("You are running pREST in debug mode.")
	}

	if _, err = os.Stat(PrestConf.QueriesPath); os.IsNotExist(err) {
		if err = os.MkdirAll(PrestConf.QueriesPath, 0700); os.IsNotExist(err) {
			fmt.Printf("Queries directory %s is not created", PrestConf.QueriesPath)
		}
	}
}
<commit_msg>fix toml config file loading on windows (#180)<commit_after>package config

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"github.com/spf13/viper"
)

// TablesConf informations
type TablesConf struct {
	Name        string   `mapstructure:"name"`
	Permissions []string `mapstructure:"permissions"`
	Fields      []string `mapstructure:"fields"`
}

// AccessConf informations
type AccessConf struct {
	Restrict bool
	Tables   []TablesConf
}

// Prest basic config
type Prest struct {
	// HTTPPort Declare which http port the PREST used
	HTTPPort        int
	PGHost          string
	PGPort          int
	PGUser          string
	PGPass          string
	PGDatabase      string
	PGMaxIdleConn   int
	PGMAxOpenConn   int
	PGConnTimeout   int
	JWTKey          string
	MigrationsPath  string
	QueriesPath     string
	AccessConf      AccessConf
	CORSAllowOrigin []string
	Debug           bool
}

// PrestConf config variable
var PrestConf *Prest

func viperCfg() {
	filePath := os.Getenv("PREST_CONF")
	dir, file := filepath.Split(filePath)
	file = strings.TrimSuffix(file, filepath.Ext(file))
	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvPrefix("PREST")
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(replacer)
	viper.AddConfigPath(dir)
	viper.SetConfigName(file)
	viper.SetConfigType("toml")
	viper.SetDefault("http.port", 3000)
	viper.SetDefault("pg.host", "127.0.0.1")
	viper.SetDefault("pg.port", 5432)
	viper.SetDefault("pg.maxidleconn", 10)
	viper.SetDefault("pg.maxopenconn", 10)
	viper.SetDefault("pg.conntimeout", 10)
	viper.SetDefault("debug", false)

	user, err := user.Current()
	if err != nil {
		log.Println("{viperCfg}", err)
	}

	viper.SetDefault("queries.location", filepath.Join(user.HomeDir, "queries"))
}

// Parse pREST config
func Parse(cfg *Prest) (err error) {
	err = viper.ReadInConfig()
	if err != nil {
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			fmt.Println("Running without config file")
		default:
			return
		}
	}
	cfg.HTTPPort = viper.GetInt("http.port")
	cfg.PGHost = viper.GetString("pg.host")
	cfg.PGPort = viper.GetInt("pg.port")
	cfg.PGUser = viper.GetString("pg.user")
	cfg.PGPass = viper.GetString("pg.pass")
	cfg.PGDatabase = viper.GetString("pg.database")
	cfg.PGMaxIdleConn = viper.GetInt("pg.maxidleconn")
	cfg.PGMAxOpenConn = viper.GetInt("pg.maxopenconn")
	cfg.PGConnTimeout = viper.GetInt("pg.conntimeout")
	cfg.JWTKey = viper.GetString("jwt.key")
	cfg.MigrationsPath = viper.GetString("migrations")
	cfg.AccessConf.Restrict = viper.GetBool("access.restrict")
	cfg.QueriesPath = viper.GetString("queries.location")
	cfg.CORSAllowOrigin = viper.GetStringSlice("cors.alloworigin")
	cfg.Debug = viper.GetBool("debug")

	var t []TablesConf
	err = viper.UnmarshalKey("access.tables", &t)
	if err != nil {
		return err
	}

	cfg.AccessConf.Tables = t

	return
}

// Load configuration
func Load() {
	viperCfg()
	PrestConf = &Prest{}
	err := Parse(PrestConf)
	if err != nil {
		panic(err)
	}

	if !PrestConf.AccessConf.Restrict {
		fmt.Println("You are running pREST in public mode.")
	}

	if PrestConf.Debug {
		fmt.Println("You are running pREST in debug mode.")
	}

	if _, err = os.Stat(PrestConf.QueriesPath); os.IsNotExist(err) {
		if err = os.MkdirAll(PrestConf.QueriesPath, 0700); os.IsNotExist(err) {
			fmt.Printf("Queries directory %s is not created", PrestConf.QueriesPath)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    // "fmt"
    "strings"
    "reflect"
	"github.com/go-ini/ini"
	"github.com/urfave/cli"
	"path"
)

// This is the global configuration, it's loaded from .s3cfg (by default) then with added
//  overrides from the command line
//
// Command lines are by default the snake case version of the the struct names with "-" instead of "_"
//
type Config struct {
	AccessKey string `ini:"access_key"`
	SecretKey string `ini:"secret_key"`

    CheckMD5  bool  `ini:"check_md5" cli:"check-md5"`
    DryRun      bool  `ini:"dry_run"`
    Verbose     bool  `ini:"verbose"`
    Recursive     bool  `ini:"recursive"`
    Force     bool  `ini:"force"`
    SkipExisting     bool  `ini:"skip_existing"`
}

// Read the configuration file if found, otherwise return default configuration
//  Precedence order (most important to least):
//   - Command Line options
//   - Environment Variables
//   - Config File
//   - Default Values
func NewConfig(c *cli.Context) *Config {
	cfgPath := "/.s3cfg"

	if c.GlobalIsSet("config") {
		cfgPath = c.GlobalString("config")
	} else if c.IsSet("config") {
		cfgPath = c.String("config")
	} else {
		if value := GetEnv("HOME"); value != nil {
			cfgPath = path.Join(*value, ".s3cfg")
		}
	}

	config := loadConfigFile(cfgPath)

    parseOptions(config, c)

    if c.GlobalIsSet("no-check-md5") || c.IsSet("no-check-md5") {
        config.CheckMD5 = false
    }

	return config
}

// Load the config file if possible, but if there is an error return the default configuration file
func loadConfigFile(path string) *Config {
	config := &Config{CheckMD5: false}

	// fmt.Println("Read config ", path)

	if err := ini.MapTo(config, path); err != nil {
		return config
	}

	return config
}

// Pull the options out of the cli.Context and save them into the configuration object
func parseOptions(config *Config, c *cli.Context) {
    rt := reflect.TypeOf(*config)
    rv := reflect.ValueOf(config)

    for i := 0; i < rt.NumField(); i++ {
        field := rt.Field(i)

        name := ""
        if field.Tag.Get("cli") != "" {
            name = field.Tag.Get("cli")
        } else {
            name = strings.Replace(CamelToSnake(field.Name), "_", "-", -1)
        }

        gset := c.GlobalIsSet(name)
        lset := c.IsSet(name)

        // fmt.Println(name, gset, lset, c.String(name))

        // FIXME: This isn't great, "IsSet()" isn't triggered for environment variables
        if !gset && !lset && c.String(name) == "" {
            continue
        }

        f := rv.Elem().FieldByName(field.Name)

        if !f.IsValid() || !f.CanSet() {
            continue
        }

        switch f.Kind() {
        case reflect.Bool:
            if lset {
                f.SetBool(c.Bool(name))
            } else {
                f.SetBool(c.GlobalBool(name))
            }
        case reflect.String:
            if lset {
                f.SetString(c.String(name))
            } else {
                f.SetString(c.GlobalString(name))
            }
        case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
            if lset {
                f.SetInt(c.Int64(name))
            } else {
                f.SetInt(c.GlobalInt64(name))
            }
        }
    }
}
<commit_msg>Fix error EmptyStaticCreds: static credentials are empty<commit_after>package main

import (
    // "fmt"
    "strings"
    "reflect"
	"github.com/go-ini/ini"
	"github.com/urfave/cli"
	"path"
)

// This is the global configuration, it's loaded from .s3cfg (by default) then with added
//  overrides from the command line
//
// Command lines are by default the snake case version of the the struct names with "-" instead of "_"
//
type Config struct {
	AccessKey string `ini:"access_key"`
	SecretKey string `ini:"secret_key"`

    CheckMD5  bool  `ini:"check_md5" cli:"check-md5"`
    DryRun      bool  `ini:"dry_run"`
    Verbose     bool  `ini:"verbose"`
    Recursive     bool  `ini:"recursive"`
    Force     bool  `ini:"force"`
    SkipExisting     bool  `ini:"skip_existing"`
}

// Read the configuration file if found, otherwise return default configuration
//  Precedence order (most important to least):
//   - Command Line options
//   - Environment Variables
//   - Config File
//   - Default Values
func NewConfig(c *cli.Context) *Config {
	cfgPath := "/.s3cfg"

	if c.GlobalIsSet("config") {
		cfgPath = c.GlobalString("config")
	} else if c.IsSet("config") {
		cfgPath = c.String("config")
	} else {
		if value := GetEnv("HOME"); value != nil {
			cfgPath = path.Join(*value, ".s3cfg")
		}
	}

	config := loadConfigFile(cfgPath)

    parseOptions(config, c)

    if c.GlobalIsSet("no-check-md5") || c.IsSet("no-check-md5") {
        config.CheckMD5 = false
    }

	return config
}

// Load the config file if possible, but if there is an error return the default configuration file
func loadConfigFile(path string) *Config {
	config := &Config{CheckMD5: false}

	// fmt.Println("Read config ", path)

	cfg, err := ini.Load(path)
	if err != nil {
		return config
	}

	if err := cfg.Section("default").MapTo(config); err != nil {
		return config
	}

	return config
}

// Pull the options out of the cli.Context and save them into the configuration object
func parseOptions(config *Config, c *cli.Context) {
    rt := reflect.TypeOf(*config)
    rv := reflect.ValueOf(config)

    for i := 0; i < rt.NumField(); i++ {
        field := rt.Field(i)

        name := ""
        if field.Tag.Get("cli") != "" {
            name = field.Tag.Get("cli")
        } else {
            name = strings.Replace(CamelToSnake(field.Name), "_", "-", -1)
        }

        gset := c.GlobalIsSet(name)
        lset := c.IsSet(name)

        // fmt.Println(name, gset, lset, c.String(name))

        // FIXME: This isn't great, "IsSet()" isn't triggered for environment variables
        if !gset && !lset && c.String(name) == "" {
            continue
        }

        f := rv.Elem().FieldByName(field.Name)

        if !f.IsValid() || !f.CanSet() {
            continue
        }

        switch f.Kind() {
        case reflect.Bool:
            if lset {
                f.SetBool(c.Bool(name))
            } else {
                f.SetBool(c.GlobalBool(name))
            }
        case reflect.String:
            if lset {
                f.SetString(c.String(name))
            } else {
                f.SetString(c.GlobalString(name))
            }
        case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
            if lset {
                f.SetInt(c.Int64(name))
            } else {
                f.SetInt(c.GlobalInt64(name))
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"

	"github.com/google/alertmanager-irc-relay/logging"
)

const (
	defaultMsgOnceTemplate = "Alert {{ .GroupLabels.alertname }} for {{ .GroupLabels.job }} is {{ .Status }}"
	defaultMsgTemplate     = "Alert {{ .Labels.alertname }} on {{ .Labels.instance }} is {{ .Status }}"
)

type IRCChannel struct {
	Name     string `yaml:"name"`
	Password string `yaml:"password"`
}

type Config struct {
	HTTPHost        string       `yaml:"http_host"`
	HTTPPort        int          `yaml:"http_port"`
	IRCNick         string       `yaml:"irc_nickname"`
	IRCNickPass     string       `yaml:"irc_nickname_password"`
	IRCRealName     string       `yaml:"irc_realname"`
	IRCHost         string       `yaml:"irc_host"`
	IRCPort         int          `yaml:"irc_port"`
	IRCHostPass     string       `yaml:"irc_host_password"`
	IRCUseSSL       bool         `yaml:"irc_use_ssl"`
	IRCVerifySSL    bool         `yaml:"irc_verify_ssl"`
	IRCChannels     []IRCChannel `yaml:"irc_channels"`
	MsgTemplate     string       `yaml:"msg_template"`
	MsgOnce         bool         `yaml:"msg_once_per_alert_group"`
	UsePrivmsg      bool         `yaml:"use_privmsg"`
	AlertBufferSize int          `yaml:"alert_buffer_size"`

	NickservIdentifyPatterns []string `yaml:"nickserv_identify_patterns"`
}

func LoadConfig(configFile string) (*Config, error) {
	config := &Config{
		HTTPHost:        "localhost",
		HTTPPort:        8000,
		IRCNick:         "alertmanager-irc-relay",
		IRCNickPass:     "",
		IRCRealName:     "Alertmanager IRC Relay",
		IRCHost:         "example.com",
		IRCPort:         7000,
		IRCHostPass:     "",
		IRCUseSSL:       true,
		IRCVerifySSL:    true,
		IRCChannels:     []IRCChannel{},
		MsgOnce:         false,
		UsePrivmsg:      false,
		AlertBufferSize: 2048,
		NickservIdentifyPatterns: []string{
			"identify via /msg NickServ identify <password>",
			"type /msg NickServ IDENTIFY password",
			"authenticate yourself to services with the IDENTIFY command",
		},
	}

	if configFile != "" {
		data, err := ioutil.ReadFile(configFile)
		if err != nil {
			return nil, err
		}
		data = []byte(os.ExpandEnv(string(data)))
		if err := yaml.Unmarshal(data, config); err != nil {
			return nil, err
		}
	}

	// Set default template if config does not have one.
	if config.MsgTemplate == "" {
		if config.MsgOnce {
			config.MsgTemplate = defaultMsgOnceTemplate
		} else {
			config.MsgTemplate = defaultMsgTemplate
		}
	}

	loadedConfig, _ := yaml.Marshal(config)
	logging.Debug("Loaded config:\n%s", loadedConfig)

	return config, nil
}
<commit_msg>Handle Atheme NickServ IDENTIFY NOTICE<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"

	"github.com/google/alertmanager-irc-relay/logging"
)

const (
	defaultMsgOnceTemplate = "Alert {{ .GroupLabels.alertname }} for {{ .GroupLabels.job }} is {{ .Status }}"
	defaultMsgTemplate     = "Alert {{ .Labels.alertname }} on {{ .Labels.instance }} is {{ .Status }}"
)

type IRCChannel struct {
	Name     string `yaml:"name"`
	Password string `yaml:"password"`
}

type Config struct {
	HTTPHost        string       `yaml:"http_host"`
	HTTPPort        int          `yaml:"http_port"`
	IRCNick         string       `yaml:"irc_nickname"`
	IRCNickPass     string       `yaml:"irc_nickname_password"`
	IRCRealName     string       `yaml:"irc_realname"`
	IRCHost         string       `yaml:"irc_host"`
	IRCPort         int          `yaml:"irc_port"`
	IRCHostPass     string       `yaml:"irc_host_password"`
	IRCUseSSL       bool         `yaml:"irc_use_ssl"`
	IRCVerifySSL    bool         `yaml:"irc_verify_ssl"`
	IRCChannels     []IRCChannel `yaml:"irc_channels"`
	MsgTemplate     string       `yaml:"msg_template"`
	MsgOnce         bool         `yaml:"msg_once_per_alert_group"`
	UsePrivmsg      bool         `yaml:"use_privmsg"`
	AlertBufferSize int          `yaml:"alert_buffer_size"`

	NickservIdentifyPatterns []string `yaml:"nickserv_identify_patterns"`
}

func LoadConfig(configFile string) (*Config, error) {
	config := &Config{
		HTTPHost:        "localhost",
		HTTPPort:        8000,
		IRCNick:         "alertmanager-irc-relay",
		IRCNickPass:     "",
		IRCRealName:     "Alertmanager IRC Relay",
		IRCHost:         "example.com",
		IRCPort:         7000,
		IRCHostPass:     "",
		IRCUseSSL:       true,
		IRCVerifySSL:    true,
		IRCChannels:     []IRCChannel{},
		MsgOnce:         false,
		UsePrivmsg:      false,
		AlertBufferSize: 2048,
		NickservIdentifyPatterns: []string{
			"Please choose a different nickname, or identify via",
			"identify via /msg NickServ identify <password>",
			"type /msg NickServ IDENTIFY password",
			"authenticate yourself to services with the IDENTIFY command",
		},
	}

	if configFile != "" {
		data, err := ioutil.ReadFile(configFile)
		if err != nil {
			return nil, err
		}
		data = []byte(os.ExpandEnv(string(data)))
		if err := yaml.Unmarshal(data, config); err != nil {
			return nil, err
		}
	}

	// Set default template if config does not have one.
	if config.MsgTemplate == "" {
		if config.MsgOnce {
			config.MsgTemplate = defaultMsgOnceTemplate
		} else {
			config.MsgTemplate = defaultMsgTemplate
		}
	}

	loadedConfig, _ := yaml.Marshal(config)
	logging.Debug("Loaded config:\n%s", loadedConfig)

	return config, nil
}
<|endoftext|>


<commit_before>package gen

import (
	"bytes"
	"fmt"
	"hash/fnv"
	"io"
	"path"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"unicode"
)

const pkgWriter = "github.com/mailru/easyjson/jwriter"
const pkgLexer = "github.com/mailru/easyjson/jlexer"
const pkgEasyJson = "github.com/mailru/easyjson"

// FieldNamer defines a policy for generating names for struct fields.
type FieldNamer interface {
	GetJSONFieldName(t reflect.Type, f reflect.StructField) string
}

// Generator generates the requested marshallers/unmarshallers.
type Generator struct {
	out *bytes.Buffer

	pkgName    string
	pkgPath    string
	buildTags  string
	hashString string

	varCounter int

	noStdMarshalers bool
	omitEmpty       bool
	fieldNamer      FieldNamer

	// package path to local alias map for tracking imports
	imports map[string]string

	// types that marshallers were requested for by user
	marshallers map[reflect.Type]bool

	// types that encoders were already generated for
	typesSeen map[reflect.Type]bool

	// types that encoders were requested for (e.g. by encoders of other types)
	typesUnseen []reflect.Type

	// function name to relevant type maps to track names of de-/encoders in
	// case of a name clash or unnamed structs
	functionNames map[string]reflect.Type
}

// NewGenerator initializes and returns a Generator.
func NewGenerator(filename string) *Generator {
	ret := &Generator{
		imports: map[string]string{
			pkgWriter:       "jwriter",
			pkgLexer:        "jlexer",
			pkgEasyJson:     "easyjson",
			"encoding/json": "json",
		},
		fieldNamer:    DefaultFieldNamer{},
		marshallers:   make(map[reflect.Type]bool),
		typesSeen:     make(map[reflect.Type]bool),
		functionNames: make(map[string]reflect.Type),
	}

	// Use a file-unique prefix on all auxiliary functions to avoid
	// name clashes.
	hash := fnv.New32()
	hash.Write([]byte(filename))
	ret.hashString = fmt.Sprintf("%x", hash.Sum32())

	return ret
}

// SetPkg sets the name and path of output package.
func (g *Generator) SetPkg(name, path string) {
	g.pkgName = name
	g.pkgPath = path
}

// SetBuildTags sets build tags for the output file.
func (g *Generator) SetBuildTags(tags string) {
	g.buildTags = tags
}

// SetFieldNamer sets field naming strategy.
func (g *Generator) SetFieldNamer(n FieldNamer) {
	g.fieldNamer = n
}

// UseSnakeCase sets snake_case field naming strategy.
func (g *Generator) UseSnakeCase() {
	g.fieldNamer = SnakeCaseFieldNamer{}
}

// NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON
// methods (only the custom interface).
func (g *Generator) NoStdMarshalers() {
	g.noStdMarshalers = true
}

// OmitEmpty triggers `json=",omitempty"` behaviour by default.
func (g *Generator) OmitEmpty() {
	g.omitEmpty = true
}

// addTypes requests to generate en-/decoding functions for the given type.
func (g *Generator) addType(t reflect.Type) {
	if g.typesSeen[t] {
		return
	}
	for _, t1 := range g.typesUnseen {
		if t1 == t {
			return
		}
	}
	g.typesUnseen = append(g.typesUnseen, t)
}

// Add requests to generate (un-)marshallers and en-/decoding functions for the type of given object.
func (g *Generator) Add(obj interface{}) {
	t := reflect.TypeOf(obj)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	g.addType(t)
	g.marshallers[t] = true
}

// printHeader prints package declaration and imports.
func (g *Generator) printHeader() {
	if g.buildTags != "" {
		fmt.Println("// +build ", g.buildTags)
		fmt.Println()
	}
	fmt.Println("// AUTOGENERATED FILE: easyjson marshaller/unmarshallers.")
	fmt.Println()
	fmt.Println("package ", g.pkgName)
	fmt.Println()

	byAlias := map[string]string{}
	var aliases []string
	for path, alias := range g.imports {
		aliases = append(aliases, alias)
		byAlias[alias] = path
	}

	sort.Strings(aliases)
	fmt.Println("import (")
	for _, alias := range g.imports {
		fmt.Printf("  %s %q\n", alias, byAlias[alias])
	}

	fmt.Println(")")
	fmt.Println("")
	fmt.Println("// suppress unused package warning")
	fmt.Println("var (")
	fmt.Println("   _ *json.RawMessage")
	fmt.Println("   _ *jlexer.Lexer")
	fmt.Println("   _ *jwriter.Writer")
	fmt.Println("   _ easyjson.Marshaler")
	fmt.Println(")")

	fmt.Println()
}

// Run runs the generator and outputs generated code to out.
func (g *Generator) Run(out io.Writer) error {
	g.out = &bytes.Buffer{}

	for len(g.typesUnseen) > 0 {
		t := g.typesUnseen[len(g.typesUnseen)-1]
		g.typesUnseen = g.typesUnseen[:len(g.typesUnseen)-1]
		g.typesSeen[t] = true

		if err := g.genDecoder(t); err != nil {
			return err
		}
		if err := g.genEncoder(t); err != nil {
			return err
		}

		if !g.marshallers[t] {
			continue
		}

		if err := g.genStructMarshaller(t); err != nil {
			return err
		}
		if err := g.genStructUnmarshaller(t); err != nil {
			return err
		}
	}
	g.printHeader()
	_, err := out.Write(g.out.Bytes())
	return err
}

// fixes vendored paths
func fixPkgPathVendoring(pkgPath string) string {
	const vendor = "/vendor/"
	if i := strings.LastIndex(pkgPath, vendor); i != -1 {
		return pkgPath[i+len(vendor):]
	}
	return pkgPath
}

// pkgAlias creates and returns and import alias for a given package.
func (g *Generator) pkgAlias(pkgPath string) string {
	pkgPath = fixPkgPathVendoring(pkgPath)
	if alias := g.imports[pkgPath]; alias != "" {
		return alias
	}

	for i := 0; ; i++ {
		alias := path.Base(pkgPath)
		if i > 0 {
			alias += fmt.Sprint(i)
		}

		exists := false
		for _, v := range g.imports {
			if v == alias {
				exists = true
				break
			}
		}

		if !exists {
			g.imports[pkgPath] = alias
			return alias
		}
	}
}

// getType return the textual type name of given type that can be used in generated code.
func (g *Generator) getType(t reflect.Type) string {
	if t.Name() == "" {
		switch t.Kind() {
		case reflect.Ptr:
			return "*" + g.getType(t.Elem())
		case reflect.Slice:
			return "[]" + g.getType(t.Elem())
		case reflect.Array:
			return "[" + strconv.Itoa(t.Len()) + "]" + g.getType(t.Elem())
		case reflect.Map:
			return "map[" + g.getType(t.Key()) + "]" + g.getType(t.Elem())
		}
	}

	if t.Name() == "" || t.PkgPath() == "" {
		return t.String()
	} else if t.PkgPath() == g.pkgPath {
		return t.Name()
	}
	// TODO: unnamed structs.
	return g.pkgAlias(t.PkgPath()) + "." + t.Name()
}

// uniqueVarName returns a file-unique name that can be used for generated variables.
func (g *Generator) uniqueVarName() string {
	g.varCounter++
	return fmt.Sprint("v", g.varCounter)
}

// safeName escapes unsafe characters in pkg/type name and returns a string that can be used
// in encoder/decoder names for the type.
func (g *Generator) safeName(t reflect.Type) string {
	name := t.PkgPath()
	if t.Name() == "" {
		name += "anonymous"
	} else {
		name += "." + t.Name()
	}

	parts := []string{}
	part := []rune{}
	for _, c := range name {
		if unicode.IsLetter(c) || unicode.IsDigit(c) {
			part = append(part, c)
		} else if len(part) > 0 {
			parts = append(parts, string(part))
			part = []rune{}
		}
	}
	return joinFunctionNameParts(false, parts...)
}

// functionName returns a function name for a given type with a given prefix. If a function
// with this prefix already exists for a type, it is returned.
//
// Method is used to track encoder/decoder names for the type.
func (g *Generator) functionName(prefix string, t reflect.Type) string {
	prefix = joinFunctionNameParts(true, "easyjson", g.hashString, prefix)
	name := joinFunctionNameParts(true, prefix, g.safeName(t))

	// Most of the names will be unique, try a shortcut first.
	if e, ok := g.functionNames[name]; !ok || e == t {
		g.functionNames[name] = t
		return name
	}

	// Search if the function already exists.
	for name1, t1 := range g.functionNames {
		if t1 == t && strings.HasPrefix(name1, prefix) {
			return name1
		}
	}

	// Create a new name in the case of a clash.
	for i := 1; ; i++ {
		nm := fmt.Sprint(name, i)
		if _, ok := g.functionNames[nm]; ok {
			continue
		}
		g.functionNames[nm] = t
		return nm
	}
}

// DefaultFieldsNamer implements trivial naming policy equivalent to encoding/json.
type DefaultFieldNamer struct{}

func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
	jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
	if jsonName != "" {
		return jsonName
	} else {
		return f.Name
	}
}

// SnakeCaseFieldNamer implements CamelCase to snake_case conversion for fields names.
type SnakeCaseFieldNamer struct{}

func camelToSnake(name string) string {
	var ret bytes.Buffer

	multipleUpper := false
	var lastUpper rune
	var beforeUpper rune

	for _, c := range name {
		// Non-lowercase character after uppercase is considered to be uppercase too.
		isUpper := (unicode.IsUpper(c) || (lastUpper != 0 && !unicode.IsLower(c)))

		if lastUpper != 0 {
			// Output a delimiter if last character was either the first uppercase character
			// in a row, or the last one in a row (e.g. 'S' in "HTTPServer").
			// Do not output a delimiter at the beginning of the name.

			firstInRow := !multipleUpper
			lastInRow := !isUpper

			if ret.Len() > 0 && (firstInRow || lastInRow) && beforeUpper != '_' {
				ret.WriteByte('_')
			}
			ret.WriteRune(unicode.ToLower(lastUpper))
		}

		// Buffer uppercase char, do not output it yet as a delimiter may be required if the
		// next character is lowercase.
		if isUpper {
			multipleUpper = (lastUpper != 0)
			lastUpper = c
			continue
		}

		ret.WriteRune(c)
		lastUpper = 0
		beforeUpper = c
		multipleUpper = false
	}

	if lastUpper != 0 {
		ret.WriteRune(unicode.ToLower(lastUpper))
	}
	return string(ret.Bytes())
}

func (SnakeCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
	jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
	if jsonName != "" {
		return jsonName
	}

	return camelToSnake(f.Name)
}

func joinFunctionNameParts(keepFirst bool, parts ...string) string {
	buf := bytes.NewBufferString("")
	for i, part := range parts {
		if i == 0 && keepFirst {
			buf.WriteString(part)
		} else {
			if len(part) > 0 {
				buf.WriteString(strings.ToUpper(string(part[0])))
			}
			if len(part) > 1 {
				buf.WriteString(part[1:])
			}
		}
	}
	return buf.String()
}
<commit_msg>rename const pkgEasyJson -> pkgEasyJSON<commit_after>package gen

import (
	"bytes"
	"fmt"
	"hash/fnv"
	"io"
	"path"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"unicode"
)

const pkgWriter = "github.com/mailru/easyjson/jwriter"
const pkgLexer = "github.com/mailru/easyjson/jlexer"
const pkgEasyJSON = "github.com/mailru/easyjson"

// FieldNamer defines a policy for generating names for struct fields.
type FieldNamer interface {
	GetJSONFieldName(t reflect.Type, f reflect.StructField) string
}

// Generator generates the requested marshallers/unmarshallers.
type Generator struct {
	out *bytes.Buffer

	pkgName    string
	pkgPath    string
	buildTags  string
	hashString string

	varCounter int

	noStdMarshalers bool
	omitEmpty       bool
	fieldNamer      FieldNamer

	// package path to local alias map for tracking imports
	imports map[string]string

	// types that marshallers were requested for by user
	marshallers map[reflect.Type]bool

	// types that encoders were already generated for
	typesSeen map[reflect.Type]bool

	// types that encoders were requested for (e.g. by encoders of other types)
	typesUnseen []reflect.Type

	// function name to relevant type maps to track names of de-/encoders in
	// case of a name clash or unnamed structs
	functionNames map[string]reflect.Type
}

// NewGenerator initializes and returns a Generator.
func NewGenerator(filename string) *Generator {
	ret := &Generator{
		imports: map[string]string{
			pkgWriter:       "jwriter",
			pkgLexer:        "jlexer",
			pkgEasyJSON:     "easyjson",
			"encoding/json": "json",
		},
		fieldNamer:    DefaultFieldNamer{},
		marshallers:   make(map[reflect.Type]bool),
		typesSeen:     make(map[reflect.Type]bool),
		functionNames: make(map[string]reflect.Type),
	}

	// Use a file-unique prefix on all auxiliary functions to avoid
	// name clashes.
	hash := fnv.New32()
	hash.Write([]byte(filename))
	ret.hashString = fmt.Sprintf("%x", hash.Sum32())

	return ret
}

// SetPkg sets the name and path of output package.
func (g *Generator) SetPkg(name, path string) {
	g.pkgName = name
	g.pkgPath = path
}

// SetBuildTags sets build tags for the output file.
func (g *Generator) SetBuildTags(tags string) {
	g.buildTags = tags
}

// SetFieldNamer sets field naming strategy.
func (g *Generator) SetFieldNamer(n FieldNamer) {
	g.fieldNamer = n
}

// UseSnakeCase sets snake_case field naming strategy.
func (g *Generator) UseSnakeCase() {
	g.fieldNamer = SnakeCaseFieldNamer{}
}

// NoStdMarshalers instructs not to generate standard MarshalJSON/UnmarshalJSON
// methods (only the custom interface).
func (g *Generator) NoStdMarshalers() {
	g.noStdMarshalers = true
}

// OmitEmpty triggers `json=",omitempty"` behaviour by default.
func (g *Generator) OmitEmpty() {
	g.omitEmpty = true
}

// addTypes requests to generate en-/decoding functions for the given type.
func (g *Generator) addType(t reflect.Type) {
	if g.typesSeen[t] {
		return
	}
	for _, t1 := range g.typesUnseen {
		if t1 == t {
			return
		}
	}
	g.typesUnseen = append(g.typesUnseen, t)
}

// Add requests to generate (un-)marshallers and en-/decoding functions for the type of given object.
func (g *Generator) Add(obj interface{}) {
	t := reflect.TypeOf(obj)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	g.addType(t)
	g.marshallers[t] = true
}

// printHeader prints package declaration and imports.
func (g *Generator) printHeader() {
	if g.buildTags != "" {
		fmt.Println("// +build ", g.buildTags)
		fmt.Println()
	}
	fmt.Println("// AUTOGENERATED FILE: easyjson marshaller/unmarshallers.")
	fmt.Println()
	fmt.Println("package ", g.pkgName)
	fmt.Println()

	byAlias := map[string]string{}
	var aliases []string
	for path, alias := range g.imports {
		aliases = append(aliases, alias)
		byAlias[alias] = path
	}

	sort.Strings(aliases)
	fmt.Println("import (")
	for _, alias := range g.imports {
		fmt.Printf("  %s %q\n", alias, byAlias[alias])
	}

	fmt.Println(")")
	fmt.Println("")
	fmt.Println("// suppress unused package warning")
	fmt.Println("var (")
	fmt.Println("   _ *json.RawMessage")
	fmt.Println("   _ *jlexer.Lexer")
	fmt.Println("   _ *jwriter.Writer")
	fmt.Println("   _ easyjson.Marshaler")
	fmt.Println(")")

	fmt.Println()
}

// Run runs the generator and outputs generated code to out.
func (g *Generator) Run(out io.Writer) error {
	g.out = &bytes.Buffer{}

	for len(g.typesUnseen) > 0 {
		t := g.typesUnseen[len(g.typesUnseen)-1]
		g.typesUnseen = g.typesUnseen[:len(g.typesUnseen)-1]
		g.typesSeen[t] = true

		if err := g.genDecoder(t); err != nil {
			return err
		}
		if err := g.genEncoder(t); err != nil {
			return err
		}

		if !g.marshallers[t] {
			continue
		}

		if err := g.genStructMarshaller(t); err != nil {
			return err
		}
		if err := g.genStructUnmarshaller(t); err != nil {
			return err
		}
	}
	g.printHeader()
	_, err := out.Write(g.out.Bytes())
	return err
}

// fixes vendored paths
func fixPkgPathVendoring(pkgPath string) string {
	const vendor = "/vendor/"
	if i := strings.LastIndex(pkgPath, vendor); i != -1 {
		return pkgPath[i+len(vendor):]
	}
	return pkgPath
}

// pkgAlias creates and returns and import alias for a given package.
func (g *Generator) pkgAlias(pkgPath string) string {
	pkgPath = fixPkgPathVendoring(pkgPath)
	if alias := g.imports[pkgPath]; alias != "" {
		return alias
	}

	for i := 0; ; i++ {
		alias := path.Base(pkgPath)
		if i > 0 {
			alias += fmt.Sprint(i)
		}

		exists := false
		for _, v := range g.imports {
			if v == alias {
				exists = true
				break
			}
		}

		if !exists {
			g.imports[pkgPath] = alias
			return alias
		}
	}
}

// getType return the textual type name of given type that can be used in generated code.
func (g *Generator) getType(t reflect.Type) string {
	if t.Name() == "" {
		switch t.Kind() {
		case reflect.Ptr:
			return "*" + g.getType(t.Elem())
		case reflect.Slice:
			return "[]" + g.getType(t.Elem())
		case reflect.Array:
			return "[" + strconv.Itoa(t.Len()) + "]" + g.getType(t.Elem())
		case reflect.Map:
			return "map[" + g.getType(t.Key()) + "]" + g.getType(t.Elem())
		}
	}

	if t.Name() == "" || t.PkgPath() == "" {
		return t.String()
	} else if t.PkgPath() == g.pkgPath {
		return t.Name()
	}
	// TODO: unnamed structs.
	return g.pkgAlias(t.PkgPath()) + "." + t.Name()
}

// uniqueVarName returns a file-unique name that can be used for generated variables.
func (g *Generator) uniqueVarName() string {
	g.varCounter++
	return fmt.Sprint("v", g.varCounter)
}

// safeName escapes unsafe characters in pkg/type name and returns a string that can be used
// in encoder/decoder names for the type.
func (g *Generator) safeName(t reflect.Type) string {
	name := t.PkgPath()
	if t.Name() == "" {
		name += "anonymous"
	} else {
		name += "." + t.Name()
	}

	parts := []string{}
	part := []rune{}
	for _, c := range name {
		if unicode.IsLetter(c) || unicode.IsDigit(c) {
			part = append(part, c)
		} else if len(part) > 0 {
			parts = append(parts, string(part))
			part = []rune{}
		}
	}
	return joinFunctionNameParts(false, parts...)
}

// functionName returns a function name for a given type with a given prefix. If a function
// with this prefix already exists for a type, it is returned.
//
// Method is used to track encoder/decoder names for the type.
func (g *Generator) functionName(prefix string, t reflect.Type) string {
	prefix = joinFunctionNameParts(true, "easyjson", g.hashString, prefix)
	name := joinFunctionNameParts(true, prefix, g.safeName(t))

	// Most of the names will be unique, try a shortcut first.
	if e, ok := g.functionNames[name]; !ok || e == t {
		g.functionNames[name] = t
		return name
	}

	// Search if the function already exists.
	for name1, t1 := range g.functionNames {
		if t1 == t && strings.HasPrefix(name1, prefix) {
			return name1
		}
	}

	// Create a new name in the case of a clash.
	for i := 1; ; i++ {
		nm := fmt.Sprint(name, i)
		if _, ok := g.functionNames[nm]; ok {
			continue
		}
		g.functionNames[nm] = t
		return nm
	}
}

// DefaultFieldsNamer implements trivial naming policy equivalent to encoding/json.
type DefaultFieldNamer struct{}

func (DefaultFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
	jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
	if jsonName != "" {
		return jsonName
	} else {
		return f.Name
	}
}

// SnakeCaseFieldNamer implements CamelCase to snake_case conversion for fields names.
type SnakeCaseFieldNamer struct{}

func camelToSnake(name string) string {
	var ret bytes.Buffer

	multipleUpper := false
	var lastUpper rune
	var beforeUpper rune

	for _, c := range name {
		// Non-lowercase character after uppercase is considered to be uppercase too.
		isUpper := (unicode.IsUpper(c) || (lastUpper != 0 && !unicode.IsLower(c)))

		if lastUpper != 0 {
			// Output a delimiter if last character was either the first uppercase character
			// in a row, or the last one in a row (e.g. 'S' in "HTTPServer").
			// Do not output a delimiter at the beginning of the name.

			firstInRow := !multipleUpper
			lastInRow := !isUpper

			if ret.Len() > 0 && (firstInRow || lastInRow) && beforeUpper != '_' {
				ret.WriteByte('_')
			}
			ret.WriteRune(unicode.ToLower(lastUpper))
		}

		// Buffer uppercase char, do not output it yet as a delimiter may be required if the
		// next character is lowercase.
		if isUpper {
			multipleUpper = (lastUpper != 0)
			lastUpper = c
			continue
		}

		ret.WriteRune(c)
		lastUpper = 0
		beforeUpper = c
		multipleUpper = false
	}

	if lastUpper != 0 {
		ret.WriteRune(unicode.ToLower(lastUpper))
	}
	return string(ret.Bytes())
}

func (SnakeCaseFieldNamer) GetJSONFieldName(t reflect.Type, f reflect.StructField) string {
	jsonName := strings.Split(f.Tag.Get("json"), ",")[0]
	if jsonName != "" {
		return jsonName
	}

	return camelToSnake(f.Name)
}

func joinFunctionNameParts(keepFirst bool, parts ...string) string {
	buf := bytes.NewBufferString("")
	for i, part := range parts {
		if i == 0 && keepFirst {
			buf.WriteString(part)
		} else {
			if len(part) > 0 {
				buf.WriteString(strings.ToUpper(string(part[0])))
			}
			if len(part) > 1 {
				buf.WriteString(part[1:])
			}
		}
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package ovn

import (
	"fmt"
	"os/exec"
	"strings"
	"unicode"

	"github.com/Sirupsen/logrus"
	kapi "k8s.io/client-go/pkg/api/v1"
)

func (ovn *Controller) getLoadBalancer(protocol kapi.Protocol) string {
	// TODO: add a cache here for the load balancer lookup, so that multiple calls to nbctl can be avoided
	var out []byte
	if protocol == kapi.ProtocolTCP {
		out, _ = exec.Command(OvnNbctl, "--data=bare", "--no-heading",
			"--columns=_uuid", "find", "load_balancer",
			"external_ids:k8s-cluster-lb-tcp=yes").CombinedOutput()
	} else if protocol == kapi.ProtocolUDP {
		out, _ = exec.Command(OvnNbctl, "--data=bare", "--no-heading",
			"--columns=_uuid", "find", "load_balancer",
			"external_ids:k8s-cluster-lb-udp=yes").CombinedOutput()
	}
	outStr := strings.TrimFunc(string(out), unicode.IsSpace)
	return outStr
}

func (ovn *Controller) createLoadBalancerVIP(lb string, serviceIP string, port int32, ips []string, targetPort int32) error {
	logrus.Debugf("Creating lb with %s, %s, %d, [%v], %d", lb, serviceIP, port, ips, targetPort)

	// With service_ip:port as a VIP, create an entry in 'load_balancer'
	// key is of the form "IP:port" (with quotes around)
	key := fmt.Sprintf("\"%s:%d\"", serviceIP, port)

	if len(ips) == 0 {
		_, err := exec.Command(OvnNbctl, "remove", "load_balancer", lb, "vips", key).CombinedOutput()
		return err
	}

	var commaSeparatedEndpoints string
	for i, ep := range ips {
		comma := ","
		if i == 0 {
			comma = ""
		}
		commaSeparatedEndpoints += fmt.Sprintf("%s%s:%d", comma, ep, targetPort)
	}
	target := fmt.Sprintf("vips:\"%s:%d\"=\"%s\"", serviceIP, port, commaSeparatedEndpoints)

	out, err := exec.Command(OvnNbctl, "set", "load_balancer", lb, target).CombinedOutput()
	if err != nil {
		logrus.Errorf("Error in creating load balancer: %v(%v)", string(out), err)
	}
	return err
}

func (ovn *Controller) addEndpoints(ep *kapi.Endpoints) error {
	// get service
	svc, err := ovn.Kube.GetService(ep.Namespace, ep.Name)
	if err != nil {
		return err
	}
	tcpPortMap := make(map[int32]([]string))
	udpPortMap := make(map[int32]([]string))
	for _, s := range ep.Subsets {
		for _, ip := range s.Addresses {
			for _, port := range s.Ports {
				var ips []string
				var portMap map[int32]([]string)
				var ok bool
				if port.Protocol == kapi.ProtocolUDP {
					portMap = udpPortMap
				} else if port.Protocol == kapi.ProtocolTCP {
					portMap = tcpPortMap
				}
				if ips, ok = portMap[port.Port]; !ok {
					ips = make([]string, 0)
				}
				ips = append(ips, ip.IP)
				portMap[port.Port] = ips
			}
		}
	}

	logrus.Debugf("Tcp table: %v\nUdp table: %v", tcpPortMap, udpPortMap)

	for targetPort, ips := range tcpPortMap {
		for _, svcPort := range svc.Spec.Ports {
			if svcPort.Protocol == kapi.ProtocolTCP && svcPort.TargetPort.IntVal == targetPort {
				err = ovn.createLoadBalancerVIP(ovn.getLoadBalancer(svcPort.Protocol), svc.Spec.ClusterIP, svcPort.Port, ips, targetPort)
				if err != nil {
					return err
				}
			}
		}
	}
	for targetPort, ips := range udpPortMap {
		for _, svcPort := range svc.Spec.Ports {
			if svcPort.Protocol == kapi.ProtocolUDP && svcPort.TargetPort.IntVal == targetPort {
				err := ovn.createLoadBalancerVIP(ovn.getLoadBalancer(svcPort.Protocol), svc.Spec.ClusterIP, svcPort.Port, ips, targetPort)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func (ovn *Controller) deleteEndpoints(ep *kapi.Endpoints) error {
	svc, err := ovn.Kube.GetService(ep.Namespace, ep.Name)
	if err != nil {
		// This is not necessarily an error. For e.g when a service is deleted,
		// you will get endpoint delete event and the call to fetch service
		// will fail.
		logrus.Debugf("no service found for endpoint %s in namespace %s",
			ep.Name, ep.Namespace)
		return nil
	}
	for _, svcPort := range svc.Spec.Ports {
		lb := ovn.getLoadBalancer(svcPort.Protocol)
		key := fmt.Sprintf("\"%s:%d\"", svc.Spec.ClusterIP, svcPort.Port)
		_, err := exec.Command(OvnNbctl, "remove", "load_balancer", lb, "vips", key).CombinedOutput()
		if err != nil {
			logrus.Errorf("Error in deleting endpoints: %v", err)
		}
	}
	return nil
}
<commit_msg>go-controller/endpoints: Avoid errors when service does not exist.<commit_after>package ovn

import (
	"fmt"
	"os/exec"
	"strings"
	"unicode"

	"github.com/Sirupsen/logrus"
	kapi "k8s.io/client-go/pkg/api/v1"
)

func (ovn *Controller) getLoadBalancer(protocol kapi.Protocol) string {
	// TODO: add a cache here for the load balancer lookup, so that multiple calls to nbctl can be avoided
	var out []byte
	if protocol == kapi.ProtocolTCP {
		out, _ = exec.Command(OvnNbctl, "--data=bare", "--no-heading",
			"--columns=_uuid", "find", "load_balancer",
			"external_ids:k8s-cluster-lb-tcp=yes").CombinedOutput()
	} else if protocol == kapi.ProtocolUDP {
		out, _ = exec.Command(OvnNbctl, "--data=bare", "--no-heading",
			"--columns=_uuid", "find", "load_balancer",
			"external_ids:k8s-cluster-lb-udp=yes").CombinedOutput()
	}
	outStr := strings.TrimFunc(string(out), unicode.IsSpace)
	return outStr
}

func (ovn *Controller) createLoadBalancerVIP(lb string, serviceIP string, port int32, ips []string, targetPort int32) error {
	logrus.Debugf("Creating lb with %s, %s, %d, [%v], %d", lb, serviceIP, port, ips, targetPort)

	// With service_ip:port as a VIP, create an entry in 'load_balancer'
	// key is of the form "IP:port" (with quotes around)
	key := fmt.Sprintf("\"%s:%d\"", serviceIP, port)

	if len(ips) == 0 {
		_, err := exec.Command(OvnNbctl, "remove", "load_balancer", lb, "vips", key).CombinedOutput()
		return err
	}

	var commaSeparatedEndpoints string
	for i, ep := range ips {
		comma := ","
		if i == 0 {
			comma = ""
		}
		commaSeparatedEndpoints += fmt.Sprintf("%s%s:%d", comma, ep, targetPort)
	}
	target := fmt.Sprintf("vips:\"%s:%d\"=\"%s\"", serviceIP, port, commaSeparatedEndpoints)

	out, err := exec.Command(OvnNbctl, "set", "load_balancer", lb, target).CombinedOutput()
	if err != nil {
		logrus.Errorf("Error in creating load balancer: %v(%v)", string(out), err)
	}
	return err
}

func (ovn *Controller) addEndpoints(ep *kapi.Endpoints) error {
	// get service
	svc, err := ovn.Kube.GetService(ep.Namespace, ep.Name)
	if err != nil {
		// This is not necessarily an error. For e.g when there are endpoints
		// without a corresponding service.
		logrus.Debugf("no service found for endpoint %s in namespace %s",
			ep.Name, ep.Namespace)
		return nil
	}
	tcpPortMap := make(map[int32]([]string))
	udpPortMap := make(map[int32]([]string))
	for _, s := range ep.Subsets {
		for _, ip := range s.Addresses {
			for _, port := range s.Ports {
				var ips []string
				var portMap map[int32]([]string)
				var ok bool
				if port.Protocol == kapi.ProtocolUDP {
					portMap = udpPortMap
				} else if port.Protocol == kapi.ProtocolTCP {
					portMap = tcpPortMap
				}
				if ips, ok = portMap[port.Port]; !ok {
					ips = make([]string, 0)
				}
				ips = append(ips, ip.IP)
				portMap[port.Port] = ips
			}
		}
	}

	logrus.Debugf("Tcp table: %v\nUdp table: %v", tcpPortMap, udpPortMap)

	for targetPort, ips := range tcpPortMap {
		for _, svcPort := range svc.Spec.Ports {
			if svcPort.Protocol == kapi.ProtocolTCP && svcPort.TargetPort.IntVal == targetPort {
				err = ovn.createLoadBalancerVIP(ovn.getLoadBalancer(svcPort.Protocol), svc.Spec.ClusterIP, svcPort.Port, ips, targetPort)
				if err != nil {
					return err
				}
			}
		}
	}
	for targetPort, ips := range udpPortMap {
		for _, svcPort := range svc.Spec.Ports {
			if svcPort.Protocol == kapi.ProtocolUDP && svcPort.TargetPort.IntVal == targetPort {
				err := ovn.createLoadBalancerVIP(ovn.getLoadBalancer(svcPort.Protocol), svc.Spec.ClusterIP, svcPort.Port, ips, targetPort)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func (ovn *Controller) deleteEndpoints(ep *kapi.Endpoints) error {
	svc, err := ovn.Kube.GetService(ep.Namespace, ep.Name)
	if err != nil {
		// This is not necessarily an error. For e.g when a service is deleted,
		// you will get endpoint delete event and the call to fetch service
		// will fail.
		logrus.Debugf("no service found for endpoint %s in namespace %s",
			ep.Name, ep.Namespace)
		return nil
	}
	for _, svcPort := range svc.Spec.Ports {
		lb := ovn.getLoadBalancer(svcPort.Protocol)
		key := fmt.Sprintf("\"%s:%d\"", svc.Spec.ClusterIP, svcPort.Port)
		_, err := exec.Command(OvnNbctl, "remove", "load_balancer", lb, "vips", key).CombinedOutput()
		if err != nil {
			logrus.Errorf("Error in deleting endpoints: %v", err)
		}
	}
	return nil
}
<|endoftext|>