func buildUpstreamName(host string, b interface{}, loc interface{}) string {
	backends, ok := b.([]*ingress.Backend)
	if !ok {
		glog.Errorf("expected an '[]*ingress.Backend' type but %T was returned", b)
		return ""
	}
	location, ok := loc.(*ingress.Location)
	if !ok {
		glog.Errorf("expected a '*ingress.Location' type but %T was returned", loc)
		return ""
	}
	upstreamName := location.Backend
	for _, backend := range backends {
		if backend.Name == location.Backend {
			if backend.SessionAffinity.AffinityType == "cookie" &&
				isSticky(host, location, backend.SessionAffinity.CookieSessionAffinity.Locations) {
				upstreamName = fmt.Sprintf("sticky-%v", upstreamName)
			}
			break
		}
	}
	return upstreamName
}
TODO: Needs Unit Tests
package template
import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"math/rand"
	"net"
	"net/url"
	"os"
	"os/exec"
	"strconv"
	"strings"
	text_template "text/template"
	"time"
	"github.com/golang/glog"
	"github.com/pkg/errors"
	"github.com/NCCloud/fluid/internal/file"
	"github.com/NCCloud/fluid/internal/ingress"
	"github.com/NCCloud/fluid/internal/ingress/annotations/ratelimit"
	"github.com/NCCloud/fluid/internal/ingress/controller/config"
	ing_net "github.com/NCCloud/fluid/internal/net"
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/util/sets"
)
const (
	slash         = "/"
	nonIdempotent = "non_idempotent"
	defBufferSize = 65535
)
type Template struct {
	tmpl *text_template.Template
	bp *BufferPool
}
func NewTemplate(file string, fs file.Filesystem) (*Template, error) {
	data, err := fs.ReadFile(file)
	if err != nil {
		return nil, errors.Wrapf(err, "unexpected error reading template %v", file)
	}
	tmpl, err := text_template.New("nginx.tmpl").Funcs(funcMap).Parse(string(data))
	if err != nil {
		return nil, err
	}
	return &Template{
		tmpl: tmpl,
		bp:   NewBufferPool(defBufferSize),
	}, nil
}
func (t *Template) Write(conf config.TemplateConfig) ([]byte, error) {
	tmplBuf := t.bp.Get()
	defer t.bp.Put(tmplBuf)
	outCmdBuf := t.bp.Get()
	defer t.bp.Put(outCmdBuf)
	if glog.V(3) {
		b, err := json.Marshal(conf)
		if err != nil {
			glog.Errorf("unexpected error: %v", err)
		}
		glog.Infof("NGINX configuration: %v", string(b))
	}
	err := t.tmpl.Execute(tmplBuf, conf)
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("/ingress-controller/clean-nginx-conf.sh")
	cmd.Stdin = tmplBuf
	cmd.Stdout = outCmdBuf
	if err := cmd.Run(); err != nil {
		glog.Warningf("unexpected error cleaning template: %v", err)
		return tmplBuf.Bytes(), nil
	}
	return outCmdBuf.Bytes(), nil
}
var (
	funcMap = text_template.FuncMap{
		"empty": func(input interface{}) bool {
			check, ok := input.(string)
			if ok {
				return len(check) == 0
			}
			return true
		},
		"buildLocation":            buildLocation,
		"buildAuthLocation":        buildAuthLocation,
		"buildAuthResponseHeaders": buildAuthResponseHeaders,
		"filterRateLimits":         filterRateLimits,
		"buildRateLimitZones":      buildRateLimitZones,
		"buildRateLimit":           buildRateLimit,
		"buildResolvers":           buildResolvers,
		"buildUpstreamName":        buildUpstreamName,
		"isLocationInLocationList": isLocationInLocationList,
		"isLocationAllowed":        isLocationAllowed,
		"buildLogFormatUpstream":   buildLogFormatUpstream,
		"buildDenyVariable":        buildDenyVariable,
		"getenv":                   os.Getenv,
		"contains":                 strings.Contains,
		"hasPrefix":                strings.HasPrefix,
		"hasSuffix":                strings.HasSuffix,
		"toUpper":                  strings.ToUpper,
		"toLower":                  strings.ToLower,
		"formatIP":                 formatIP,
		"buildNextUpstream":        buildNextUpstream,
		"getIngressInformation":    getIngressInformation,
		"serverConfig": func(all config.TemplateConfig, server *ingress.Server) interface{} {
			return struct{ First, Second interface{} }{all, server}
		},
		"isValidClientBodyBufferSize": isValidClientBodyBufferSize,
		"buildForwardedFor":           buildForwardedFor,
		"buildAuthSignURL":            buildAuthSignURL,
		"buildOpentracingLoad":        buildOpentracingLoad,
		"buildOpentracing":            buildOpentracing,
	}
)
func formatIP(input string) string {
	ip := net.ParseIP(input)
	if ip == nil {
		return input
	}
	if v4 := ip.To4(); v4 != nil {
		return input
	}
	return fmt.Sprintf("[%s]", input)
}
func buildResolvers(res interface{}, disableIpv6 interface{}) string {
	nss, ok := res.([]net.IP)
	if !ok {
		glog.Errorf("expected a '[]net.IP' type but %T was returned", res)
		return ""
	}
	no6, ok := disableIpv6.(bool)
	if !ok {
		glog.Errorf("expected a 'bool' type but %T was returned", disableIpv6)
		return ""
	}
	if len(nss) == 0 {
		return ""
	}
	r := []string{"resolver"}
	for _, ns := range nss {
		if ing_net.IsIPV6(ns) {
			if no6 {
				continue
			}
			r = append(r, fmt.Sprintf("[%v]", ns))
		} else {
			r = append(r, fmt.Sprintf("%v", ns))
		}
	}
	r = append(r, "valid=30s")
	if no6 {
		r = append(r, "ipv6=off")
	}
	return strings.Join(r, " ") + ";"
}
func buildLocation(input interface{}) string {
	location, ok := input.(*ingress.Location)
	if !ok {
		glog.Errorf("expected an '*ingress.Location' type but %T was returned", input)
		return slash
	}
	path := location.Path
	if len(location.Rewrite.Target) > 0 && location.Rewrite.Target != path {
		if path == slash {
			return fmt.Sprintf("~* %s", path)
		}
		baseuri := `(?<baseuri>.*)`
		if !strings.HasSuffix(path, slash) {
			baseuri = fmt.Sprintf(`\/?%s`, baseuri)
		}
		return fmt.Sprintf(`~* ^%s%s`, path, baseuri)
	}
	return path
}
func buildAuthLocation(input interface{}) string {
	location, ok := input.(*ingress.Location)
	if !ok {
		glog.Errorf("expected an '*ingress.Location' type but %T was returned", input)
		return ""
	}
	if location.ExternalAuth.URL == "" {
		return ""
	}
	str := base64.URLEncoding.EncodeToString([]byte(location.Path))
	str = strings.Replace(str, "=", "", -1)
	return fmt.Sprintf("/_external-auth-%v", str)
}
func buildAuthResponseHeaders(input interface{}) []string {
	location, ok := input.(*ingress.Location)
	res := []string{}
	if !ok {
		glog.Errorf("expected an '*ingress.Location' type but %T was returned", input)
		return res
	}
	if len(location.ExternalAuth.ResponseHeaders) == 0 {
		return res
	}
	for i, h := range location.ExternalAuth.ResponseHeaders {
		hvar := strings.ToLower(h)
		hvar = strings.NewReplacer("-", "_").Replace(hvar)
		res = append(res, fmt.Sprintf("auth_request_set $authHeader%v $upstream_http_%v;", i, hvar))
		res = append(res, fmt.Sprintf("proxy_set_header '%v' $authHeader%v;", h, i))
	}
	return res
}
func buildLogFormatUpstream(input interface{}) string {
	cfg, ok := input.(config.Configuration)
	if !ok {
		glog.Errorf("expected a 'config.Configuration' type but %T was returned", input)
		return ""
	}
	return cfg.BuildLogFormatUpstream()
}
func filterRateLimits(input interface{}) []ratelimit.Config {
	ratelimits := []ratelimit.Config{}
	found := sets.String{}
	servers, ok := input.([]*ingress.Server)
	if !ok {
		glog.Errorf("expected a '[]ratelimit.RateLimit' type but %T was returned", input)
		return ratelimits
	}
	for _, server := range servers {
		for _, loc := range server.Locations {
			if loc.RateLimit.ID != "" && !found.Has(loc.RateLimit.ID) {
				found.Insert(loc.RateLimit.ID)
				ratelimits = append(ratelimits, loc.RateLimit)
			}
		}
	}
	return ratelimits
}
func buildRateLimitZones(input interface{}) []string {
	zones := sets.String{}
	servers, ok := input.([]*ingress.Server)
	if !ok {
		glog.Errorf("expected a '[]*ingress.Server' type but %T was returned", input)
		return zones.List()
	}
	for _, server := range servers {
		for _, loc := range server.Locations {
			if loc.RateLimit.Connections.Limit > 0 {
				zone := fmt.Sprintf("limit_conn_zone $limit_%s zone=%v:%vm;",
					loc.RateLimit.ID,
					loc.RateLimit.Connections.Name,
					loc.RateLimit.Connections.SharedSize)
				if !zones.Has(zone) {
					zones.Insert(zone)
				}
			}
			if loc.RateLimit.RPM.Limit > 0 {
				zone := fmt.Sprintf("limit_req_zone $limit_%s zone=%v:%vm rate=%vr/m;",
					loc.RateLimit.ID,
					loc.RateLimit.RPM.Name,
					loc.RateLimit.RPM.SharedSize,
					loc.RateLimit.RPM.Limit)
				if !zones.Has(zone) {
					zones.Insert(zone)
				}
			}
			if loc.RateLimit.RPS.Limit > 0 {
				zone := fmt.Sprintf("limit_req_zone $limit_%s zone=%v:%vm rate=%vr/s;",
					loc.RateLimit.ID,
					loc.RateLimit.RPS.Name,
					loc.RateLimit.RPS.SharedSize,
					loc.RateLimit.RPS.Limit)
				if !zones.Has(zone) {
					zones.Insert(zone)
				}
			}
		}
	}
	return zones.List()
}
func buildRateLimit(input interface{}) []string {
	limits := []string{}
	loc, ok := input.(*ingress.Location)
	if !ok {
		glog.Errorf("expected an '*ingress.Location' type but %T was returned", input)
		return limits
	}
	if loc.RateLimit.Connections.Limit > 0 {
		limit := fmt.Sprintf("limit_conn %v %v;",
			loc.RateLimit.Connections.Name, loc.RateLimit.Connections.Limit)
		limits = append(limits, limit)
	}
	if loc.RateLimit.RPS.Limit > 0 {
		limit := fmt.Sprintf("limit_req zone=%v burst=%v nodelay;",
			loc.RateLimit.RPS.Name, loc.RateLimit.RPS.Burst)
		limits = append(limits, limit)
	}
	if loc.RateLimit.RPM.Limit > 0 {
		limit := fmt.Sprintf("limit_req zone=%v burst=%v nodelay;",
			loc.RateLimit.RPM.Name, loc.RateLimit.RPM.Burst)
		limits = append(limits, limit)
	}
	if loc.RateLimit.LimitRateAfter > 0 {
		limit := fmt.Sprintf("limit_rate_after %vk;",
			loc.RateLimit.LimitRateAfter)
		limits = append(limits, limit)
	}
	if loc.RateLimit.LimitRate > 0 {
		limit := fmt.Sprintf("limit_rate %vk;",
			loc.RateLimit.LimitRate)
		limits = append(limits, limit)
	}
	return limits
}
func isLocationInLocationList(location interface{}, rawLocationList string) bool {
	loc, ok := location.(*ingress.Location)
	if !ok {
		glog.Errorf("expected an '*ingress.Location' type but %T was returned", location)
		return false
	}
	locationList := strings.Split(rawLocationList, ",")
	for _, locationListItem := range locationList {
		locationListItem = strings.Trim(locationListItem, " ")
		if locationListItem == "" {
			continue
		}
		if strings.HasPrefix(loc.Path, locationListItem) {
			return true
		}
	}
	return false
}
func isLocationAllowed(input interface{}) bool {
	loc, ok := input.(*ingress.Location)
	if !ok {
		glog.Errorf("expected an '*ingress.Location' type but %T was returned", input)
		return false
	}
	return loc.Denied == nil
}
var (
	denyPathSlugMap = map[string]string{}
)
func buildDenyVariable(a interface{}) string {
	l, ok := a.(string)
	if !ok {
		glog.Errorf("expected a 'string' type but %T was returned", a)
		return ""
	}
	if _, ok := denyPathSlugMap[l]; !ok {
		denyPathSlugMap[l] = randomString()
	}
	return fmt.Sprintf("$deny_%v", denyPathSlugMap[l])
}