<commit_before>package aws

import (
	"fmt"
	"log"
	"reflect"
	"strconv"
	"time"

	awssdk "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/elbv2"

	"github.com/gliderlabs/registrator/bridge"
	fargo "github.com/hudl/fargo"
	gocache "github.com/patrickmn/go-cache"
)

// LBInfo represents a ELBv2 endpoint
type LBInfo struct {
	DNSName string
	Port    int64
}

type lookupValues struct {
	InstanceID string
	Port       int64
}

var defExpirationTime = 10 * time.Second
var generalCache = gocache.New(defExpirationTime, defExpirationTime)

type any interface{}

//
// Provide a general caching mechanism for any function that lasts a few seconds.
//
func getAndCache(key string, input any, f any, cacheTime time.Duration) (any, error) {

	vf := reflect.ValueOf(f)
	vinput := reflect.ValueOf(input)

	result, found := generalCache.Get(key)
	if !found {
		//log.Printf("Key %v not cached.  Caching for %v", key, cacheTime)
		caller := vf.Call([]reflect.Value{vinput})
		output := caller[0].Interface()
		err, _ := caller[1].Interface().(error)
		if err == nil {
			generalCache.Set(key, output, cacheTime)
			return output, nil
		}
		return nil, err
	}
	return result, nil
}

// RemoveLBCache : Delete any cache of load balancer for this containerID
func RemoveLBCache(key string) {
	generalCache.Delete(key)
}

// Get a session to AWS API
func getSession() (*elbv2.ELBV2, error) {
	sess, err := session.NewSession()
	if err != nil {
		message := fmt.Errorf("Failed to create session connecting to AWS: %s", err)
		return nil, message
	}

	// Need to set the region here - we'll get it from instance metadata
	awsMetadata := GetMetadata()
	return elbv2.New(sess, awssdk.NewConfig().WithRegion(awsMetadata.Region)), nil
}

// Helper function to retrieve all target groups
func getAllTargetGroups(svc *elbv2.ELBV2) ([]*elbv2.DescribeTargetGroupsOutput, error) {
	var tgs []*elbv2.DescribeTargetGroupsOutput
	var e error
	var mark *string

	// Get first page of groups
	tg, e := getTargetGroupsPage(svc, mark)

	if e != nil {
		return nil, e
	}
	tgs = append(tgs, tg)
	mark = tg.NextMarker

	// Page through all remaining target groups generating a slice of DescribeTargetGroupOutputs
	for mark != nil {
		tg, e = getTargetGroupsPage(svc, mark)
		tgs = append(tgs, tg)
		mark = tg.NextMarker
		if e != nil {
			return nil, e
		}
	}
	return tgs, e
}

// Helper function to get a page of target groups
func getTargetGroupsPage(svc *elbv2.ELBV2, marker *string) (*elbv2.DescribeTargetGroupsOutput, error) {
	params := &elbv2.DescribeTargetGroupsInput{
		PageSize: awssdk.Int64(400),
		Marker:   marker,
	}

	tg, e := svc.DescribeTargetGroups(params)

	if e != nil {
		log.Printf("An error occurred using DescribeTargetGroups: %s \n", e.Error())
		return nil, e
	}
	return tg, nil
}

// GetELBV2ForContainer returns an LBInfo struct with the load balancer DNS name and listener port for a given instanceId and port
// if an error occurs, or the target is not found, an empty LBInfo is returned.
// Pass it the instanceID for the docker host, and the the host port to lookup the associated ELB.
//
func GetELBV2ForContainer(containerID string, instanceID string, port int64) (lbinfo *LBInfo, err error) {
	i := lookupValues{InstanceID: instanceID, Port: port}
	out, err := getAndCache(containerID, i, getLB, gocache.NoExpiration)
	ret, _ := out.(*LBInfo)
	return ret, err
}

//
// Does the real work of retrieving the load balancer details, given a lookupValues struct.
// Note: This function uses caching extensively to reduce the burden on the AWS API when called from multiple goroutines
//
func getLB(l lookupValues) (lbinfo *LBInfo, err error) {
	instanceID := l.InstanceID
	port := l.Port

	var lbArns []*string
	var lbPort *int64
	var tgArn string
	info := &LBInfo{}

	svc, err := getSession()
	if err != nil {
		return nil, err
	}

	// TODO Note: There could be thousands of these, and we need to check them all.  Seems to be no
	// other way to retrieve a TG via instance/port with current API

	out1, err := getAndCache("tg", svc, getAllTargetGroups, defExpirationTime)
	tgslice := out1.([]*elbv2.DescribeTargetGroupsOutput)
	if err != nil || tgslice == nil {
		message := fmt.Errorf("Failed to retrieve Target Groups: %s", err)
		return nil, message
	}

	// Check each target group's target list for a matching port and instanceID
	// Assumption: that that there is only one LB for the target group (though the data structure allows more)
	for _, tgs := range tgslice {
		for _, tg := range tgs.TargetGroups {

			thParams := &elbv2.DescribeTargetHealthInput{
				TargetGroupArn: awssdk.String(*tg.TargetGroupArn),
			}

			out2, err := getAndCache(*thParams.TargetGroupArn, thParams, svc.DescribeTargetHealth, defExpirationTime)
			tarH := out2.(*elbv2.DescribeTargetHealthOutput)
			if err != nil {
				log.Printf("An error occurred using DescribeTargetHealth: %s \n", err.Error())
				return nil, err
			}

			for _, thd := range tarH.TargetHealthDescriptions {
				if *thd.Target.Port == port && *thd.Target.Id == instanceID {
					lbArns = tg.LoadBalancerArns
					tgArn = *tg.TargetGroupArn
					break
				}
			}
		}
		if lbArns != nil && tgArn != "" {
			break
		}
	}

	if err != nil || lbArns == nil {
		message := fmt.Errorf("failed to retrieve load balancer ARN")
		return nil, message
	}

	// Loop through the load balancer listeners to get the listener port for the target group
	lsnrParams := &elbv2.DescribeListenersInput{
		LoadBalancerArn: lbArns[0],
	}
	out3, err := getAndCache("lsnr_"+*lsnrParams.LoadBalancerArn, lsnrParams, svc.DescribeListeners, defExpirationTime)
	if err != nil {
		log.Printf("An error occurred using DescribeListeners: %s \n", err.Error())
		return nil, err
	}
	lnrData := out3.(*elbv2.DescribeListenersOutput)
	for _, listener := range lnrData.Listeners {
		for _, act := range listener.DefaultActions {
			if *act.TargetGroupArn == tgArn {
				log.Printf("Found matching listener: %v", *listener.ListenerArn)
				lbPort = listener.Port
				break
			}
		}
	}
	if lbPort == nil {
		message := fmt.Errorf("error: Unable to identify listener port for ELBv2")
		return nil, message
	}

	// Get more information on the load balancer to retrieve the DNSName
	lbParams := &elbv2.DescribeLoadBalancersInput{
		LoadBalancerArns: lbArns,
	}
	out4, err := getAndCache("lb_"+*lbParams.LoadBalancerArns[0], lbParams, svc.DescribeLoadBalancers, defExpirationTime)
	if err != nil {
		log.Printf("An error occurred using DescribeLoadBalancers: %s \n", err.Error())
		return nil, err
	}
	lbData := out4.(*elbv2.DescribeLoadBalancersOutput)
	log.Printf("LB Endpoint for Instance:%v Port:%v, Target Group:%v, is: %s:%s\n", instanceID, port, tgArn, *lbData.LoadBalancers[0].DNSName, strconv.FormatInt(*lbPort, 10))

	info.DNSName = *lbData.LoadBalancers[0].DNSName
	info.Port = *lbPort
	return info, nil
}

// CheckELBFlags - Helper function to check if the correct config flags are set to use ELBs
// We accept two possible configurations here - either eureka_lookup_elbv2_endpoint can be set,
// for automatic lookup, or eureka_elbv2_hostname and eureka_elbv2_port can be set manually
// to avoid the 10-20s wait for lookups
func CheckELBFlags(service *bridge.Service) bool {

	isAws := service.Attrs["eureka_datacenterinfo_name"] != fargo.MyOwn
	var hasExplicit bool
	var useLookup bool

	if service.Attrs["eureka_elbv2_hostname"] != "" && service.Attrs["eureka_elbv2_port"] != "" {
		v, err := strconv.ParseUint(service.Attrs["eureka_elbv2_port"], 10, 16)
		if err != nil {
			log.Printf("eureka_elbv2_port must be valid 16-bit unsigned int, was %v : %s", v, err)
			hasExplicit = false
		}
		hasExplicit = true
		useLookup = true
	}

	if service.Attrs["eureka_lookup_elbv2_endpoint"] != "" {
		v, err := strconv.ParseBool(service.Attrs["eureka_lookup_elbv2_endpoint"])
		if err != nil {
			log.Printf("eureka_lookup_elbv2_endpoint must be valid boolean, was %v : %s", v, err)
			useLookup = false
		}
		useLookup = v
	}

	if (hasExplicit || useLookup) && isAws {
		return true
	}
	return false
}

// CheckELBOnlyReg - Helper function to check if only the ELB should be registered (no containers)
func CheckELBOnlyReg(service *bridge.Service) bool {

	if service.Attrs["eureka_elbv2_only_registration"] != "" {
		v, err := strconv.ParseBool(service.Attrs["eureka_elbv2_only_registration"])
		if err != nil {
			log.Printf("eureka_elbv2_only_registration must be valid boolean, was %v : %s", v, err)
			return true
		}
		return v
	}
	return true
}

// GetUniqueID Note: Helper function reimplemented here to avoid segfault calling it on fargo.Instance struct
func GetUniqueID(instance fargo.Instance) string {
	return instance.HostName + "_" + strconv.Itoa(instance.Port)
}

// Helper function to alter registration info and add the ELBv2 endpoint
// useCache parameter is passed to getELBV2ForContainer
func setRegInfo(service *bridge.Service, registration *fargo.Instance) *fargo.Instance {

	awsMetadata := GetMetadata()
	var elbEndpoint string

	// We've been given the ELB endpoint, so use this
	if service.Attrs["eureka_elbv2_hostname"] != "" && service.Attrs["eureka_elbv2_port"] != "" {
		log.Printf("found ELBv2 hostname=%v and port=%v options, using these.", service.Attrs["eureka_elbv2_hostname"], service.Attrs["eureka_elbv2_port"])
		registration.Port, _ = strconv.Atoi(service.Attrs["eureka_elbv2_port"])
		registration.HostName = service.Attrs["eureka_elbv2_hostname"]
		registration.IPAddr = ""
		registration.VipAddress = ""
		elbEndpoint = service.Attrs["eureka_elbv2_hostname"] + "_" + service.Attrs["eureka_elbv2_port"]

	} else {
		// We don't have the ELB endpoint, so look it up.
		elbMetadata, err := GetELBV2ForContainer(service.Origin.ContainerID, awsMetadata.InstanceID, int64(registration.Port))

		if err != nil {
			log.Printf("Unable to find associated ELBv2 for: %s, Error: %s\n", registration.HostName, err)
			return nil
		}

		elbStrPort := strconv.FormatInt(elbMetadata.Port, 10)
		elbEndpoint = elbMetadata.DNSName + "_" + elbStrPort
		registration.Port = int(elbMetadata.Port)
		registration.IPAddr = ""
		registration.HostName = elbMetadata.DNSName
	}

	if CheckELBOnlyReg(service) {
		// Remove irrelevant metadata from an ELB only registration
		registration.DataCenterInfo.Metadata = fargo.AmazonMetadataType{
			InstanceID:     GetUniqueID(*registration), // This is deliberate - due to limitations in uniqueIDs
			PublicHostname: registration.HostName,
			HostName:       registration.HostName,
		}
		registration.SetMetadataString("container-id", "")
		registration.SetMetadataString("container-name", "")
		registration.SetMetadataString("aws-instance-id", "")
	}

	registration.SetMetadataString("has-elbv2", "true")
	registration.SetMetadataString("elbv2-endpoint", elbEndpoint)
	registration.VipAddress = registration.IPAddr
	return registration
}

// RegisterWithELBv2 - If called, and flags are active, register an ELBv2 endpoint instead of the container directly
// This will mean traffic is directed to the ALB rather than directly to containers
func RegisterWithELBv2(service *bridge.Service, registration *fargo.Instance, client fargo.EurekaConnection) error {
	if CheckELBFlags(service) {
		log.Printf("Found ELBv2 flags, will attempt to register LB for: %s\n", GetUniqueID(*registration))
		elbReg := setRegInfo(service, registration)
		if elbReg != nil {
			err := client.ReregisterInstance(elbReg)
			return err
		}
		for i := 1; i == 3; i++ {
			// If there's no ELBv2 data, we need to retry a couple of times, as it takes a little while to propogate target group membership
			// To avoid any wait, the endpoints can be specified manually as eureka_elbv2_hostname and eureka_elbv2_port vars
			period := (time.Second * time.Duration(defExpirationTime+1) * time.Duration(i))
			log.Printf("Retrying retrieval of ELBv2 data, attempt %v/3 - Waiting for %v seconds", i, period)
			time.Sleep(period)
			elbReg = setRegInfo(service, registration)
			if elbReg != nil {
				err := client.ReregisterInstance(elbReg)
				return err
			}
		}
	}
	return fmt.Errorf("[%v] unable to register ELBv2", service.Origin.ContainerID)
}

// HeartbeatELBv2 - Heartbeat an ELB registration
func HeartbeatELBv2(service *bridge.Service, registration *fargo.Instance, client fargo.EurekaConnection) error {
	if CheckELBFlags(service) {
		log.Printf("Heartbeating ELBv2: %s\n", GetUniqueID(*registration))
		elbReg := setRegInfo(service, registration)
		if elbReg != nil {
			err := client.HeartBeatInstance(elbReg)
			return err
		}
	}
	return fmt.Errorf("unable to heartbeat ELBv2. %s", GetUniqueID(*registration))
}
<commit_msg>Make more type assertions safe<commit_after>package aws

import (
	"fmt"
	"log"
	"reflect"
	"strconv"
	"time"

	awssdk "github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/elbv2"

	"github.com/gliderlabs/registrator/bridge"
	fargo "github.com/hudl/fargo"
	gocache "github.com/patrickmn/go-cache"
)

// LBInfo represents a ELBv2 endpoint
type LBInfo struct {
	DNSName string
	Port    int64
}

type lookupValues struct {
	InstanceID string
	Port       int64
}

var defExpirationTime = 10 * time.Second
var generalCache = gocache.New(defExpirationTime, defExpirationTime)

type any interface{}

//
// Provide a general caching mechanism for any function that lasts a few seconds.
//
func getAndCache(key string, input any, f any, cacheTime time.Duration) (any, error) {

	vf := reflect.ValueOf(f)
	vinput := reflect.ValueOf(input)

	result, found := generalCache.Get(key)
	if !found {
		//log.Printf("Key %v not cached.  Caching for %v", key, cacheTime)
		caller := vf.Call([]reflect.Value{vinput})
		output := caller[0].Interface()
		err, _ := caller[1].Interface().(error)
		if err == nil {
			generalCache.Set(key, output, cacheTime)
			return output, nil
		}
		return nil, err
	}
	return result, nil
}

// RemoveLBCache : Delete any cache of load balancer for this containerID
func RemoveLBCache(key string) {
	generalCache.Delete(key)
}

// Get a session to AWS API
func getSession() (*elbv2.ELBV2, error) {
	sess, err := session.NewSession()
	if err != nil {
		message := fmt.Errorf("Failed to create session connecting to AWS: %s", err)
		return nil, message
	}

	// Need to set the region here - we'll get it from instance metadata
	awsMetadata := GetMetadata()
	return elbv2.New(sess, awssdk.NewConfig().WithRegion(awsMetadata.Region)), nil
}

// Helper function to retrieve all target groups
func getAllTargetGroups(svc *elbv2.ELBV2) ([]*elbv2.DescribeTargetGroupsOutput, error) {
	var tgs []*elbv2.DescribeTargetGroupsOutput
	var e error
	var mark *string

	// Get first page of groups
	tg, e := getTargetGroupsPage(svc, mark)

	if e != nil {
		return nil, e
	}
	tgs = append(tgs, tg)
	mark = tg.NextMarker

	// Page through all remaining target groups generating a slice of DescribeTargetGroupOutputs
	for mark != nil {
		tg, e = getTargetGroupsPage(svc, mark)
		tgs = append(tgs, tg)
		mark = tg.NextMarker
		if e != nil {
			return nil, e
		}
	}
	return tgs, e
}

// Helper function to get a page of target groups
func getTargetGroupsPage(svc *elbv2.ELBV2, marker *string) (*elbv2.DescribeTargetGroupsOutput, error) {
	params := &elbv2.DescribeTargetGroupsInput{
		PageSize: awssdk.Int64(400),
		Marker:   marker,
	}

	tg, e := svc.DescribeTargetGroups(params)

	if e != nil {
		log.Printf("An error occurred using DescribeTargetGroups: %s \n", e.Error())
		return nil, e
	}
	return tg, nil
}

// GetELBV2ForContainer returns an LBInfo struct with the load balancer DNS name and listener port for a given instanceId and port
// if an error occurs, or the target is not found, an empty LBInfo is returned.
// Pass it the instanceID for the docker host, and the the host port to lookup the associated ELB.
//
func GetELBV2ForContainer(containerID string, instanceID string, port int64) (lbinfo *LBInfo, err error) {
	i := lookupValues{InstanceID: instanceID, Port: port}
	out, err := getAndCache(containerID, i, getLB, gocache.NoExpiration)
	ret, _ := out.(*LBInfo)
	return ret, err
}

//
// Does the real work of retrieving the load balancer details, given a lookupValues struct.
// Note: This function uses caching extensively to reduce the burden on the AWS API when called from multiple goroutines
//
func getLB(l lookupValues) (lbinfo *LBInfo, err error) {
	instanceID := l.InstanceID
	port := l.Port

	var lbArns []*string
	var lbPort *int64
	var tgArn string
	info := &LBInfo{}

	svc, err := getSession()
	if err != nil {
		return nil, err
	}

	// TODO Note: There could be thousands of these, and we need to check them all.  Seems to be no
	// other way to retrieve a TG via instance/port with current API

	out1, err := getAndCache("tg", svc, getAllTargetGroups, defExpirationTime)
	if err != nil || tgslice == nil {
		message := fmt.Errorf("Failed to retrieve Target Groups: %s", err)
		return nil, message
	}
	tgslice, _ := out1.([]*elbv2.DescribeTargetGroupsOutput)

	// Check each target group's target list for a matching port and instanceID
	// Assumption: that that there is only one LB for the target group (though the data structure allows more)
	for _, tgs := range tgslice {
		for _, tg := range tgs.TargetGroups {

			thParams := &elbv2.DescribeTargetHealthInput{
				TargetGroupArn: awssdk.String(*tg.TargetGroupArn),
			}

			out2, err := getAndCache(*thParams.TargetGroupArn, thParams, svc.DescribeTargetHealth, defExpirationTime)
			if err != nil || out2 == nil {
				log.Printf("An error occurred using DescribeTargetHealth: %s \n", err.Error())
				return nil, err
			}
			tarH, _ := out2.(*elbv2.DescribeTargetHealthOutput)
			for _, thd := range tarH.TargetHealthDescriptions {
				if *thd.Target.Port == port && *thd.Target.Id == instanceID {
					lbArns = tg.LoadBalancerArns
					tgArn = *tg.TargetGroupArn
					break
				}
			}
		}
		if lbArns != nil && tgArn != "" {
			break
		}
	}

	if err != nil || lbArns == nil {
		message := fmt.Errorf("failed to retrieve load balancer ARN")
		return nil, message
	}

	// Loop through the load balancer listeners to get the listener port for the target group
	lsnrParams := &elbv2.DescribeListenersInput{
		LoadBalancerArn: lbArns[0],
	}
	out3, err := getAndCache("lsnr_"+*lsnrParams.LoadBalancerArn, lsnrParams, svc.DescribeListeners, defExpirationTime)
	if err != nil || out3 == nil {
		log.Printf("An error occurred using DescribeListeners: %s \n", err.Error())
		return nil, err
	}
	lnrData, _ := out3.(*elbv2.DescribeListenersOutput)
	for _, listener := range lnrData.Listeners {
		for _, act := range listener.DefaultActions {
			if *act.TargetGroupArn == tgArn {
				log.Printf("Found matching listener: %v", *listener.ListenerArn)
				lbPort = listener.Port
				break
			}
		}
	}
	if lbPort == nil {
		message := fmt.Errorf("error: Unable to identify listener port for ELBv2")
		return nil, message
	}

	// Get more information on the load balancer to retrieve the DNSName
	lbParams := &elbv2.DescribeLoadBalancersInput{
		LoadBalancerArns: lbArns,
	}
	out4, err := getAndCache("lb_"+*lbParams.LoadBalancerArns[0], lbParams, svc.DescribeLoadBalancers, defExpirationTime)
	if err != nil || out4 == nil {
		log.Printf("An error occurred using DescribeLoadBalancers: %s \n", err.Error())
		return nil, err
	}
	lbData, _ := out4.(*elbv2.DescribeLoadBalancersOutput)
	log.Printf("LB Endpoint for Instance:%v Port:%v, Target Group:%v, is: %s:%s\n", instanceID, port, tgArn, *lbData.LoadBalancers[0].DNSName, strconv.FormatInt(*lbPort, 10))

	info.DNSName = *lbData.LoadBalancers[0].DNSName
	info.Port = *lbPort
	return info, nil
}

// CheckELBFlags - Helper function to check if the correct config flags are set to use ELBs
// We accept two possible configurations here - either eureka_lookup_elbv2_endpoint can be set,
// for automatic lookup, or eureka_elbv2_hostname and eureka_elbv2_port can be set manually
// to avoid the 10-20s wait for lookups
func CheckELBFlags(service *bridge.Service) bool {

	isAws := service.Attrs["eureka_datacenterinfo_name"] != fargo.MyOwn
	var hasExplicit bool
	var useLookup bool

	if service.Attrs["eureka_elbv2_hostname"] != "" && service.Attrs["eureka_elbv2_port"] != "" {
		v, err := strconv.ParseUint(service.Attrs["eureka_elbv2_port"], 10, 16)
		if err != nil {
			log.Printf("eureka_elbv2_port must be valid 16-bit unsigned int, was %v : %s", v, err)
			hasExplicit = false
		}
		hasExplicit = true
		useLookup = true
	}

	if service.Attrs["eureka_lookup_elbv2_endpoint"] != "" {
		v, err := strconv.ParseBool(service.Attrs["eureka_lookup_elbv2_endpoint"])
		if err != nil {
			log.Printf("eureka_lookup_elbv2_endpoint must be valid boolean, was %v : %s", v, err)
			useLookup = false
		}
		useLookup = v
	}

	if (hasExplicit || useLookup) && isAws {
		return true
	}
	return false
}

// CheckELBOnlyReg - Helper function to check if only the ELB should be registered (no containers)
func CheckELBOnlyReg(service *bridge.Service) bool {

	if service.Attrs["eureka_elbv2_only_registration"] != "" {
		v, err := strconv.ParseBool(service.Attrs["eureka_elbv2_only_registration"])
		if err != nil {
			log.Printf("eureka_elbv2_only_registration must be valid boolean, was %v : %s", v, err)
			return true
		}
		return v
	}
	return true
}

// GetUniqueID Note: Helper function reimplemented here to avoid segfault calling it on fargo.Instance struct
func GetUniqueID(instance fargo.Instance) string {
	return instance.HostName + "_" + strconv.Itoa(instance.Port)
}

// Helper function to alter registration info and add the ELBv2 endpoint
// useCache parameter is passed to getELBV2ForContainer
func setRegInfo(service *bridge.Service, registration *fargo.Instance) *fargo.Instance {

	awsMetadata := GetMetadata()
	var elbEndpoint string

	// We've been given the ELB endpoint, so use this
	if service.Attrs["eureka_elbv2_hostname"] != "" && service.Attrs["eureka_elbv2_port"] != "" {
		log.Printf("found ELBv2 hostname=%v and port=%v options, using these.", service.Attrs["eureka_elbv2_hostname"], service.Attrs["eureka_elbv2_port"])
		registration.Port, _ = strconv.Atoi(service.Attrs["eureka_elbv2_port"])
		registration.HostName = service.Attrs["eureka_elbv2_hostname"]
		registration.IPAddr = ""
		registration.VipAddress = ""
		elbEndpoint = service.Attrs["eureka_elbv2_hostname"] + "_" + service.Attrs["eureka_elbv2_port"]

	} else {
		// We don't have the ELB endpoint, so look it up.
		elbMetadata, err := GetELBV2ForContainer(service.Origin.ContainerID, awsMetadata.InstanceID, int64(registration.Port))

		if err != nil {
			log.Printf("Unable to find associated ELBv2 for: %s, Error: %s\n", registration.HostName, err)
			return nil
		}

		elbStrPort := strconv.FormatInt(elbMetadata.Port, 10)
		elbEndpoint = elbMetadata.DNSName + "_" + elbStrPort
		registration.Port = int(elbMetadata.Port)
		registration.IPAddr = ""
		registration.HostName = elbMetadata.DNSName
	}

	if CheckELBOnlyReg(service) {
		// Remove irrelevant metadata from an ELB only registration
		registration.DataCenterInfo.Metadata = fargo.AmazonMetadataType{
			InstanceID:     GetUniqueID(*registration), // This is deliberate - due to limitations in uniqueIDs
			PublicHostname: registration.HostName,
			HostName:       registration.HostName,
		}
		registration.SetMetadataString("container-id", "")
		registration.SetMetadataString("container-name", "")
		registration.SetMetadataString("aws-instance-id", "")
	}

	registration.SetMetadataString("has-elbv2", "true")
	registration.SetMetadataString("elbv2-endpoint", elbEndpoint)
	registration.VipAddress = registration.IPAddr
	return registration
}

// RegisterWithELBv2 - If called, and flags are active, register an ELBv2 endpoint instead of the container directly
// This will mean traffic is directed to the ALB rather than directly to containers
func RegisterWithELBv2(service *bridge.Service, registration *fargo.Instance, client fargo.EurekaConnection) error {
	if CheckELBFlags(service) {
		log.Printf("Found ELBv2 flags, will attempt to register LB for: %s\n", GetUniqueID(*registration))
		elbReg := setRegInfo(service, registration)
		if elbReg != nil {
			err := client.ReregisterInstance(elbReg)
			return err
		}
		for i := 1; i == 3; i++ {
			// If there's no ELBv2 data, we need to retry a couple of times, as it takes a little while to propogate target group membership
			// To avoid any wait, the endpoints can be specified manually as eureka_elbv2_hostname and eureka_elbv2_port vars
			period := (time.Second * time.Duration(defExpirationTime+1) * time.Duration(i))
			log.Printf("Retrying retrieval of ELBv2 data, attempt %v/3 - Waiting for %v seconds", i, period)
			time.Sleep(period)
			elbReg = setRegInfo(service, registration)
			if elbReg != nil {
				err := client.ReregisterInstance(elbReg)
				return err
			}
		}
	}
	return fmt.Errorf("[%v] unable to register ELBv2", service.Origin.ContainerID)
}

// HeartbeatELBv2 - Heartbeat an ELB registration
func HeartbeatELBv2(service *bridge.Service, registration *fargo.Instance, client fargo.EurekaConnection) error {
	if CheckELBFlags(service) {
		log.Printf("Heartbeating ELBv2: %s\n", GetUniqueID(*registration))
		elbReg := setRegInfo(service, registration)
		if elbReg != nil {
			err := client.HeartBeatInstance(elbReg)
			return err
		}
	}
	return fmt.Errorf("unable to heartbeat ELBv2. %s", GetUniqueID(*registration))
}
<|endoftext|>


<commit_before>package fastly

import (
	"fmt"
	"sort"
)

// Backend represents a backend response from the Fastly API.
type Backend struct {
	ServiceID string `mapstructure:"service_id"`
	Version   string `mapstructure:"version"`

	Name                string   `mapstructure:"name"`
	Address             string   `mapstructure:"address"`
	Port                uint     `mapstructure:"port"`
	ConnectTimeout      uint     `mapstructure:"connect_timeout"`
	MaxConn             uint     `mapstructure:"max_conn"`
	ErrorThreshold      uint     `mapstructure:"error_threshold"`
	FirstByteTimeout    uint     `mapstructure:"first_byte_timeout"`
	BetweenBytesTimeout uint     `mapstructure:"between_bytes_timeout"`
	AutoLoadbalance     bool     `mapstructure:"auto_loadbalance"`
	Weight              uint     `mapstructure:"weight"`
	RequestCondition    string   `mapstructure:"request_condition"`
	HealthCheck         string   `mapstructure:"healthcheck"`
	Hostname            string   `mapstructure:"hostname"`
	Shield              string   `mapstructure:"shield"`
	UseSSL              bool     `mapstructure:"use_ssl"`
	SSLCheckCert        bool     `mapstructure:"ssl_check_cert"`
	SSLHostname         string   `mapstructure:"ssl_hostname"`
	SSLCertHostname     string   `mapstructure:"ssl_cert_hostname"`
	SSLSNIHostname      string   `mapstructure:"ssl_sni_hostname"`
	MinTLSVersion       string   `mapstructure:"min_tls_version"`
	MaxTLSVersion       string   `mapstructure:"max_tls_version"`
	SSLCiphers          []string `mapstructure:"ssl_ciphers"`
}

// backendsByName is a sortable list of backends.
type backendsByName []*Backend

// Len, Swap, and Less implement the sortable interface.
func (s backendsByName) Len() int      { return len(s) }
func (s backendsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s backendsByName) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

// ListBackendsInput is used as input to the ListBackends function.
type ListBackendsInput struct {
	// Service is the ID of the service (required).
	Service string

	// Version is the specific configuration version (required).
	Version string
}

// ListBackends returns the list of backends for the configuration version.
func (c *Client) ListBackends(i *ListBackendsInput) ([]*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var bs []*Backend
	if err := decodeJSON(&bs, resp.Body); err != nil {
		return nil, err
	}
	sort.Stable(backendsByName(bs))
	return bs, nil
}

// CreateBackendInput is used as input to the CreateBackend function.
type CreateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	Name                string       `form:"name,omitempty"`
	Address             string       `form:"address,omitempty"`
	Port                uint         `form:"port,omitempty"`
	ConnectTimeout      uint         `form:"connect_timeout,omitempty"`
	MaxConn             uint         `form:"max_conn,omitempty"`
	ErrorThreshold      uint         `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint         `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint         `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     *Compatibool `form:"auto_loadbalance,omitempty"`
	Weight              uint         `form:"weight,omitempty"`
	RequestCondition    string       `form:"request_condition,omitempty"`
	HealthCheck         string       `form:"healthcheck,omitempty"`
	Shield              string       `form:"shield,omitempty"`
	UseSSL              *Compatibool `form:"use_ssl,omitempty"`
	SSLCheckCert        *Compatibool `form:"ssl_check_cert,omitempty"`
	SSLHostname         string       `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string       `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string       `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string       `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string       `form:"max_tls_version,omitempty"`
	SSLCiphers          []string     `form:"ssl_ciphers,omitempty"`
}

// CreateBackend creates a new Fastly backend.
func (c *Client) CreateBackend(i *CreateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.PostForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// GetBackendInput is used as input to the GetBackend function.
type GetBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to fetch.
	Name string
}

// GetBackend gets the backend configuration with the given parameters.
func (c *Client) GetBackend(i *GetBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// UpdateBackendInput is used as input to the UpdateBackend function.
type UpdateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to update.
	Name string

	NewName             string       `form:"name,omitempty"`
	Address             string       `form:"address,omitempty"`
	Port                uint         `form:"port,omitempty"`
	ConnectTimeout      uint         `form:"connect_timeout,omitempty"`
	MaxConn             uint         `form:"max_conn,omitempty"`
	ErrorThreshold      uint         `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint         `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint         `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     *Compatibool `form:"auto_loadbalance,omitempty"`
	Weight              uint         `form:"weight,omitempty"`
	RequestCondition    string       `form:"request_condition,omitempty"`
	HealthCheck         string       `form:"healthcheck,omitempty"`
	Shield              string       `form:"shield,omitempty"`
	UseSSL              *Compatibool `form:"use_ssl,omitempty"`
	SSLCheckCert        *Compatibool `form:"ssl_check_cert,omitempty"`
	SSLHostname         string       `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string       `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string       `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string       `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string       `form:"max_tls_version,omitempty"`
	SSLCiphers          []string     `form:"ssl_ciphers,omitempty"`
}

// UpdateBackend updates a specific backend.
func (c *Client) UpdateBackend(i *UpdateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.PutForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// DeleteBackendInput is the input parameter to DeleteBackend.
type DeleteBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to delete (required).
	Name string
}

// DeleteBackend deletes the given backend version.
func (c *Client) DeleteBackend(i *DeleteBackendInput) error {
	if i.Service == "" {
		return ErrMissingService
	}

	if i.Version == "" {
		return ErrMissingVersion
	}

	if i.Name == "" {
		return ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Delete(path, nil)
	if err != nil {
		return err
	}

	var r *statusResp
	if err := decodeJSON(&r, resp.Body); err != nil {
		return err
	}
	if !r.Ok() {
		return fmt.Errorf("Not Ok")
	}
	return nil
}
<commit_msg>Add additional SSL fields to backend<commit_after>package fastly

import (
	"fmt"
	"sort"
)

// Backend represents a backend response from the Fastly API.
type Backend struct {
	ServiceID string `mapstructure:"service_id"`
	Version   string `mapstructure:"version"`

	Name                string   `mapstructure:"name"`
	Address             string   `mapstructure:"address"`
	Port                uint     `mapstructure:"port"`
	ConnectTimeout      uint     `mapstructure:"connect_timeout"`
	MaxConn             uint     `mapstructure:"max_conn"`
	ErrorThreshold      uint     `mapstructure:"error_threshold"`
	FirstByteTimeout    uint     `mapstructure:"first_byte_timeout"`
	BetweenBytesTimeout uint     `mapstructure:"between_bytes_timeout"`
	AutoLoadbalance     bool     `mapstructure:"auto_loadbalance"`
	Weight              uint     `mapstructure:"weight"`
	RequestCondition    string   `mapstructure:"request_condition"`
	HealthCheck         string   `mapstructure:"healthcheck"`
	Hostname            string   `mapstructure:"hostname"`
	Shield              string   `mapstructure:"shield"`
	UseSSL              bool     `mapstructure:"use_ssl"`
	SSLCheckCert        bool     `mapstructure:"ssl_check_cert"`
	SSLCACert           string   `mapstructure:"ssl_ca_cert"`
	SSLClientCert       string   `mapstructure:"ssl_client_cert"`
	SSLClientKey        string   `mapstructure:"ssl_client_key"`
	SSLHostname         string   `mapstructure:"ssl_hostname"`
	SSLCertHostname     string   `mapstructure:"ssl_cert_hostname"`
	SSLSNIHostname      string   `mapstructure:"ssl_sni_hostname"`
	MinTLSVersion       string   `mapstructure:"min_tls_version"`
	MaxTLSVersion       string   `mapstructure:"max_tls_version"`
	SSLCiphers          []string `mapstructure:"ssl_ciphers"`
}

// backendsByName is a sortable list of backends.
type backendsByName []*Backend

// Len, Swap, and Less implement the sortable interface.
func (s backendsByName) Len() int      { return len(s) }
func (s backendsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s backendsByName) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

// ListBackendsInput is used as input to the ListBackends function.
type ListBackendsInput struct {
	// Service is the ID of the service (required).
	Service string

	// Version is the specific configuration version (required).
	Version string
}

// ListBackends returns the list of backends for the configuration version.
func (c *Client) ListBackends(i *ListBackendsInput) ([]*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var bs []*Backend
	if err := decodeJSON(&bs, resp.Body); err != nil {
		return nil, err
	}
	sort.Stable(backendsByName(bs))
	return bs, nil
}

// CreateBackendInput is used as input to the CreateBackend function.
type CreateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	Name                string       `form:"name,omitempty"`
	Address             string       `form:"address,omitempty"`
	Port                uint         `form:"port,omitempty"`
	ConnectTimeout      uint         `form:"connect_timeout,omitempty"`
	MaxConn             uint         `form:"max_conn,omitempty"`
	ErrorThreshold      uint         `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint         `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint         `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     *Compatibool `form:"auto_loadbalance,omitempty"`
	Weight              uint         `form:"weight,omitempty"`
	RequestCondition    string       `form:"request_condition,omitempty"`
	HealthCheck         string       `form:"healthcheck,omitempty"`
	Shield              string       `form:"shield,omitempty"`
	UseSSL              *Compatibool `form:"use_ssl,omitempty"`
	SSLCheckCert        *Compatibool `form:"ssl_check_cert,omitempty"`
	SSLCACert           string       `form:"ssl_ca_cert,omitempty"`
	SSLClientCert       string       `form:"ssl_client_cert,omitempty"`
	SSLClientKey        string       `form:"ssl_client_key,omitempty"`
	SSLHostname         string       `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string       `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string       `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string       `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string       `form:"max_tls_version,omitempty"`
	SSLCiphers          []string     `form:"ssl_ciphers,omitempty"`
}

// CreateBackend creates a new Fastly backend.
func (c *Client) CreateBackend(i *CreateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend", i.Service, i.Version)
	resp, err := c.PostForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// GetBackendInput is used as input to the GetBackend function.
type GetBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to fetch.
	Name string
}

// GetBackend gets the backend configuration with the given parameters.
func (c *Client) GetBackend(i *GetBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Get(path, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// UpdateBackendInput is used as input to the UpdateBackend function.
type UpdateBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to update.
	Name string

	NewName             string       `form:"name,omitempty"`
	Address             string       `form:"address,omitempty"`
	Port                uint         `form:"port,omitempty"`
	ConnectTimeout      uint         `form:"connect_timeout,omitempty"`
	MaxConn             uint         `form:"max_conn,omitempty"`
	ErrorThreshold      uint         `form:"error_threshold,omitempty"`
	FirstByteTimeout    uint         `form:"first_byte_timeout,omitempty"`
	BetweenBytesTimeout uint         `form:"between_bytes_timeout,omitempty"`
	AutoLoadbalance     *Compatibool `form:"auto_loadbalance,omitempty"`
	Weight              uint         `form:"weight,omitempty"`
	RequestCondition    string       `form:"request_condition,omitempty"`
	HealthCheck         string       `form:"healthcheck,omitempty"`
	Shield              string       `form:"shield,omitempty"`
	UseSSL              *Compatibool `form:"use_ssl,omitempty"`
	SSLCheckCert        *Compatibool `form:"ssl_check_cert,omitempty"`
	SSLCACert           string       `form:"ssl_ca_cert,omitempty"`
	SSLClientCert       string       `form:"ssl_client_cert,omitempty"`
	SSLClientKey        string       `form:"ssl_client_key,omitempty"`
	SSLHostname         string       `form:"ssl_hostname,omitempty"`
	SSLCertHostname     string       `form:"ssl_cert_hostname,omitempty"`
	SSLSNIHostname      string       `form:"ssl_sni_hostname,omitempty"`
	MinTLSVersion       string       `form:"min_tls_version,omitempty"`
	MaxTLSVersion       string       `form:"max_tls_version,omitempty"`
	SSLCiphers          []string     `form:"ssl_ciphers,omitempty"`
}

// UpdateBackend updates a specific backend.
func (c *Client) UpdateBackend(i *UpdateBackendInput) (*Backend, error) {
	if i.Service == "" {
		return nil, ErrMissingService
	}

	if i.Version == "" {
		return nil, ErrMissingVersion
	}

	if i.Name == "" {
		return nil, ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.PutForm(path, i, nil)
	if err != nil {
		return nil, err
	}

	var b *Backend
	if err := decodeJSON(&b, resp.Body); err != nil {
		return nil, err
	}
	return b, nil
}

// DeleteBackendInput is the input parameter to DeleteBackend.
type DeleteBackendInput struct {
	// Service is the ID of the service. Version is the specific configuration
	// version. Both fields are required.
	Service string
	Version string

	// Name is the name of the backend to delete (required).
	Name string
}

// DeleteBackend deletes the given backend version.
func (c *Client) DeleteBackend(i *DeleteBackendInput) error {
	if i.Service == "" {
		return ErrMissingService
	}

	if i.Version == "" {
		return ErrMissingVersion
	}

	if i.Name == "" {
		return ErrMissingName
	}

	path := fmt.Sprintf("/service/%s/version/%s/backend/%s", i.Service, i.Version, i.Name)
	resp, err := c.Delete(path, nil)
	if err != nil {
		return err
	}

	var r *statusResp
	if err := decodeJSON(&r, resp.Body); err != nil {
		return err
	}
	if !r.Ok() {
		return fmt.Errorf("Not Ok")
	}
	return nil
}
<|endoftext|>


<commit_before>package adapt

import (
	"fmt"
)

// Surrogate is an interpolant for a function.
type Surrogate struct {
	Inputs    uint      // Number of inputs
	Outputs   uint      // Number of outputs
	Level     uint      // Interpolation level
	Nodes     uint      // Number of nodes
	Indices   []uint64  // Indices of the nodes
	Surpluses []float64 // Hierarchical surpluses of the nodes
	Passive   []uint    // Number of passive nodes at each iteration
}

func newSurrogate(ni, no uint) *Surrogate {
	return &Surrogate{
		Inputs:    ni,
		Outputs:   no,
		Indices:   make([]uint64, 0, ni),
		Surpluses: make([]float64, 0, no),
		Passive:   make([]uint, 0),
	}
}

func (self *Surrogate) push(indices []uint64, surpluses []float64) {
	self.Indices = append(self.Indices, indices...)
	self.Surpluses = append(self.Surpluses, surpluses...)
}

func (self *Surrogate) step(level, passive uint) {
	self.Level = level
	self.Nodes += passive
	self.Passive = append(self.Passive, passive)
}

// String returns human-friendly information about the interpolant.
func (self *Surrogate) String() string {
	return fmt.Sprintf("Surrogate{inputs: %d, outputs: %d, level: %d, nodes: %d}",
		self.Inputs, self.Outputs, self.Level, self.Nodes)
}
<commit_msg>Make a cosmetic adjustment<commit_after>package adapt

import (
	"fmt"
)

// Surrogate is an interpolant for a function.
type Surrogate struct {
	Inputs    uint      // Number of inputs
	Outputs   uint      // Number of outputs
	Level     uint      // Interpolation level
	Nodes     uint      // Number of nodes
	Passive   []uint    // Number of passive nodes at each iteration
	Indices   []uint64  // Indices of the nodes
	Surpluses []float64 // Hierarchical surpluses of the nodes
}

func newSurrogate(ni, no uint) *Surrogate {
	return &Surrogate{
		Inputs:    ni,
		Outputs:   no,
		Passive:   make([]uint, 0),
		Indices:   make([]uint64, 0, ni),
		Surpluses: make([]float64, 0, no),
	}
}

func (self *Surrogate) push(indices []uint64, surpluses []float64) {
	self.Indices = append(self.Indices, indices...)
	self.Surpluses = append(self.Surpluses, surpluses...)
}

func (self *Surrogate) step(level, passive uint) {
	self.Level = level
	self.Nodes += passive
	self.Passive = append(self.Passive, passive)
}

// String returns human-friendly information about the interpolant.
func (self *Surrogate) String() string {
	return fmt.Sprintf("Surrogate{inputs: %d, outputs: %d, level: %d, nodes: %d}",
		self.Inputs, self.Outputs, self.Level, self.Nodes)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/bobziuchkovski/writ"
	"github.com/jkomoros/boardgame/boardgame-util/lib/config"
	"strings"
)

type ConfigSet struct {
	baseSubCommand

	Secret bool
	Mode   string
}

func (c *ConfigSet) Run(p writ.Path, positional []string) {

	//base := c.Base().(*BoardgameUtil)

	//cfg := base.GetConfig()

	mode := config.TypeBase

	c.Mode = strings.ToLower(c.Mode)

	switch c.Mode {
	case "base":
		//Pass; mode is already base
	case "dev":
		mode = config.TypeDev
	case "prod":
		mode = config.TypeProd
	default:
		errAndQuit(c.Mode + " is not a valid mode")
	}

	fmt.Println("This command is not yet fully functional")
	fmt.Println("Would have set", mode, c.Secret)

}

func (c *ConfigSet) Name() string {
	return "set"
}

func (c *ConfigSet) Description() string {
	return "Sets the given field to the given value"
}

func (c *ConfigSet) WritOptions() []*writ.Option {
	return []*writ.Option{
		{
			Names:       []string{"s", "secret"},
			Flag:        true,
			Description: "If provided, will set the secret config instead of public config, creating it if necessary.",
			Decoder:     writ.NewFlagDecoder(&c.Secret),
		},
		{
			Names:       []string{"m", "mode"},
			Description: "The mode type to operate on. One of {base, dev, prod}. Defaults to base.",
			Decoder: writ.NewDefaulter(
				writ.NewOptionDecoder(&c.Mode),
				"base",
			),
		},
	}
}

//TODO: usage for positional

func (c *ConfigSet) HelpText() string {
	return c.Name() + ` sets the given field to the given value in the current config.`
}
<commit_msg>Actually wired up so that `set` works correctly, but not yet for firebase or games node. Part of #655.<commit_after>package main

import (
	"errors"
	"github.com/bobziuchkovski/writ"
	"github.com/jkomoros/boardgame/boardgame-util/lib/config"
	"strings"
)

type ConfigSet struct {
	baseSubCommand

	Secret bool
	Mode   string
}

func strToBool(in string) (bool, error) {

	in = strings.ToLower(in)

	if in == "0" || in == "false" {
		return false, nil
	}

	if in == "1" || in == "true" {
		return true, nil
	}

	return false, errors.New("Invalid bool string: " + in)

}

func (c *ConfigSet) Run(p writ.Path, positional []string) {

	base := c.Base().(*BoardgameUtil)

	cfg := base.GetConfig()

	mode := config.TypeBase

	c.Mode = strings.ToLower(c.Mode)

	switch c.Mode {
	case "base":
		//Pass; mode is already base
	case "dev":
		mode = config.TypeDev
	case "prod":
		mode = config.TypeProd
	default:
		errAndQuit(c.Mode + " is not a valid mode")
	}

	if len(positional) < 1 {
		errAndQuit("KEY must be provided")
	}

	field := config.FieldFromString(positional[0])

	fieldType := config.FieldTypes[field]

	var updater config.ConfigUpdater

	switch fieldType {
	case config.FieldTypeInvalid:
		errAndQuit(positional[0] + " is not a valid field")
	case config.FieldTypeString:
		if len(positional) != 2 {
			errAndQuit("KEY of type string wants precisely one VAL")
		}
		updater = config.SetString(field, positional[1])
	case config.FieldTypeStringSlice:
		if len(positional) != 2 {
			errAndQuit("KEY of type []string wants precisely one VAL")
		}
		updater = config.AddString(field, positional[1])
	case config.FieldTypeStringMap:
		if len(positional) != 3 {
			errAndQuit("KEY of type map[string]string wants KEY SUB-KEY VAL")
		}
		updater = config.SetStringKey(field, positional[1], positional[2])
	case config.FieldTypeBool:
		if len(positional) != 2 {
			errAndQuit("KEY of type bool wants one VAL")
		}
		b, err := strToBool(positional[1])
		if err != nil {
			errAndQuit(err.Error())
		}
		updater = config.SetBool(field, b)
	case config.FieldTypeFirebase:
		errAndQuit("Firebase key not yet supported")
	case config.FieldTypeGameNode:
		errAndQuit("GAmes not yet supported")
	}

	if err := cfg.Update(mode, c.Secret, updater); err != nil {
		errAndQuit("Couldn't update value: " + err.Error())
	}

	if err := cfg.Save(); err != nil {
		errAndQuit("Couldn't save updated config files: " + err.Error())
	}

}

func (c *ConfigSet) Name() string {
	return "set"
}

func (c *ConfigSet) Description() string {
	return "Sets the given field to the given value"
}

func (c *ConfigSet) WritOptions() []*writ.Option {
	return []*writ.Option{
		{
			Names:       []string{"s", "secret"},
			Flag:        true,
			Description: "If provided, will set the secret config instead of public config, creating it if necessary.",
			Decoder:     writ.NewFlagDecoder(&c.Secret),
		},
		{
			Names:       []string{"m", "mode"},
			Description: "The mode type to operate on. One of {base, dev, prod}. Defaults to base.",
			Decoder: writ.NewDefaulter(
				writ.NewOptionDecoder(&c.Mode),
				"base",
			),
		},
	}
}

func (c *ConfigSet) Usage() string {
	return "KEY [SUB-KEY] VAL"
}

func (c *ConfigSet) HelpText() string {
	return c.Name() + ` sets the given field to the given value in the current config.

If KEY is of type string, simply sets the key to the given val.

If KEY is of type []string, simply adds the key to the given val if it doesn't exist.

If KEY is of type bool, val must be either "0", "1", "true", "false"

If KEY is of type map[key]val then SUB-KEY must also be provided.`
}
<|endoftext|>


<commit_before>package eval

import (
	"fmt"
)

// Scope represents a symbol table for evaluation. Each symbol appearing in
// an expression belongs to exactly one scope.
type Scope struct {
	parent  *Scope
	symbols map[string]*Symbol
}

// Symbol represents a particular symbol used in expressions. Symbol represents
// the location where a value will be stored rather than the value itself.
//
// A value for each symbol is defined in an associated Context.
type Symbol struct {
	name  string
	scope *Scope
}

// NewChild creates a new, empty scope that is a child of the receiver.
func (s *Scope) NewChild() *Scope {
	return &Scope{
		parent:  s,
		symbols: map[string]*Symbol{},
	}
}

// Declare establishes a new symbol in the recieving scope and returns it.
//
// This method will panic if a symbol of the same name was already declared in
// the receiving scope. This does not apply if the symbol is declared in an
// ancestor scope. Use Declared to determine if a given name was already declared.
//
// This method will also panic if a caller attempts to declare a symbol in the
// global scope, since the global scope is an immutable singleton.
func (s *Scope) Declare(name string) *Symbol {
	if s == globalScope {
		panic(fmt.Errorf("attempt to declare %q in the immutable global scope", name))
	}
	if s.symbols[name] != nil {
		panic(fmt.Errorf("attempt to re-declare %q in %#v", name, s))
	}
	sym := &Symbol{
		name:  name,
		scope: s,
	}
	s.symbols[name] = sym
	return sym
}

// Declared returns true if a symbol of the given name was already declared in
// the receiving scope. Returns false if the name is not defined or if it is
// declared only in ancestor scopes.
func (s *Scope) Declared(name string) bool {
	_, declared := s.symbols[name]
	return declared
}

// Get returns the symbol with the given name in either the receiver or
// its closest ancestor that declares the given name.
//
// If no scope in the inheritance chain declares the given name, the result
// is nil.
func (s *Scope) Get(name string) *Symbol {
	current := s
	for current != nil {
		sym := current.symbols[name]
		if sym != nil {
			return sym
		}
		current = current.parent
	}
	return nil
}
<commit_msg>eval: helper to get all known names in a scope<commit_after>package eval

import (
	"fmt"
	"sort"
)

// Scope represents a symbol table for evaluation. Each symbol appearing in
// an expression belongs to exactly one scope.
type Scope struct {
	parent  *Scope
	symbols map[string]*Symbol
}

// Symbol represents a particular symbol used in expressions. Symbol represents
// the location where a value will be stored rather than the value itself.
//
// A value for each symbol is defined in an associated Context.
type Symbol struct {
	name  string
	scope *Scope
}

// NewChild creates a new, empty scope that is a child of the receiver.
func (s *Scope) NewChild() *Scope {
	return &Scope{
		parent:  s,
		symbols: map[string]*Symbol{},
	}
}

// Declare establishes a new symbol in the recieving scope and returns it.
//
// This method will panic if a symbol of the same name was already declared in
// the receiving scope. This does not apply if the symbol is declared in an
// ancestor scope. Use Declared to determine if a given name was already declared.
//
// This method will also panic if a caller attempts to declare a symbol in the
// global scope, since the global scope is an immutable singleton.
func (s *Scope) Declare(name string) *Symbol {
	if s == globalScope {
		panic(fmt.Errorf("attempt to declare %q in the immutable global scope", name))
	}
	if s.symbols[name] != nil {
		panic(fmt.Errorf("attempt to re-declare %q in %#v", name, s))
	}
	sym := &Symbol{
		name:  name,
		scope: s,
	}
	s.symbols[name] = sym
	return sym
}

// Declared returns true if a symbol of the given name was already declared in
// the receiving scope. Returns false if the name is not defined or if it is
// declared only in ancestor scopes.
func (s *Scope) Declared(name string) bool {
	_, declared := s.symbols[name]
	return declared
}

// Get returns the symbol with the given name in either the receiver or
// its closest ancestor that declares the given name.
//
// If no scope in the inheritance chain declares the given name, the result
// is nil.
func (s *Scope) Get(name string) *Symbol {
	current := s
	for current != nil {
		sym := current.symbols[name]
		if sym != nil {
			return sym
		}
		current = current.parent
	}
	return nil
}

// AllNames returns a slice of all of the names declared in the receiver and
// all of its ancestor scopes.
//
// This is primarily intended for giving feedback to the user in error messages
// about references to undeclared symbols. It is returned sorted first by
// scope in order of "closeness" (receiver first, global scope last) and then
// by name lexicographically within each scope.
//
// Due to the sorting strategy, the same name may appear multiple times in the
// list if it is declared in multiple different scopes.
func (s *Scope) AllNames() []string {
	if s == nil {
		return nil
	}

	inherited := s.parent.AllNames()
	ret := make([]string, 0, len(s.symbols)+len(inherited))
	for name := range s.symbols {
		ret = append(ret, name)
	}
	sort.Strings(ret)
	ret = append(ret, inherited...)

	return ret
}
<|endoftext|>


<commit_before>package eventsource

import (
	"bytes"
	"testing"
)

var message []byte = []byte("{id: 1}")
var deflated = "eJyqzkyxUjCsBQQAAP//CfUCUQ=="

func TestEventBytesWithName(t *testing.T) {
	expecting := []byte("event: test\ndata: {id: 1}\n\n")
	e := Event{
		Name:    "test",
		Message: message,
	}
	result := e.bytes()
	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestEventBytesWithoutName(t *testing.T) {
	expecting := []byte("data: {id: 1}\n\n")
	e := Event{
		Message: message,
	}
	result := e.bytes()
	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestEventBytesWithCompression(t *testing.T) {
	expecting := []byte("data: " + deflated + "\n\n")
	e := Event{
		Message:  message,
		Compress: true,
	}
	result := e.bytes()
	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestDeflate(t *testing.T) {
	expecting := deflated
	result := deflate(message)
	if expecting != result {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}
<commit_msg>Add initial test for send method<commit_after>package eventsource

import (
	"bytes"
	"testing"
)

var message []byte = []byte("{id: 1}")
var deflated = "eJyqzkyxUjCsBQQAAP//CfUCUQ=="

func TestEventSendToClient(t *testing.T) {
	e := Event{Message: message}
	c := client{events: make(chan payload)}
	go e.send([]client{c})
	p := <-c.events

	expecting := e.bytes()
	result := p.data

	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestEventBytesWithName(t *testing.T) {
	expecting := []byte("event: test\ndata: {id: 1}\n\n")
	e := Event{
		Name:    "test",
		Message: message,
	}
	result := e.bytes()
	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestEventBytesWithoutName(t *testing.T) {
	expecting := []byte("data: {id: 1}\n\n")
	e := Event{
		Message: message,
	}
	result := e.bytes()
	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestEventBytesWithCompression(t *testing.T) {
	expecting := []byte("data: " + deflated + "\n\n")
	e := Event{
		Message:  message,
		Compress: true,
	}
	result := e.bytes()
	if !bytes.Equal(expecting, result) {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}

func TestDeflate(t *testing.T) {
	expecting := deflated
	result := deflate(message)
	if expecting != result {
		t.Errorf("expected:\n%s\ngot:\n%s\n", expecting, result)
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package common

import (
	"launchpad.net/loggo"

	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/params"
)

var logger = loggo.GetLogger("juju.state.apiserver.common")

// AddressAndCertGetter can be used to find out
// state server addresses and the CA public certificate.
type AddressAndCertGetter interface {
	Addresses() ([]string, error)
	APIAddresses() ([]string, error)
	CACert() []byte
}

// Addresser implements a common set of methods for getting state and
// API server addresses, and the CA certificate used to authenticate
// them.
type Addresser struct {
	getter AddressAndCertGetter
}

// NewAddresser returns a new Addresser that uses the given
// st value to fetch its addresses.
func NewAddresser(getter AddressAndCertGetter) *Addresser {
	return &Addresser{getter}
}

// StateAddresses returns the list of addresses used to connect to the state.
//
// TODO(dimitern): Remove this once we have a way to get state/API
// public addresses from state.
// BUG(lp:1205371): This is temporary, until the Addresser worker
// lands and we can take the addresses of all machines with
// JobManageState.
func (a *Addresser) StateAddresses() (params.StringsResult, error) {
	addrs, err := a.getter.Addresses()
	if err != nil {
		return params.StringsResult{}, err
	}
	return params.StringsResult{
		Result: addrs,
	}, nil
}

// APIAddresses returns the list of addresses used to connect to the API.
//
// TODO(dimitern): Remove this once we have a way to get state/API
// public addresses from state.
// BUG(lp:1205371): This is temporary, until the Addresser worker
// lands and we can take the addresses of all machines with
// JobManageState.
func (a *Addresser) APIAddresses() (params.StringsResult, error) {
	addrs, err := a.getter.APIAddresses()
	if err != nil {
		return params.StringsResult{}, err
	}
	return params.StringsResult{
		Result: addrs,
	}, nil
}

// CACert returns the certificate used to validate the state connection.
func (a *Addresser) CACert() params.BytesResult {
	return params.BytesResult{
		Result: a.getter.CACert(),
	}
}
<commit_msg>we don't need the imports, either<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package common

import (
	"launchpad.net/loggo"

	"launchpad.net/juju-core/state/api/params"
)

var logger = loggo.GetLogger("juju.state.apiserver.common")

// AddressAndCertGetter can be used to find out
// state server addresses and the CA public certificate.
type AddressAndCertGetter interface {
	Addresses() ([]string, error)
	APIAddresses() ([]string, error)
	CACert() []byte
}

// Addresser implements a common set of methods for getting state and
// API server addresses, and the CA certificate used to authenticate
// them.
type Addresser struct {
	getter AddressAndCertGetter
}

// NewAddresser returns a new Addresser that uses the given
// st value to fetch its addresses.
func NewAddresser(getter AddressAndCertGetter) *Addresser {
	return &Addresser{getter}
}

// StateAddresses returns the list of addresses used to connect to the state.
//
// TODO(dimitern): Remove this once we have a way to get state/API
// public addresses from state.
// BUG(lp:1205371): This is temporary, until the Addresser worker
// lands and we can take the addresses of all machines with
// JobManageState.
func (a *Addresser) StateAddresses() (params.StringsResult, error) {
	addrs, err := a.getter.Addresses()
	if err != nil {
		return params.StringsResult{}, err
	}
	return params.StringsResult{
		Result: addrs,
	}, nil
}

// APIAddresses returns the list of addresses used to connect to the API.
//
// TODO(dimitern): Remove this once we have a way to get state/API
// public addresses from state.
// BUG(lp:1205371): This is temporary, until the Addresser worker
// lands and we can take the addresses of all machines with
// JobManageState.
func (a *Addresser) APIAddresses() (params.StringsResult, error) {
	addrs, err := a.getter.APIAddresses()
	if err != nil {
		return params.StringsResult{}, err
	}
	return params.StringsResult{
		Result: addrs,
	}, nil
}

// CACert returns the certificate used to validate the state connection.
func (a *Addresser) CACert() params.BytesResult {
	return params.BytesResult{
		Result: a.getter.CACert(),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	"github.com/lxc/lxd/shared"
	"golang.org/x/crypto/scrypt"
)

func (d *Daemon) hasPwd() bool {
	passfname := shared.VarPath("adminpwd")
	_, err := os.Open(passfname)
	return err == nil
}

func (d *Daemon) verifyAdminPwd(password string) bool {
	passfname := shared.VarPath("adminpwd")
	passOut, err := os.Open(passfname)
	if err != nil {
		shared.Debugf("verifyAdminPwd: no password is set")
		return false
	}
	defer passOut.Close()
	buff := make([]byte, PW_SALT_BYTES+PW_HASH_BYTES)
	_, err = passOut.Read(buff)
	if err != nil {
		shared.Debugf("failed to read the saved admin pasword for verification")
		return false
	}
	salt := buff[0:PW_SALT_BYTES]
	hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, PW_HASH_BYTES)
	if err != nil {
		shared.Debugf("failed to create hash to check")
		return false
	}
	if !bytes.Equal(hash, buff[PW_SALT_BYTES:]) {
		shared.Debugf("Bad password received")
		return false
	}
	shared.Debugf("Verified the admin password")
	return true
}

func certificatesGet(d *Daemon, r *http.Request) Response {
	body := shared.Jmap{}
	for host, cert := range d.clientCerts {
		fingerprint := shared.GenerateFingerprint(&cert)
		body[host] = fingerprint
	}

	return SyncResponse(true, body)
}

type certificatesPostBody struct {
	Type        string `json:"type"`
	Certificate string `json:"certificate"`
	Name        string `json:"name"`
	Password    string `json:"password"`
}

func readSavedClientCAList(d *Daemon) {
	d.clientCerts = make(map[string]x509.Certificate)
	rows, err := shared.DbQuery(d.db, "SELECT fingerprint, type, name, certificate FROM certificates")
	if err != nil {
		shared.Logf("Error reading certificates from database: %s\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var fp string
		var t int
		var name string
		var cf []byte
		rows.Scan(&fp, &t, &name, &cf)
		cert_block, _ := pem.Decode(cf)
		cert, err := x509.ParseCertificate(cert_block.Bytes)
		if err != nil {
			shared.Logf("Error reading certificate for %s: %s\n", name, err)
			continue
		}
		d.clientCerts[name] = *cert
	}
}

func saveCert(d *Daemon, host string, cert *x509.Certificate) error {
	tx, err := d.db.Begin()
	if err != nil {
		return err
	}
	fingerprint := shared.GenerateFingerprint(cert)
	stmt, err := tx.Prepare("INSERT INTO certificates (fingerprint,type,name,certificate) VALUES (?, ?, ?, ?)")
	if err != nil {
		tx.Rollback()
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(fingerprint, 1, host, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
	if err != nil {
		tx.Rollback()
		return err
	}

	return shared.TxCommit(tx)
}

func certificatesPost(d *Daemon, r *http.Request) Response {
	req := certificatesPostBody{}

	if err := shared.ReadToJSON(r.Body, &req); err != nil {
		return BadRequest(err)
	}

	if req.Type != "client" {
		return BadRequest(fmt.Errorf("Unknown request type %s", req.Type))
	}

	var cert *x509.Certificate
	var name string
	if req.Certificate != "" {

		data, err := base64.StdEncoding.DecodeString(req.Certificate)
		if err != nil {
			return BadRequest(err)
		}

		cert, err = x509.ParseCertificate(data)
		if err != nil {
			return BadRequest(err)
		}
		name = req.Name

	} else {
		cert = r.TLS.PeerCertificates[len(r.TLS.PeerCertificates)-1]
		name = r.TLS.ServerName
	}

	fingerprint := shared.GenerateFingerprint(cert)
	for existingName, existingCert := range d.clientCerts {
		if name == existingName {
			if fingerprint == shared.GenerateFingerprint(&existingCert) {
				return EmptySyncResponse
			}
			return Conflict
		}
	}

	if !d.isTrustedClient(r) && !d.verifyAdminPwd(req.Password) {
		return Forbidden
	}

	err := saveCert(d, name, cert)
	if err != nil {
		return InternalError(err)
	}

	d.clientCerts[name] = *cert

	return EmptySyncResponse
}

var certificatesCmd = Command{"certificates", false, true, certificatesGet, nil, certificatesPost, nil}

func certificateFingerprintGet(d *Daemon, r *http.Request) Response {
	fingerprint := mux.Vars(r)["fingerprint"]

	for _, cert := range d.clientCerts {
		if fingerprint == shared.GenerateFingerprint(&cert) {
			b64 := base64.StdEncoding.EncodeToString(cert.Raw)
			body := shared.Jmap{"type": "client", "certificates": b64}
			return SyncResponse(true, body)
		}
	}

	return NotFound
}

func certificateFingerprintDelete(d *Daemon, r *http.Request) Response {
	fingerprint := mux.Vars(r)["fingerprint"]
	for name, cert := range d.clientCerts {
		if fingerprint == shared.GenerateFingerprint(&cert) {
			delete(d.clientCerts, name)
			_, err := shared.DbExec(d.db, "DELETE FROM certificates WHERE name=?", name)
			if err != nil {
				return SmartError(err)
			}
			return EmptySyncResponse
		}
	}

	return NotFound
}

var certificateFingerprintCmd = Command{"certificates/{fingerprint}", false, false, certificateFingerprintGet, nil, nil, certificateFingerprintDelete}
<commit_msg>little client cert fix<commit_after>package main

import (
	"bytes"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"net/http"
	"os"

	"github.com/gorilla/mux"
	"github.com/lxc/lxd/shared"
	"golang.org/x/crypto/scrypt"
)

func (d *Daemon) hasPwd() bool {
	passfname := shared.VarPath("adminpwd")
	_, err := os.Open(passfname)
	return err == nil
}

func (d *Daemon) verifyAdminPwd(password string) bool {
	passfname := shared.VarPath("adminpwd")
	passOut, err := os.Open(passfname)
	if err != nil {
		shared.Debugf("verifyAdminPwd: no password is set")
		return false
	}
	defer passOut.Close()
	buff := make([]byte, PW_SALT_BYTES+PW_HASH_BYTES)
	_, err = passOut.Read(buff)
	if err != nil {
		shared.Debugf("failed to read the saved admin pasword for verification")
		return false
	}
	salt := buff[0:PW_SALT_BYTES]
	hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, PW_HASH_BYTES)
	if err != nil {
		shared.Debugf("failed to create hash to check")
		return false
	}
	if !bytes.Equal(hash, buff[PW_SALT_BYTES:]) {
		shared.Debugf("Bad password received")
		return false
	}
	shared.Debugf("Verified the admin password")
	return true
}

func certificatesGet(d *Daemon, r *http.Request) Response {
	body := shared.Jmap{}
	for host, cert := range d.clientCerts {
		fingerprint := shared.GenerateFingerprint(&cert)
		body[host] = fingerprint
	}

	return SyncResponse(true, body)
}

type certificatesPostBody struct {
	Type        string `json:"type"`
	Certificate string `json:"certificate"`
	Name        string `json:"name"`
	Password    string `json:"password"`
}

func readSavedClientCAList(d *Daemon) {
	d.clientCerts = make(map[string]x509.Certificate)
	rows, err := shared.DbQuery(d.db, "SELECT fingerprint, type, name, certificate FROM certificates")
	if err != nil {
		shared.Logf("Error reading certificates from database: %s\n", err)
		return
	}
	defer rows.Close()
	for rows.Next() {
		var fp string
		var t int
		var name string
		var cf []byte
		rows.Scan(&fp, &t, &name, &cf)
		cert_block, _ := pem.Decode(cf)
		cert, err := x509.ParseCertificate(cert_block.Bytes)
		if err != nil {
			shared.Logf("Error reading certificate for %s: %s\n", name, err)
			continue
		}
		d.clientCerts[name] = *cert
	}
}

func saveCert(d *Daemon, host string, cert *x509.Certificate) error {
	tx, err := d.db.Begin()
	if err != nil {
		return err
	}
	fingerprint := shared.GenerateFingerprint(cert)
	stmt, err := tx.Prepare("INSERT INTO certificates (fingerprint,type,name,certificate) VALUES (?, ?, ?, ?)")
	if err != nil {
		tx.Rollback()
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(fingerprint, 1, host, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
	if err != nil {
		tx.Rollback()
		return err
	}

	return shared.TxCommit(tx)
}

func certificatesPost(d *Daemon, r *http.Request) Response {
	req := certificatesPostBody{}

	if err := shared.ReadToJSON(r.Body, &req); err != nil {
		return BadRequest(err)
	}

	if req.Type != "client" {
		return BadRequest(fmt.Errorf("Unknown request type %s", req.Type))
	}

	var cert *x509.Certificate
	var name string
	if req.Certificate != "" {

		data, err := base64.StdEncoding.DecodeString(req.Certificate)
		if err != nil {
			return BadRequest(err)
		}

		cert, err = x509.ParseCertificate(data)
		if err != nil {
			return BadRequest(err)
		}
		name = req.Name

	} else {

		if len(r.TLS.PeerCertificates) < 1 {
			return BadRequest(fmt.Errorf("No client certificate provided"))
		}

		cert = r.TLS.PeerCertificates[len(r.TLS.PeerCertificates)-1]
		name = r.TLS.ServerName
	}

	fingerprint := shared.GenerateFingerprint(cert)
	for existingName, existingCert := range d.clientCerts {
		if name == existingName {
			if fingerprint == shared.GenerateFingerprint(&existingCert) {
				return EmptySyncResponse
			}
			return Conflict
		}
	}

	if !d.isTrustedClient(r) && !d.verifyAdminPwd(req.Password) {
		return Forbidden
	}

	err := saveCert(d, name, cert)
	if err != nil {
		return InternalError(err)
	}

	d.clientCerts[name] = *cert

	return EmptySyncResponse
}

var certificatesCmd = Command{"certificates", false, true, certificatesGet, nil, certificatesPost, nil}

func certificateFingerprintGet(d *Daemon, r *http.Request) Response {
	fingerprint := mux.Vars(r)["fingerprint"]

	for _, cert := range d.clientCerts {
		if fingerprint == shared.GenerateFingerprint(&cert) {
			b64 := base64.StdEncoding.EncodeToString(cert.Raw)
			body := shared.Jmap{"type": "client", "certificates": b64}
			return SyncResponse(true, body)
		}
	}

	return NotFound
}

func certificateFingerprintDelete(d *Daemon, r *http.Request) Response {
	fingerprint := mux.Vars(r)["fingerprint"]
	for name, cert := range d.clientCerts {
		if fingerprint == shared.GenerateFingerprint(&cert) {
			delete(d.clientCerts, name)
			_, err := shared.DbExec(d.db, "DELETE FROM certificates WHERE name=?", name)
			if err != nil {
				return SmartError(err)
			}
			return EmptySyncResponse
		}
	}

	return NotFound
}

var certificateFingerprintCmd = Command{"certificates/{fingerprint}", false, false, certificateFingerprintGet, nil, nil, certificateFingerprintDelete}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

type containerType int

const (
	cTypeRegular  containerType = 0
	cTypeSnapshot containerType = 1
)

func dbContainerRemove(db *sql.DB, name string) error {
	_, err := dbExec(db, "DELETE FROM containers WHERE name=?", name)
	return err
}

func dbContainerIDGet(db *sql.DB, name string) (int, error) {
	q := "SELECT id FROM containers WHERE name=?"
	id := -1
	arg1 := []interface{}{name}
	arg2 := []interface{}{&id}
	err := dbQueryRowScan(db, q, arg1, arg2)
	return id, err
}

func dbContainerGet(db *sql.DB, name string) (*containerLXDArgs, error) {
	args := &containerLXDArgs{
		Ephemeral: false,
	}

	ephemInt := -1
	q := "SELECT id, architecture, type, ephemeral FROM containers WHERE name=?"
	arg1 := []interface{}{name}
	arg2 := []interface{}{&args.ID, &args.Architecture, &args.Ctype, &ephemInt}
	err := dbQueryRowScan(db, q, arg1, arg2)
	if err != nil {
		return nil, err
	}
	if args.ID == -1 {
		return nil, fmt.Errorf("Unknown container")
	}

	if ephemInt == 1 {
		args.Ephemeral = true
	}

	config, err := dbContainerConfigGet(db, args.ID)
	if err != nil {
		return nil, err
	}
	args.Config = config

	profiles, err := dbContainerProfilesGet(db, args.ID)
	if err != nil {
		return nil, err
	}
	args.Profiles = profiles

	args.Devices = shared.Devices{}
	/* get container_devices */
	newdevs, err := dbDevicesGet(db, name, false)
	if err != nil {
		return nil, err
	}
	for k, v := range newdevs {
		args.Devices[k] = v
	}

	return args, nil
}

func dbContainerCreate(db *sql.DB, name string, args containerLXDArgs) (int, error) {
	id, err := dbContainerIDGet(db, name)
	if err == nil {
		return 0, DbErrAlreadyDefined
	}

	tx, err := dbBegin(db)
	if err != nil {
		return 0, err
	}
	ephemInt := 0
	if args.Ephemeral == true {
		ephemInt = 1
	}

	str := fmt.Sprintf("INSERT INTO containers (name, architecture, type, ephemeral) VALUES (?, ?, ?, ?)")
	stmt, err := tx.Prepare(str)
	if err != nil {
		tx.Rollback()
		return 0, err
	}
	defer stmt.Close()
	result, err := stmt.Exec(name, args.Architecture, args.Ctype, ephemInt)
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	id64, err := result.LastInsertId()
	if err != nil {
		tx.Rollback()
		return 0, fmt.Errorf("Error inserting %s into database", name)
	}
	// TODO: is this really int64? we should fix it everywhere if so
	id = int(id64)
	if err := dbContainerConfigInsert(tx, id, args.Config); err != nil {
		tx.Rollback()
		return 0, err
	}

	if err := dbContainerProfilesInsert(tx, id, args.Profiles); err != nil {
		tx.Rollback()
		return 0, err
	}

	if err := dbDevicesAdd(tx, "container", id, args.Devices); err != nil {
		tx.Rollback()
		return 0, err
	}

	return id, txCommit(tx)
}

func dbContainerConfigClear(tx *sql.Tx, id int) error {
	_, err := tx.Exec("DELETE FROM containers_config WHERE container_id=?", id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM containers_profiles WHERE container_id=?", id)
	if err != nil {
		return err
	}
	_, err = tx.Exec(`DELETE FROM containers_devices_config WHERE id IN
		(SELECT containers_devices_config.id
		 FROM containers_devices_config JOIN containers_devices
		 ON containers_devices_config.container_device_id=containers_devices.id
		 WHERE containers_devices.container_id=?)`, id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM containers_devices WHERE container_id=?", id)
	return err
}

func dbContainerConfigInsert(tx *sql.Tx, id int, config map[string]string) error {
	str := "INSERT INTO containers_config (container_id, key, value) values (?, ?, ?)"
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for k, v := range config {
		if k == "raw.lxc" {
			err := validateRawLxc(config["raw.lxc"])
			if err != nil {
				return err
			}
		}

		if !ValidContainerConfigKey(k) {
			return fmt.Errorf("Bad key: %s\n", k)
		}

		_, err = stmt.Exec(id, k, v)
		if err != nil {
			shared.Debugf("Error adding configuration item %s = %s to container %d\n",
				k, v, id)
			return err
		}
	}

	return nil
}

func dbContainerProfilesInsert(tx *sql.Tx, id int, profiles []string) error {
	applyOrder := 1
	str := `INSERT INTO containers_profiles (container_id, profile_id, apply_order) VALUES
		(?, (SELECT id FROM profiles WHERE name=?), ?);`
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()
	for _, p := range profiles {
		_, err = stmt.Exec(id, p, applyOrder)
		if err != nil {
			shared.Debugf("Error adding profile %s to container: %s\n",
				p, err)
			return err
		}
		applyOrder = applyOrder + 1
	}

	return nil
}

func dbContainerSnapshotRemove(db *sql.DB, cname string, sname string) {
	name := fmt.Sprintf("%s/%s", cname, sname)
	_, _ = dbExec(db, "DELETE FROM containers WHERE type=? AND name=?", cTypeSnapshot, name)
}

func dbContainerDeviceConfigGet(db *sql.DB, id int, isprofile bool) (shared.Device, error) {
	var query string
	var key, value string
	newdev := shared.Device{} // That's a map[string]string
	inargs := []interface{}{id}
	outfmt := []interface{}{key, value}

	if isprofile {
		query = `SELECT key, value FROM profiles_devices_config WHERE profile_device_id=?`
	} else {
		query = `SELECT key, value FROM containers_devices_config WHERE container_device_id=?`
	}

	results, err := dbQueryScan(db, query, inargs, outfmt)

	if err != nil {
		return newdev, err
	}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)
		newdev[key] = value
	}

	return newdev, nil
}

// Get a list of profiles for a given container id.
func dbContainerProfilesGet(db *sql.DB, containerID int) ([]string, error) {
	var name string
	var profiles []string

	query := `
        SELECT name FROM containers_profiles
        JOIN profiles ON containers_profiles.profile_id=profiles.id
		WHERE container_id=?
        ORDER BY containers_profiles.apply_order`
	inargs := []interface{}{containerID}
	outfmt := []interface{}{name}

	results, err := dbQueryScan(db, query, inargs, outfmt)
	if err != nil {
		return nil, err
	}

	for _, r := range results {
		name = r[0].(string)

		profiles = append(profiles, name)
	}

	return profiles, nil
}

// dbContainerConfigGet gets the container configuration map from the DB
func dbContainerConfigGet(db *sql.DB, containerID int) (map[string]string, error) {
	var key, value string
	q := `SELECT key, value FROM containers_config WHERE container_id=?`

	inargs := []interface{}{containerID}
	outfmt := []interface{}{key, value}

	// Results is already a slice here, not db Rows anymore.
	results, err := dbQueryScan(db, q, inargs, outfmt)
	if err != nil {
		return nil, err //SmartError will wrap this and make "not found" errors pretty
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

func dbContainersList(db *sql.DB) ([]string, error) {
	q := fmt.Sprintf("SELECT name FROM containers WHERE type=? ORDER BY name")
	inargs := []interface{}{cTypeRegular}
	var container string
	outfmt := []interface{}{container}
	result, err := dbQueryScan(db, q, inargs, outfmt)
	if err != nil {
		return nil, err
	}

	var ret []string
	for _, container := range result {
		ret = append(ret, container[0].(string))
	}

	return ret, nil
}

func dbContainerRename(db *sql.DB, oldName string, newName string) error {
	tx, err := dbBegin(db)
	if err != nil {
		return err
	}

	str := fmt.Sprintf("UPDATE containers SET name = ? WHERE name = ?")
	stmt, err := tx.Prepare(str)
	if err != nil {
		tx.Rollback()
		return err
	}
	defer stmt.Close()

	shared.Log.Debug(
		"Calling SQL Query",
		log.Ctx{
			"query":   "UPDATE containers SET name = ? WHERE name = ?",
			"oldName": oldName,
			"newName": newName})
	if _, err := stmt.Exec(newName, oldName); err != nil {
		tx.Rollback()
		return err
	}

	return txCommit(tx)
}

func dbContainerGetSnapshots(db *sql.DB, name string) ([]string, error) {
	result := []string{}

	regexp := name + shared.SnapshotDelimiter
	length := len(regexp)
	q := "SELECT name FROM containers WHERE type=? AND SUBSTR(name,1,?)=?"
	inargs := []interface{}{cTypeSnapshot, length, regexp}
	outfmt := []interface{}{name}
	dbResults, err := dbQueryScan(db, q, inargs, outfmt)
	if err != nil {
		return result, err
	}

	for _, r := range dbResults {
		result = append(result, r[0].(string))
	}

	return result, nil
}

// ValidContainerConfigKey returns if the given config key is a known/valid key.
func ValidContainerConfigKey(k string) bool {
	switch k {
	case "limits.cpus":
		return true
	case "limits.memory":
		return true
	case "security.privileged":
		return true
	case "raw.apparmor":
		return true
	case "raw.lxc":
		return true
	case "volatile.baseImage":
		return true
	}

	if _, err := ExtractInterfaceFromConfigName(k); err == nil {
		return true
	}

	return strings.HasPrefix(k, "user.")
}
<commit_msg>Fix dbContainerCreate, sry.<commit_after>package main

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

type containerType int

const (
	cTypeRegular  containerType = 0
	cTypeSnapshot containerType = 1
)

func dbContainerRemove(db *sql.DB, name string) error {
	_, err := dbExec(db, "DELETE FROM containers WHERE name=?", name)
	return err
}

func dbContainerIDGet(db *sql.DB, name string) (int, error) {
	q := "SELECT id FROM containers WHERE name=?"
	id := -1
	arg1 := []interface{}{name}
	arg2 := []interface{}{&id}
	err := dbQueryRowScan(db, q, arg1, arg2)
	return id, err
}

func dbContainerGet(db *sql.DB, name string) (*containerLXDArgs, error) {
	args := &containerLXDArgs{
		Ephemeral: false,
	}

	ephemInt := -1
	q := "SELECT id, architecture, type, ephemeral FROM containers WHERE name=?"
	arg1 := []interface{}{name}
	arg2 := []interface{}{&args.ID, &args.Architecture, &args.Ctype, &ephemInt}
	err := dbQueryRowScan(db, q, arg1, arg2)
	if err != nil {
		return nil, err
	}
	if args.ID == -1 {
		return nil, fmt.Errorf("Unknown container")
	}

	if ephemInt == 1 {
		args.Ephemeral = true
	}

	config, err := dbContainerConfigGet(db, args.ID)
	if err != nil {
		return nil, err
	}
	args.Config = config

	profiles, err := dbContainerProfilesGet(db, args.ID)
	if err != nil {
		return nil, err
	}
	args.Profiles = profiles

	args.Devices = shared.Devices{}
	/* get container_devices */
	newdevs, err := dbDevicesGet(db, name, false)
	if err != nil {
		return nil, err
	}
	for k, v := range newdevs {
		args.Devices[k] = v
	}

	return args, nil
}

func dbContainerCreate(db *sql.DB, name string, args containerLXDArgs) (int, error) {
	id, err := dbContainerIDGet(db, name)
	if err == nil {
		return 0, DbErrAlreadyDefined
	}

	if args.Profiles == nil {
		args.Profiles = []string{"default"}
	}

	if args.BaseImage != "" {
		if args.Config == nil {
			args.Config = map[string]string{}
		}

		args.Config["volatile.baseImage"] = args.BaseImage
	}

	tx, err := dbBegin(db)
	if err != nil {
		return 0, err
	}
	ephemInt := 0
	if args.Ephemeral == true {
		ephemInt = 1
	}

	str := fmt.Sprintf("INSERT INTO containers (name, architecture, type, ephemeral) VALUES (?, ?, ?, ?)")
	stmt, err := tx.Prepare(str)
	if err != nil {
		tx.Rollback()
		return 0, err
	}
	defer stmt.Close()
	result, err := stmt.Exec(name, args.Architecture, args.Ctype, ephemInt)
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	id64, err := result.LastInsertId()
	if err != nil {
		tx.Rollback()
		return 0, fmt.Errorf("Error inserting %s into database", name)
	}
	// TODO: is this really int64? we should fix it everywhere if so
	id = int(id64)
	if err := dbContainerConfigInsert(tx, id, args.Config); err != nil {
		tx.Rollback()
		return 0, err
	}

	if err := dbContainerProfilesInsert(tx, id, args.Profiles); err != nil {
		tx.Rollback()
		return 0, err
	}

	if err := dbDevicesAdd(tx, "container", id, args.Devices); err != nil {
		tx.Rollback()
		return 0, err
	}

	return id, txCommit(tx)
}

func dbContainerConfigClear(tx *sql.Tx, id int) error {
	_, err := tx.Exec("DELETE FROM containers_config WHERE container_id=?", id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM containers_profiles WHERE container_id=?", id)
	if err != nil {
		return err
	}
	_, err = tx.Exec(`DELETE FROM containers_devices_config WHERE id IN
		(SELECT containers_devices_config.id
		 FROM containers_devices_config JOIN containers_devices
		 ON containers_devices_config.container_device_id=containers_devices.id
		 WHERE containers_devices.container_id=?)`, id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM containers_devices WHERE container_id=?", id)
	return err
}

func dbContainerConfigInsert(tx *sql.Tx, id int, config map[string]string) error {
	str := "INSERT INTO containers_config (container_id, key, value) values (?, ?, ?)"
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for k, v := range config {
		if k == "raw.lxc" {
			err := validateRawLxc(config["raw.lxc"])
			if err != nil {
				return err
			}
		}

		if !ValidContainerConfigKey(k) {
			return fmt.Errorf("Bad key: %s\n", k)
		}

		_, err = stmt.Exec(id, k, v)
		if err != nil {
			shared.Debugf("Error adding configuration item %s = %s to container %d\n",
				k, v, id)
			return err
		}
	}

	return nil
}

func dbContainerProfilesInsert(tx *sql.Tx, id int, profiles []string) error {
	applyOrder := 1
	str := `INSERT INTO containers_profiles (container_id, profile_id, apply_order) VALUES
		(?, (SELECT id FROM profiles WHERE name=?), ?);`
	stmt, err := tx.Prepare(str)
	if err != nil {
		return err
	}
	defer stmt.Close()
	for _, p := range profiles {
		_, err = stmt.Exec(id, p, applyOrder)
		if err != nil {
			shared.Debugf("Error adding profile %s to container: %s\n",
				p, err)
			return err
		}
		applyOrder = applyOrder + 1
	}

	return nil
}

func dbContainerSnapshotRemove(db *sql.DB, cname string, sname string) {
	name := fmt.Sprintf("%s/%s", cname, sname)
	_, _ = dbExec(db, "DELETE FROM containers WHERE type=? AND name=?", cTypeSnapshot, name)
}

func dbContainerDeviceConfigGet(db *sql.DB, id int, isprofile bool) (shared.Device, error) {
	var query string
	var key, value string
	newdev := shared.Device{} // That's a map[string]string
	inargs := []interface{}{id}
	outfmt := []interface{}{key, value}

	if isprofile {
		query = `SELECT key, value FROM profiles_devices_config WHERE profile_device_id=?`
	} else {
		query = `SELECT key, value FROM containers_devices_config WHERE container_device_id=?`
	}

	results, err := dbQueryScan(db, query, inargs, outfmt)

	if err != nil {
		return newdev, err
	}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)
		newdev[key] = value
	}

	return newdev, nil
}

// Get a list of profiles for a given container id.
func dbContainerProfilesGet(db *sql.DB, containerID int) ([]string, error) {
	var name string
	var profiles []string

	query := `
        SELECT name FROM containers_profiles
        JOIN profiles ON containers_profiles.profile_id=profiles.id
		WHERE container_id=?
        ORDER BY containers_profiles.apply_order`
	inargs := []interface{}{containerID}
	outfmt := []interface{}{name}

	results, err := dbQueryScan(db, query, inargs, outfmt)
	if err != nil {
		return nil, err
	}

	for _, r := range results {
		name = r[0].(string)

		profiles = append(profiles, name)
	}

	return profiles, nil
}

// dbContainerConfigGet gets the container configuration map from the DB
func dbContainerConfigGet(db *sql.DB, containerID int) (map[string]string, error) {
	var key, value string
	q := `SELECT key, value FROM containers_config WHERE container_id=?`

	inargs := []interface{}{containerID}
	outfmt := []interface{}{key, value}

	// Results is already a slice here, not db Rows anymore.
	results, err := dbQueryScan(db, q, inargs, outfmt)
	if err != nil {
		return nil, err //SmartError will wrap this and make "not found" errors pretty
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

func dbContainersList(db *sql.DB) ([]string, error) {
	q := fmt.Sprintf("SELECT name FROM containers WHERE type=? ORDER BY name")
	inargs := []interface{}{cTypeRegular}
	var container string
	outfmt := []interface{}{container}
	result, err := dbQueryScan(db, q, inargs, outfmt)
	if err != nil {
		return nil, err
	}

	var ret []string
	for _, container := range result {
		ret = append(ret, container[0].(string))
	}

	return ret, nil
}

func dbContainerRename(db *sql.DB, oldName string, newName string) error {
	tx, err := dbBegin(db)
	if err != nil {
		return err
	}

	str := fmt.Sprintf("UPDATE containers SET name = ? WHERE name = ?")
	stmt, err := tx.Prepare(str)
	if err != nil {
		tx.Rollback()
		return err
	}
	defer stmt.Close()

	shared.Log.Debug(
		"Calling SQL Query",
		log.Ctx{
			"query":   "UPDATE containers SET name = ? WHERE name = ?",
			"oldName": oldName,
			"newName": newName})
	if _, err := stmt.Exec(newName, oldName); err != nil {
		tx.Rollback()
		return err
	}

	return txCommit(tx)
}

func dbContainerGetSnapshots(db *sql.DB, name string) ([]string, error) {
	result := []string{}

	regexp := name + shared.SnapshotDelimiter
	length := len(regexp)
	q := "SELECT name FROM containers WHERE type=? AND SUBSTR(name,1,?)=?"
	inargs := []interface{}{cTypeSnapshot, length, regexp}
	outfmt := []interface{}{name}
	dbResults, err := dbQueryScan(db, q, inargs, outfmt)
	if err != nil {
		return result, err
	}

	for _, r := range dbResults {
		result = append(result, r[0].(string))
	}

	return result, nil
}

// ValidContainerConfigKey returns if the given config key is a known/valid key.
func ValidContainerConfigKey(k string) bool {
	switch k {
	case "limits.cpus":
		return true
	case "limits.memory":
		return true
	case "security.privileged":
		return true
	case "raw.apparmor":
		return true
	case "raw.lxc":
		return true
	case "volatile.baseImage":
		return true
	}

	if _, err := ExtractInterfaceFromConfigName(k); err == nil {
		return true
	}

	return strings.HasPrefix(k, "user.")
}
<|endoftext|>