<commit_before>package features

import (
	"fmt"
	"strconv"
	"strings"

	v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	managementv3 "github.com/rancher/rancher/pkg/generated/controllers/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	features = make(map[string]*Feature)

	// Features, ex.: ClusterRandomName = newFeature("cluster-randomizer", "Randomizes clusters.", false, false)

	UnsupportedStorageDrivers = newFeature(
		"unsupported-storage-drivers",
		"Allows the use of types for storage providers and provisioners that are not enabled by default.",
		false,
		true,
		true)
	IstioVirtualServiceUI = newFeature(
		"istio-virtual-service-ui",
		"Exposes a UI that enables users to create, read, update and delete virtual services and destination rules, which are traffic management features of Istio.",
		true,
		true,
		true)
	Steve = newFeature(
		"dashboard",
		"Deploy experimental new UI for managing resources inside of clusters.",
		true,
		false,
		true)
	SteveProxy = newFeature(
		"proxy",
		"Use new experimental proxy for Kubernetes API requests.",
		false,
		true,
		true)
	MCM = newFeature(
		"multi-cluster-management",
		"Multi-cluster provisioning and management of Kubernetes clusters.",
		true,
		true,
		false)
)

type Feature struct {
	name string
	// val is the effective value- it is equal to default until explicitly changed
	description string
	val         bool
	def         bool
	// if a feature is not dynamic, then rancher must be restarted when the value is changed
	dynamic bool
	// Whether we should install this feature or assume something else will install and manage the Feature CR
	install bool
}

// InitializeFeatures updates feature default if given valid --features flag and creates/updates necessary features in k8s
func InitializeFeatures(featuresClient managementv3.FeatureClient, featureArgs string) {
	// applies any default values assigned in --features flag to feature map
	if err := applyArgumentDefaults(featureArgs); err != nil {
		logrus.Errorf("failed to apply feature args: %v", err)
	}

	if featuresClient == nil {
		return
	}

	// creates any features in map that do not exist, updates features with new default value
	for key, f := range features {
		featureState, err := featuresClient.Get(key, metav1.GetOptions{})
		if err != nil {
			if !errors.IsNotFound(err) {
				logrus.Errorf("unable to retrieve feature %s in initialize features: %v", f.name, err)
			}

			if f.install {
				// value starts off as nil, that way rancher can determine if value has been manually assigned
				newFeature := &v3.Feature{
					ObjectMeta: metav1.ObjectMeta{
						Name: f.name,
					},
					Spec: v3.FeatureSpec{
						Value: nil,
					},
					Status: v3.FeatureStatus{
						Default:     f.def,
						Dynamic:     f.dynamic,
						Description: f.description,
					},
				}

				if _, err := featuresClient.Create(newFeature); err != nil {
					logrus.Errorf("unable to create feature %s in initialize features: %v", f.name, err)
				}
			}
		} else {
			newFeatureState := featureState.DeepCopy()
			// checks if default value has changed
			if featureState.Status.Default != f.def {
				newFeatureState.Status.Default = f.def
			}

			// checks if developer has changed dynamic value from previous rancher version
			if featureState.Status.Dynamic != f.dynamic {
				newFeatureState.Status.Dynamic = f.dynamic
			}

			// checks if developer has changed description value from previous rancher version
			if featureState.Status.Description != f.description {
				newFeatureState.Status.Description = f.description
			}

			if newFeatureState, err = featuresClient.Update(newFeatureState); err != nil {
				logrus.Errorf("unable to update feature %s in initialize features: %v", f.name, err)
				continue
			}

			if featureState.Spec.Value == nil {
				continue
			}

			if *featureState.Spec.Value == f.val {
				continue
			}

			f.Set(*featureState.Spec.Value)
		}
	}
}

// applyArgumentDefaults reads the features arguments and uses their values to overwrite
// the corresponding feature default value
func applyArgumentDefaults(featureArgs string) error {
	if featureArgs == "" {
		return nil
	}

	formattingError := fmt.Errorf("feature argument [%s] should be of the form \"features=feature1=bool,feature2=bool\"", featureArgs)
	args := strings.Split(featureArgs, ",")

	applyFeatureDefaults := make(map[string]bool)

	for _, feature := range args {
		featureSet := strings.Split(feature, "=")
		if len(featureSet) != 2 {
			return formattingError
		}

		key := featureSet[0]
		if features[key] == nil {
			return fmt.Errorf("\"%s\" is not a valid feature", key)
		}

		value, err := strconv.ParseBool(featureSet[1])
		if err != nil {
			return formattingError
		}

		applyFeatureDefaults[key] = value
	}

	// only want to apply defaults once all args have been parsed and validated
	for k, v := range applyFeatureDefaults {
		features[k].def = v
		features[k].val = v
	}

	return nil
}

// Enabled returns whether the feature is enabled
func (f *Feature) Enabled() bool {
	return f.val
}

// Disable will disable a feature such that regardless of the user's choice it will always be false
func (f *Feature) Disable() {
	f.val = false
	f.def = false
	delete(features, f.name)
}

// Dynamic returns whether the feature is dynamic. Rancher must be restarted when
// a non-dynamic feature's effective value is changed.
func (f *Feature) Dynamic() bool {
	return f.dynamic
}

func (f *Feature) Set(val bool) {
	f.val = val
}

func (f *Feature) Name() string {
	return f.name
}

func GetFeatureByName(name string) *Feature {
	return features[name]
}

func IsEnabled(feature *v3.Feature) bool {
	if feature == nil {
		return false
	}
	if feature.Spec.Value == nil {
		return feature.Status.Default
	}
	return *feature.Spec.Value
}

// newFeature adds feature to the global feature map
func newFeature(name, description string, def, dynamic, install bool) *Feature {
	feature := &Feature{
		name:        name,
		description: description,
		def:         def,
		val:         def,
		dynamic:     dynamic,
		install:     install,
	}

	// feature will be stored in feature map, features contained in feature
	// map will be then be initialized
	features[name] = feature

	return feature
}
<commit_msg>Don't install steve proxy feature<commit_after>package features

import (
	"fmt"
	"strconv"
	"strings"

	v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	managementv3 "github.com/rancher/rancher/pkg/generated/controllers/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	features = make(map[string]*Feature)

	// Features, ex.: ClusterRandomName = newFeature("cluster-randomizer", "Randomizes clusters.", false, false)

	UnsupportedStorageDrivers = newFeature(
		"unsupported-storage-drivers",
		"Allows the use of types for storage providers and provisioners that are not enabled by default.",
		false,
		true,
		true)
	IstioVirtualServiceUI = newFeature(
		"istio-virtual-service-ui",
		"Exposes a UI that enables users to create, read, update and delete virtual services and destination rules, which are traffic management features of Istio.",
		true,
		true,
		true)
	Steve = newFeature(
		"dashboard",
		"Deploy experimental new UI for managing resources inside of clusters.",
		true,
		false,
		true)
	SteveProxy = newFeature(
		"proxy",
		"Use new experimental proxy for Kubernetes API requests.",
		false,
		true,
		false)
	MCM = newFeature(
		"multi-cluster-management",
		"Multi-cluster provisioning and management of Kubernetes clusters.",
		true,
		true,
		false)
)

type Feature struct {
	name string
	// val is the effective value- it is equal to default until explicitly changed
	description string
	val         bool
	def         bool
	// if a feature is not dynamic, then rancher must be restarted when the value is changed
	dynamic bool
	// Whether we should install this feature or assume something else will install and manage the Feature CR
	install bool
}

// InitializeFeatures updates feature default if given valid --features flag and creates/updates necessary features in k8s
func InitializeFeatures(featuresClient managementv3.FeatureClient, featureArgs string) {
	// applies any default values assigned in --features flag to feature map
	if err := applyArgumentDefaults(featureArgs); err != nil {
		logrus.Errorf("failed to apply feature args: %v", err)
	}

	if featuresClient == nil {
		return
	}

	// creates any features in map that do not exist, updates features with new default value
	for key, f := range features {
		featureState, err := featuresClient.Get(key, metav1.GetOptions{})
		if err != nil {
			if !errors.IsNotFound(err) {
				logrus.Errorf("unable to retrieve feature %s in initialize features: %v", f.name, err)
			}

			if f.install {
				// value starts off as nil, that way rancher can determine if value has been manually assigned
				newFeature := &v3.Feature{
					ObjectMeta: metav1.ObjectMeta{
						Name: f.name,
					},
					Spec: v3.FeatureSpec{
						Value: nil,
					},
					Status: v3.FeatureStatus{
						Default:     f.def,
						Dynamic:     f.dynamic,
						Description: f.description,
					},
				}

				if _, err := featuresClient.Create(newFeature); err != nil {
					logrus.Errorf("unable to create feature %s in initialize features: %v", f.name, err)
				}
			}
		} else {
			newFeatureState := featureState.DeepCopy()
			// checks if default value has changed
			if featureState.Status.Default != f.def {
				newFeatureState.Status.Default = f.def
			}

			// checks if developer has changed dynamic value from previous rancher version
			if featureState.Status.Dynamic != f.dynamic {
				newFeatureState.Status.Dynamic = f.dynamic
			}

			// checks if developer has changed description value from previous rancher version
			if featureState.Status.Description != f.description {
				newFeatureState.Status.Description = f.description
			}

			if newFeatureState, err = featuresClient.Update(newFeatureState); err != nil {
				logrus.Errorf("unable to update feature %s in initialize features: %v", f.name, err)
				continue
			}

			if featureState.Spec.Value == nil {
				continue
			}

			if *featureState.Spec.Value == f.val {
				continue
			}

			f.Set(*featureState.Spec.Value)
		}
	}
}

// applyArgumentDefaults reads the features arguments and uses their values to overwrite
// the corresponding feature default value
func applyArgumentDefaults(featureArgs string) error {
	if featureArgs == "" {
		return nil
	}

	formattingError := fmt.Errorf("feature argument [%s] should be of the form \"features=feature1=bool,feature2=bool\"", featureArgs)
	args := strings.Split(featureArgs, ",")

	applyFeatureDefaults := make(map[string]bool)

	for _, feature := range args {
		featureSet := strings.Split(feature, "=")
		if len(featureSet) != 2 {
			return formattingError
		}

		key := featureSet[0]
		if features[key] == nil {
			return fmt.Errorf("\"%s\" is not a valid feature", key)
		}

		value, err := strconv.ParseBool(featureSet[1])
		if err != nil {
			return formattingError
		}

		applyFeatureDefaults[key] = value
	}

	// only want to apply defaults once all args have been parsed and validated
	for k, v := range applyFeatureDefaults {
		features[k].def = v
		features[k].val = v
	}

	return nil
}

// Enabled returns whether the feature is enabled
func (f *Feature) Enabled() bool {
	return f.val
}

// Disable will disable a feature such that regardless of the user's choice it will always be false
func (f *Feature) Disable() {
	f.val = false
	f.def = false
	delete(features, f.name)
}

// Dynamic returns whether the feature is dynamic. Rancher must be restarted when
// a non-dynamic feature's effective value is changed.
func (f *Feature) Dynamic() bool {
	return f.dynamic
}

func (f *Feature) Set(val bool) {
	f.val = val
}

func (f *Feature) Name() string {
	return f.name
}

func GetFeatureByName(name string) *Feature {
	return features[name]
}

func IsEnabled(feature *v3.Feature) bool {
	if feature == nil {
		return false
	}
	if feature.Spec.Value == nil {
		return feature.Status.Default
	}
	return *feature.Spec.Value
}

// newFeature adds feature to the global feature map
func newFeature(name, description string, def, dynamic, install bool) *Feature {
	feature := &Feature{
		name:        name,
		description: description,
		def:         def,
		val:         def,
		dynamic:     dynamic,
		install:     install,
	}

	// feature will be stored in feature map, features contained in feature
	// map will be then be initialized
	features[name] = feature

	return feature
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package configs helps with loading and parsing configuration files
package configs

import (
	"context"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"cloud.google.com/go/storage"
	"github.com/golang/glog"
	"github.com/pkg/errors"
	"google.golang.org/api/iterator"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var (
	client = configGCSClient()
)

func configGCSClient() (client *storage.Client) {
	ctx := context.Background()

	client, err := storage.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}
	return client
}

// file encapsulates a YAML or Rego file to be read
type file interface {
	read() ([]byte, error)
	String() string
}

type localFile struct {
	path string
}

type gcsFile struct {
	bucket string
	path   string
}

// dir encapsulates a directory of YAML or Rego file to be read
type dir interface {
	listFiles() ([]string, error)
}

type gcsDir struct {
	path string
}

type localDir struct {
	path string
}

func newFile(name string) (file, error) {
	fileURL, err := url.Parse(name)
	if err != nil {
		return nil, err
	}

	if fileURL.Scheme == "gs" {
		configFile := new(gcsFile)
		configFile.bucket = fileURL.Host
		configFile.path = strings.Replace(fileURL.Path, "/", "", 1)
		return configFile, nil
	}

	configFile := new(localFile)
	configFile.path = name

	return configFile, nil
}

func (f *localFile) read() ([]byte, error) {
	glog.V(2).Infof("Loading local file at path %s", f.path)
	fileBytes, err := ioutil.ReadFile(f.path)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to read file %s", f.path)
	}
	return fileBytes, nil
}

func (f *localFile) String() string {
	return f.path
}

func (f *gcsFile) read() ([]byte, error) {
	ctx := context.Background()
	glog.V(2).Infof("Loading file in GCS at path %s", f.path)

	rc, err := client.Bucket(f.bucket).Object(f.path).NewReader(ctx)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	data, err := ioutil.ReadAll(rc)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to read file %s from bucket %s", f.path, f.bucket)
	}
	return data, nil
	// [END download_file]
}

func (f *gcsFile) String() string {
	return f.path
}

func newDir(name string) (dir, error) {
	dirURL, err := url.Parse(name)
	if err != nil {
		return nil, err
	}

	if dirURL.Scheme == "gs" {
		configDir := new(gcsDir)
		configDir.path = name
		return configDir, nil
	}

	configDir := new(localDir)
	configDir.path = name

	return configDir, nil
}

func (d *localDir) listFiles() ([]string, error) {
	var files []string

	visit := func(path string, f os.FileInfo, err error) error {
		if err != nil {
			return errors.Wrapf(err, "error visiting path %s", path)
		}
		if !f.IsDir() {

			files = append(files, path)
		}
		return nil
	}

	err := filepath.Walk(d.path, visit)
	if err != nil {
		return nil, status.Error(codes.InvalidArgument, err.Error())
	}
	return files, nil
}

func (d *gcsDir) listFiles() ([]string, error) {
	var files []string
	ctx := context.Background()
	dirURL, err := url.Parse(d.path)

	if err != nil {
		return nil, errors.Wrapf(err, "error visiting path %s", d.path)
	}

	bucket := dirURL.Host
	prefix := strings.Replace(dirURL.Path, "/", "", 1)

	it := client.Bucket(bucket).Objects(ctx, &storage.Query{
		Prefix: prefix,
	})
	glog.V(2).Infof("Listing files in GCS at host %s and path %s", bucket, prefix)
	for {
		attrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, err
		}
		fileName := "gs://" + bucket + "/" + attrs.Name
		glog.V(2).Infof("Listing GCS Object %s", fileName)

		files = append(files, fileName)
	}
	return files, nil
}
<commit_msg>Lazy load GCS client.<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package configs helps with loading and parsing configuration files
package configs

import (
	"context"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/golang/glog"
	"github.com/pkg/errors"
	"google.golang.org/api/iterator"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var (
	globals struct {
		once   sync.Once
		client *storage.Client
	}
)

func configGCSClient() {
	ctx := context.Background()

	var err error
	globals.client, err = storage.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}
}

// file encapsulates a YAML or Rego file to be read
type file interface {
	read() ([]byte, error)
	String() string
}

type localFile struct {
	path string
}

type gcsFile struct {
	bucket string
	path   string
}

// dir encapsulates a directory of YAML or Rego file to be read
type dir interface {
	listFiles() ([]string, error)
}

type gcsDir struct {
	path string
}

type localDir struct {
	path string
}

func newFile(name string) (file, error) {
	fileURL, err := url.Parse(name)
	if err != nil {
		return nil, err
	}

	if fileURL.Scheme == "gs" {
		globals.once.Do(configGCSClient)
		configFile := new(gcsFile)
		configFile.bucket = fileURL.Host
		configFile.path = strings.Replace(fileURL.Path, "/", "", 1)
		return configFile, nil
	}

	configFile := new(localFile)
	configFile.path = name

	return configFile, nil
}

func (f *localFile) read() ([]byte, error) {
	glog.V(2).Infof("Loading local file at path %s", f.path)
	fileBytes, err := ioutil.ReadFile(f.path)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to read file %s", f.path)
	}
	return fileBytes, nil
}

func (f *localFile) String() string {
	return f.path
}

func (f *gcsFile) read() ([]byte, error) {
	ctx := context.Background()
	glog.V(2).Infof("Loading file in GCS at path %s", f.path)

	rc, err := globals.client.Bucket(f.bucket).Object(f.path).NewReader(ctx)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	data, err := ioutil.ReadAll(rc)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to read file %s from bucket %s", f.path, f.bucket)
	}
	return data, nil
	// [END download_file]
}

func (f *gcsFile) String() string {
	return f.path
}

func newDir(name string) (dir, error) {
	dirURL, err := url.Parse(name)
	if err != nil {
		return nil, err
	}

	if dirURL.Scheme == "gs" {
		configDir := new(gcsDir)
		configDir.path = name
		return configDir, nil
	}

	configDir := new(localDir)
	configDir.path = name

	return configDir, nil
}

func (d *localDir) listFiles() ([]string, error) {
	var files []string

	visit := func(path string, f os.FileInfo, err error) error {
		if err != nil {
			return errors.Wrapf(err, "error visiting path %s", path)
		}
		if !f.IsDir() {

			files = append(files, path)
		}
		return nil
	}

	err := filepath.Walk(d.path, visit)
	if err != nil {
		return nil, status.Error(codes.InvalidArgument, err.Error())
	}
	return files, nil
}

func (d *gcsDir) listFiles() ([]string, error) {
	var files []string
	ctx := context.Background()
	dirURL, err := url.Parse(d.path)

	if err != nil {
		return nil, errors.Wrapf(err, "error visiting path %s", d.path)
	}

	bucket := dirURL.Host
	prefix := strings.Replace(dirURL.Path, "/", "", 1)

	it := globals.client.Bucket(bucket).Objects(ctx, &storage.Query{
		Prefix: prefix,
	})
	glog.V(2).Infof("Listing files in GCS at host %s and path %s", bucket, prefix)
	for {
		attrs, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, err
		}
		fileName := "gs://" + bucket + "/" + attrs.Name
		glog.V(2).Infof("Listing GCS Object %s", fileName)

		files = append(files, fileName)
	}
	return files, nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Cisco Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hostagent

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"text/template"

	"github.com/Sirupsen/logrus"
	"github.com/vishvananda/netlink"
)

func (agent *HostAgent) discoverHostConfig() (conf *HostAgentNodeConfig) {
	links, err := netlink.LinkList()
	if err != nil {
		agent.log.Error("Could not enumerate interfaces: ", err)
		return
	}

	for _, link := range links {
		switch link := link.(type) {
		case *netlink.Vlan:
			// find link with matching vlan
			if link.VlanId != int(agent.config.AciInfraVlan) {
				continue
			}

			if link.MTU < 1600 {
				agent.log.WithFields(logrus.Fields{
					"name": link.Name,
					"vlan": agent.config.AciInfraVlan,
					"mtu":  link.MTU,
				}).Error("OpFlex link MTU must be >= 1600")
				return
			}

			// find parent link
			var parent netlink.Link
			for _, plink := range links {
				if plink.Attrs().Index != link.ParentIndex {
					continue
				}

				parent = plink
				if parent.Attrs().MTU < 1600 {
					agent.log.WithFields(logrus.Fields{
						"name": parent.Attrs().Name,
						"vlan": agent.config.AciInfraVlan,
						"mtu":  parent.Attrs().MTU,
					}).Error("Uplink MTU must be >= 1600")
					return
				}
			}
			if parent == nil {
				agent.log.WithFields(logrus.Fields{
					"index": link.ParentIndex,
					"name":  link.Name,
				}).Error("Could not find parent link for OpFlex interface")
				return
			}

			// Find address of link to compute anycast and peer IPs
			addrs, err := netlink.AddrList(link, 2)
			if err != nil {
				agent.log.WithFields(logrus.Fields{
					"name": link.Name,
				}).Error("Could not enumerate link addresses: ", err)
				return
			}
			var anycast net.IP
			var peerIp net.IP
			for _, addr := range addrs {
				if addr.IP.To4() == nil || addr.IP.IsLoopback() {
					continue
				}
				anycast = addr.IP.Mask(addr.Mask)
				anycast[len(anycast)-1] = 32
				peerIp = addr.IP.Mask(addr.Mask)
				peerIp[len(peerIp)-1] = 30
			}

			if anycast == nil {
				agent.log.WithFields(logrus.Fields{
					"name": link.Name,
					"vlan": agent.config.AciInfraVlan,
				}).Error("IP address not set for OpFlex link")
			}

			conf = &HostAgentNodeConfig{}
			conf.VxlanIface = link.Name
			conf.UplinkIface = parent.Attrs().Name
			conf.VxlanAnycastIp = anycast.String()
			conf.OpflexPeerIp = peerIp.String()
			return
		}
	}

	agent.log.WithFields(logrus.Fields{"vlan": agent.config.AciInfraVlan}).
		Error("Could not find suitable host uplink interface for vlan")
	return
}

var opflexConfigBase = initTempl("opflex-config-base", `{
    "opflex": {
        "name": "{{.NodeName | js}}",
        "domain": "{{print "comp/prov-[" .AciVmmDomainType "]/ctrlr-[" .AciVmmDomain "]-" .AciVmmController "/sw-InsiemeLSOid" | js}}",
        "peers": [
            {"hostname": "{{.OpflexPeerIp | js}}", "port": "8009"}
        ]
    } ,
    "endpoint-sources": {
        "filesystem": ["/usr/local/var/lib/opflex-agent-ovs/endpoints"]
    },
    "service-sources": {
        "filesystem": ["/usr/local/var/lib/opflex-agent-ovs/services"]
    }
}
`)

var opflexConfigVxlan = initTempl("opflex-config-vxlan", `{
    "renderers": {
        "stitched-mode": {
            "int-bridge-name": "br-int",
            "access-bridge-name": "br-access",
            "encap": {
                "vxlan" : {
                    "encap-iface": "vxlan0",
                    "uplink-iface": "{{.VxlanIface | js}}",
                    "uplink-vlan": "{{.AciInfraVlan}}",
                    "remote-ip": "{{.VxlanAnycastIp | js}}",
                    "remote-port": 8472
                }
            }
        }
    }
}
`)

var opflexConfigVlan = initTempl("opflex-config-vlan", `{
    "renderers": {
        "stitched-mode": {
            "int-bridge-name": "br-int",
            "access-bridge-name": "br-access",
            "encap": {
                "vlan" : {
                    "encap-iface": "{{.UplinkIface | js}}"
                }
            }
        }
    }
}
`)

func initTempl(name string, templ string) *template.Template {
	return template.Must(template.New(name).Parse(templ))
}

func (agent *HostAgent) writeConfigFile(name string,
	templ *template.Template) error {

	var buffer bytes.Buffer
	templ.Execute(&buffer, agent.config)

	path := filepath.Join(agent.config.OpFlexConfigPath, name)

	existing, err := ioutil.ReadFile(path)
	if err != nil {
		if bytes.Equal(existing, buffer.Bytes()) {
			agent.log.Info("OpFlex agent configuration file ",
				path, " unchanged")
			return nil
		}
	}

	f, err := os.Create(path)
	if err != nil {
		return err
	}
	f.Write(buffer.Bytes())
	f.Close()

	agent.log.Info("Wrote OpFlex agent configuration file ", path)

	return nil
}

func (agent *HostAgent) updateOpflexConfig() {
	if agent.config.OpFlexConfigPath == "" {
		agent.log.Debug("OpFlex agent configuration path not set")
		return
	}

	newNodeConfig := agent.discoverHostConfig()
	if newNodeConfig == nil {
		panic(errors.New("Node configuration autodiscovery failed"))
	}
	var update bool

	agent.indexMutex.Lock()
	if !reflect.DeepEqual(*newNodeConfig, agent.config.HostAgentNodeConfig) ||
		!agent.opflexConfigWritten {

		agent.config.HostAgentNodeConfig = *newNodeConfig
		agent.log.WithFields(logrus.Fields{
			"uplink-iface":     newNodeConfig.UplinkIface,
			"vxlan-iface":      newNodeConfig.VxlanIface,
			"vxlan-anycast-ip": newNodeConfig.VxlanAnycastIp,
			"opflex-peer-ip":   newNodeConfig.OpflexPeerIp,
		}).Info("Discovered node configuration")
		agent.writeOpflexConfig()
		agent.opflexConfigWritten = true
	}
	agent.indexMutex.Unlock()

	if update {
		agent.updateAllServices()
	}
}

func (agent *HostAgent) writeOpflexConfig() error {
	err := agent.writeConfigFile("01-base.conf", opflexConfigBase)
	if err != nil {
		return err
	}

	var rtempl *template.Template
	if agent.config.EncapType == "vlan" {
		rtempl = opflexConfigVlan
	} else if agent.config.EncapType == "vxlan" {
		rtempl = opflexConfigVxlan
	} else {
		panic("Unsupported encap type: " + agent.config.EncapType)
	}

	err = agent.writeConfigFile("10-renderer.conf", rtempl)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Fix incorrect brackets in VMM domain DN in OpFlex configuration.<commit_after>// Copyright 2017 Cisco Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hostagent

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"text/template"

	"github.com/Sirupsen/logrus"
	"github.com/vishvananda/netlink"
)

func (agent *HostAgent) discoverHostConfig() (conf *HostAgentNodeConfig) {
	links, err := netlink.LinkList()
	if err != nil {
		agent.log.Error("Could not enumerate interfaces: ", err)
		return
	}

	for _, link := range links {
		switch link := link.(type) {
		case *netlink.Vlan:
			// find link with matching vlan
			if link.VlanId != int(agent.config.AciInfraVlan) {
				continue
			}

			if link.MTU < 1600 {
				agent.log.WithFields(logrus.Fields{
					"name": link.Name,
					"vlan": agent.config.AciInfraVlan,
					"mtu":  link.MTU,
				}).Error("OpFlex link MTU must be >= 1600")
				return
			}

			// find parent link
			var parent netlink.Link
			for _, plink := range links {
				if plink.Attrs().Index != link.ParentIndex {
					continue
				}

				parent = plink
				if parent.Attrs().MTU < 1600 {
					agent.log.WithFields(logrus.Fields{
						"name": parent.Attrs().Name,
						"vlan": agent.config.AciInfraVlan,
						"mtu":  parent.Attrs().MTU,
					}).Error("Uplink MTU must be >= 1600")
					return
				}
			}
			if parent == nil {
				agent.log.WithFields(logrus.Fields{
					"index": link.ParentIndex,
					"name":  link.Name,
				}).Error("Could not find parent link for OpFlex interface")
				return
			}

			// Find address of link to compute anycast and peer IPs
			addrs, err := netlink.AddrList(link, 2)
			if err != nil {
				agent.log.WithFields(logrus.Fields{
					"name": link.Name,
				}).Error("Could not enumerate link addresses: ", err)
				return
			}
			var anycast net.IP
			var peerIp net.IP
			for _, addr := range addrs {
				if addr.IP.To4() == nil || addr.IP.IsLoopback() {
					continue
				}
				anycast = addr.IP.Mask(addr.Mask)
				anycast[len(anycast)-1] = 32
				peerIp = addr.IP.Mask(addr.Mask)
				peerIp[len(peerIp)-1] = 30
			}

			if anycast == nil {
				agent.log.WithFields(logrus.Fields{
					"name": link.Name,
					"vlan": agent.config.AciInfraVlan,
				}).Error("IP address not set for OpFlex link")
			}

			conf = &HostAgentNodeConfig{}
			conf.VxlanIface = link.Name
			conf.UplinkIface = parent.Attrs().Name
			conf.VxlanAnycastIp = anycast.String()
			conf.OpflexPeerIp = peerIp.String()
			return
		}
	}

	agent.log.WithFields(logrus.Fields{"vlan": agent.config.AciInfraVlan}).
		Error("Could not find suitable host uplink interface for vlan")
	return
}

var opflexConfigBase = initTempl("opflex-config-base", `{
    "opflex": {
        "name": "{{.NodeName | js}}",
        "domain": "{{print "comp/prov-" .AciVmmDomainType "/ctrlr-[" .AciVmmDomain "]-" .AciVmmController "/sw-InsiemeLSOid" | js}}",
        "peers": [
            {"hostname": "{{.OpflexPeerIp | js}}", "port": "8009"}
        ]
    } ,
    "endpoint-sources": {
        "filesystem": ["/usr/local/var/lib/opflex-agent-ovs/endpoints"]
    },
    "service-sources": {
        "filesystem": ["/usr/local/var/lib/opflex-agent-ovs/services"]
    }
}
`)

var opflexConfigVxlan = initTempl("opflex-config-vxlan", `{
    "renderers": {
        "stitched-mode": {
            "int-bridge-name": "br-int",
            "access-bridge-name": "br-access",
            "encap": {
                "vxlan" : {
                    "encap-iface": "vxlan0",
                    "uplink-iface": "{{.VxlanIface | js}}",
                    "uplink-vlan": "{{.AciInfraVlan}}",
                    "remote-ip": "{{.VxlanAnycastIp | js}}",
                    "remote-port": 8472
                }
            }
        }
    }
}
`)

var opflexConfigVlan = initTempl("opflex-config-vlan", `{
    "renderers": {
        "stitched-mode": {
            "int-bridge-name": "br-int",
            "access-bridge-name": "br-access",
            "encap": {
                "vlan" : {
                    "encap-iface": "{{.UplinkIface | js}}"
                }
            }
        }
    }
}
`)

func initTempl(name string, templ string) *template.Template {
	return template.Must(template.New(name).Parse(templ))
}

func (agent *HostAgent) writeConfigFile(name string,
	templ *template.Template) error {

	var buffer bytes.Buffer
	templ.Execute(&buffer, agent.config)

	path := filepath.Join(agent.config.OpFlexConfigPath, name)

	existing, err := ioutil.ReadFile(path)
	if err != nil {
		if bytes.Equal(existing, buffer.Bytes()) {
			agent.log.Info("OpFlex agent configuration file ",
				path, " unchanged")
			return nil
		}
	}

	f, err := os.Create(path)
	if err != nil {
		return err
	}
	f.Write(buffer.Bytes())
	f.Close()

	agent.log.Info("Wrote OpFlex agent configuration file ", path)

	return nil
}

func (agent *HostAgent) updateOpflexConfig() {
	if agent.config.OpFlexConfigPath == "" {
		agent.log.Debug("OpFlex agent configuration path not set")
		return
	}

	newNodeConfig := agent.discoverHostConfig()
	if newNodeConfig == nil {
		panic(errors.New("Node configuration autodiscovery failed"))
	}
	var update bool

	agent.indexMutex.Lock()
	if !reflect.DeepEqual(*newNodeConfig, agent.config.HostAgentNodeConfig) ||
		!agent.opflexConfigWritten {

		agent.config.HostAgentNodeConfig = *newNodeConfig
		agent.log.WithFields(logrus.Fields{
			"uplink-iface":     newNodeConfig.UplinkIface,
			"vxlan-iface":      newNodeConfig.VxlanIface,
			"vxlan-anycast-ip": newNodeConfig.VxlanAnycastIp,
			"opflex-peer-ip":   newNodeConfig.OpflexPeerIp,
		}).Info("Discovered node configuration")
		agent.writeOpflexConfig()
		agent.opflexConfigWritten = true
	}
	agent.indexMutex.Unlock()

	if update {
		agent.updateAllServices()
	}
}

func (agent *HostAgent) writeOpflexConfig() error {
	err := agent.writeConfigFile("01-base.conf", opflexConfigBase)
	if err != nil {
		return err
	}

	var rtempl *template.Template
	if agent.config.EncapType == "vlan" {
		rtempl = opflexConfigVlan
	} else if agent.config.EncapType == "vxlan" {
		rtempl = opflexConfigVxlan
	} else {
		panic("Unsupported encap type: " + agent.config.EncapType)
	}

	err = agent.writeConfigFile("10-renderer.conf", rtempl)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Matt Jibson (mjibson@gmail.com)

package rsg

import (
	"fmt"
	"math"
	"math/rand"
	"strings"
	"time"

	"github.com/cockroachdb/cockroach/pkg/internal/rsg/yacc"
	"github.com/cockroachdb/cockroach/pkg/sql/parser"
	"github.com/cockroachdb/cockroach/pkg/util/duration"
	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
)

// RSG is a random syntax generator.
type RSG struct {
	lock  syncutil.Mutex
	src   *rand.Rand
	seen  map[string]bool
	prods map[string][]*yacc.ExpressionNode
}

// NewRSG creates a random syntax generator from the given random seed and
// yacc file.
func NewRSG(seed int64, y string) (*RSG, error) {
	tree, err := yacc.Parse("sql", y)
	if err != nil {
		return nil, err
	}
	rsg := RSG{
		src:   rand.New(rand.NewSource(seed)),
		seen:  make(map[string]bool),
		prods: make(map[string][]*yacc.ExpressionNode),
	}
	for _, prod := range tree.Productions {
		rsg.prods[prod.Name] = prod.Expressions
	}
	return &rsg, nil
}

// Generate generates a unique random syntax from the root node. At most depth
// levels of token expansion are performed. An empty string is returned on
// error or if depth is exceeded. Generate is safe to call from mulitipule
// goroutines. If Generate is called more times than it can generate unique
// output, it will block forever.
func (r *RSG) Generate(root string, depth int) string {
	for {
		s := strings.Join(r.generate(root, depth), " ")
		r.lock.Lock()
		if !r.seen[s] {
			r.seen[s] = true
		} else {
			s = ""
		}
		r.lock.Unlock()
		if s != "" {
			s = strings.Replace(s, "_LA", "", -1)
			s = strings.Replace(s, " AS OF SYSTEM TIME \"string\"", "", -1)
			return s
		}
	}
}

func (r *RSG) generate(root string, depth int) []string {
	var ret []string
	prods := r.prods[root]
	if len(prods) == 0 {
		return []string{root}
	}
	prod := prods[r.Intn(len(prods))]
	for _, item := range prod.Items {
		switch item.Typ {
		case yacc.TypLiteral:
			v := item.Value[1 : len(item.Value)-1]
			ret = append(ret, v)
		case yacc.TypToken:
			var v []string
			switch item.Value {
			case "IDENT":
				v = []string{"ident"}
			case "c_expr":
				v = r.generate(item.Value, 30)
			case "SCONST":
				v = []string{`'string'`}
			case "ICONST":
				v = []string{fmt.Sprint(r.Intn(1000) - 500)}
			case "FCONST":
				v = []string{fmt.Sprint(r.Float64())}
			case "BCONST":
				v = []string{`b'bytes'`}
			case "substr_from":
				v = []string{"FROM", `'string'`}
			case "substr_for":
				v = []string{"FOR", `'string'`}
			case "overlay_placing":
				v = []string{"PLACING", `'string'`}
			default:
				if depth == 0 {
					return nil
				}
				v = r.generate(item.Value, depth-1)
			}
			if v == nil {
				return nil
			}
			ret = append(ret, v...)
		default:
			panic("unknown item type")
		}
	}
	return ret
}

// Intn returns a random int.
func (r *RSG) Intn(n int) int {
	r.lock.Lock()
	v := r.src.Intn(n)
	r.lock.Unlock()
	return v
}

// Int63 returns a random int64.
func (r *RSG) Int63() int64 {
	r.lock.Lock()
	v := r.src.Int63()
	r.lock.Unlock()
	return v
}

// Float64 returns a random float. It is sometimes +/-Inf, NaN, and attempts to
// be distributed among very small, large, and normal scale numbers.
func (r *RSG) Float64() float64 {
	r.lock.Lock()
	v := r.src.Float64()*2 - 1
	switch r.src.Intn(10) {
	case 0:
		v = 0
	case 1:
		v = math.Inf(1)
	case 2:
		v = math.Inf(-1)
	case 3:
		v = math.NaN()
	case 4, 5:
		i := r.src.Intn(50)
		v *= math.Pow10(i)
	case 6, 7:
		i := r.src.Intn(50)
		v *= math.Pow10(-i)
	}
	r.lock.Unlock()
	return v
}

// GenerateRandomArg generates a random, valid, SQL function argument of
// the spcified type.
func (r *RSG) GenerateRandomArg(typ parser.Type) string {
	if r.Intn(10) == 0 {
		return "NULL"
	}
	var v interface{}
	switch typ {
	case parser.TypeInt:
		i := r.Int63()
		i -= r.Int63()
		v = i
	case parser.TypeFloat, parser.TypeDecimal:
		v = r.Float64()
	case parser.TypeString:
		v = `'string'`
	case parser.TypeBytes:
		v = `b'bytes'`
	case parser.TypeTimestamp, parser.TypeTimestampTZ:
		t := time.Unix(0, r.Int63())
		v = fmt.Sprintf(`'%s'`, t.Format(time.RFC3339Nano))
	case parser.TypeBool:
		if r.Intn(2) == 0 {
			v = "false"
		} else {
			v = "true"
		}
	case parser.TypeDate:
		i := r.Int63()
		i -= r.Int63()
		d := parser.NewDDate(parser.DDate(i))
		v = fmt.Sprintf(`'%s'`, d)
	case parser.TypeInterval:
		d := duration.Duration{Nanos: r.Int63()}
		v = fmt.Sprintf(`'%s'`, &parser.DInterval{Duration: d})
	case parser.TypeIntArray, parser.TypeStringArray, parser.TypeAnyArray:
		v = "NULL"
	default:
		switch typ.(type) {
		case parser.TTuple:
			v = "NULL"
		default:
			panic(fmt.Errorf("unknown arg type: %s (%T)", typ, typ))
		}
	}
	return fmt.Sprintf("%v::%s", v, typ.String())
}
<commit_msg>sql: add TypeAny to RSG<commit_after>// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Matt Jibson (mjibson@gmail.com)

package rsg

import (
	"fmt"
	"math"
	"math/rand"
	"strings"
	"time"

	"github.com/cockroachdb/cockroach/pkg/internal/rsg/yacc"
	"github.com/cockroachdb/cockroach/pkg/sql/parser"
	"github.com/cockroachdb/cockroach/pkg/util/duration"
	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
)

// RSG is a random syntax generator.
type RSG struct {
	lock  syncutil.Mutex
	src   *rand.Rand
	seen  map[string]bool
	prods map[string][]*yacc.ExpressionNode
}

// NewRSG creates a random syntax generator from the given random seed and
// yacc file.
func NewRSG(seed int64, y string) (*RSG, error) {
	tree, err := yacc.Parse("sql", y)
	if err != nil {
		return nil, err
	}
	rsg := RSG{
		src:   rand.New(rand.NewSource(seed)),
		seen:  make(map[string]bool),
		prods: make(map[string][]*yacc.ExpressionNode),
	}
	for _, prod := range tree.Productions {
		rsg.prods[prod.Name] = prod.Expressions
	}
	return &rsg, nil
}

// Generate generates a unique random syntax from the root node. At most depth
// levels of token expansion are performed. An empty string is returned on
// error or if depth is exceeded. Generate is safe to call from mulitipule
// goroutines. If Generate is called more times than it can generate unique
// output, it will block forever.
func (r *RSG) Generate(root string, depth int) string {
	for {
		s := strings.Join(r.generate(root, depth), " ")
		r.lock.Lock()
		if !r.seen[s] {
			r.seen[s] = true
		} else {
			s = ""
		}
		r.lock.Unlock()
		if s != "" {
			s = strings.Replace(s, "_LA", "", -1)
			s = strings.Replace(s, " AS OF SYSTEM TIME \"string\"", "", -1)
			return s
		}
	}
}

func (r *RSG) generate(root string, depth int) []string {
	var ret []string
	prods := r.prods[root]
	if len(prods) == 0 {
		return []string{root}
	}
	prod := prods[r.Intn(len(prods))]
	for _, item := range prod.Items {
		switch item.Typ {
		case yacc.TypLiteral:
			v := item.Value[1 : len(item.Value)-1]
			ret = append(ret, v)
		case yacc.TypToken:
			var v []string
			switch item.Value {
			case "IDENT":
				v = []string{"ident"}
			case "c_expr":
				v = r.generate(item.Value, 30)
			case "SCONST":
				v = []string{`'string'`}
			case "ICONST":
				v = []string{fmt.Sprint(r.Intn(1000) - 500)}
			case "FCONST":
				v = []string{fmt.Sprint(r.Float64())}
			case "BCONST":
				v = []string{`b'bytes'`}
			case "substr_from":
				v = []string{"FROM", `'string'`}
			case "substr_for":
				v = []string{"FOR", `'string'`}
			case "overlay_placing":
				v = []string{"PLACING", `'string'`}
			default:
				if depth == 0 {
					return nil
				}
				v = r.generate(item.Value, depth-1)
			}
			if v == nil {
				return nil
			}
			ret = append(ret, v...)
		default:
			panic("unknown item type")
		}
	}
	return ret
}

// Intn returns a random int.
func (r *RSG) Intn(n int) int {
	r.lock.Lock()
	v := r.src.Intn(n)
	r.lock.Unlock()
	return v
}

// Int63 returns a random int64.
func (r *RSG) Int63() int64 {
	r.lock.Lock()
	v := r.src.Int63()
	r.lock.Unlock()
	return v
}

// Float64 returns a random float. It is sometimes +/-Inf, NaN, and attempts to
// be distributed among very small, large, and normal scale numbers.
func (r *RSG) Float64() float64 {
	r.lock.Lock()
	v := r.src.Float64()*2 - 1
	switch r.src.Intn(10) {
	case 0:
		v = 0
	case 1:
		v = math.Inf(1)
	case 2:
		v = math.Inf(-1)
	case 3:
		v = math.NaN()
	case 4, 5:
		i := r.src.Intn(50)
		v *= math.Pow10(i)
	case 6, 7:
		i := r.src.Intn(50)
		v *= math.Pow10(-i)
	}
	r.lock.Unlock()
	return v
}

// GenerateRandomArg generates a random, valid, SQL function argument of
// the spcified type.
func (r *RSG) GenerateRandomArg(typ parser.Type) string {
	if r.Intn(10) == 0 {
		return "NULL"
	}
	var v interface{}
	switch typ {
	case parser.TypeInt:
		i := r.Int63()
		i -= r.Int63()
		v = i
	case parser.TypeFloat, parser.TypeDecimal:
		v = r.Float64()
	case parser.TypeString:
		v = `'string'`
	case parser.TypeBytes:
		v = `b'bytes'`
	case parser.TypeTimestamp, parser.TypeTimestampTZ:
		t := time.Unix(0, r.Int63())
		v = fmt.Sprintf(`'%s'`, t.Format(time.RFC3339Nano))
	case parser.TypeBool:
		if r.Intn(2) == 0 {
			v = "false"
		} else {
			v = "true"
		}
	case parser.TypeDate:
		i := r.Int63()
		i -= r.Int63()
		d := parser.NewDDate(parser.DDate(i))
		v = fmt.Sprintf(`'%s'`, d)
	case parser.TypeInterval:
		d := duration.Duration{Nanos: r.Int63()}
		v = fmt.Sprintf(`'%s'`, &parser.DInterval{Duration: d})
	case parser.TypeIntArray,
		parser.TypeStringArray,
		parser.TypeAnyArray,
		parser.TypeAny:
		v = "NULL"
	default:
		switch typ.(type) {
		case parser.TTuple:
			v = "NULL"
		default:
			panic(fmt.Errorf("unknown arg type: %s (%T)", typ, typ))
		}
	}
	return fmt.Sprintf("%v::%s", v, typ.String())
}
<|endoftext|>


<commit_before>package pubsub

import (
	"sync"
	"time"
)

// NewPublisher creates a new pub/sub publisher to broadcast messages.
// The duration is used as the send timeout as to not block the publisher publishing
// messages to other clients if one client is slow or unresponsive.
// The buffer is used when creating new channels for subscribers.
func NewPublisher(publishTimeout time.Duration, buffer int) *Publisher {
	return &Publisher{
		buffer:      buffer,
		timeout:     publishTimeout,
		subscribers: make(map[subscriber]struct{}),
	}
}

type subscriber chan interface{}

type Publisher struct {
	m           sync.RWMutex
	buffer      int
	timeout     time.Duration
	subscribers map[subscriber]struct{}
}

// Len returns the number of subscribers for the publisher
func (p *Publisher) Len() int {
	p.m.RLock()
	i := len(p.subscribers)
	p.m.RUnlock()
	return i
}

// Subscribe adds a new subscriber to the publisher returning the channel.
func (p *Publisher) Subscribe() chan interface{} {
	ch := make(chan interface{}, p.buffer)
	p.m.Lock()
	p.subscribers[ch] = struct{}{}
	p.m.Unlock()
	return ch
}

// Evict removes the specified subscriber from receiving any more messages.
func (p *Publisher) Evict(sub chan interface{}) {
	p.m.Lock()
	delete(p.subscribers, sub)
	close(sub)
	p.m.Unlock()
}

// Publish sends the data in v to all subscribers currently registered with the publisher.
func (p *Publisher) Publish(v interface{}) {
	p.m.RLock()
	for sub := range p.subscribers {
		// send under a select as to not block if the receiver is unavailable
		select {
		case sub <- v:
		case <-time.After(p.timeout):
		}
	}
	p.m.RUnlock()
}

// Close closes the channels to all subscribers registered with the publisher.
func (p *Publisher) Close() {
	p.m.Lock()
	for sub := range p.subscribers {
		close(sub)
	}
	p.m.Unlock()
}
<commit_msg>Fix send on closed channel bug<commit_after>package pubsub

import (
	"sync"
	"time"
)

// NewPublisher creates a new pub/sub publisher to broadcast messages.
// The duration is used as the send timeout as to not block the publisher publishing
// messages to other clients if one client is slow or unresponsive.
// The buffer is used when creating new channels for subscribers.
func NewPublisher(publishTimeout time.Duration, buffer int) *Publisher {
	return &Publisher{
		buffer:      buffer,
		timeout:     publishTimeout,
		subscribers: make(map[subscriber]struct{}),
	}
}

type subscriber chan interface{}

type Publisher struct {
	m           sync.RWMutex
	buffer      int
	timeout     time.Duration
	subscribers map[subscriber]struct{}
}

// Len returns the number of subscribers for the publisher
func (p *Publisher) Len() int {
	p.m.RLock()
	i := len(p.subscribers)
	p.m.RUnlock()
	return i
}

// Subscribe adds a new subscriber to the publisher returning the channel.
func (p *Publisher) Subscribe() chan interface{} {
	ch := make(chan interface{}, p.buffer)
	p.m.Lock()
	p.subscribers[ch] = struct{}{}
	p.m.Unlock()
	return ch
}

// Evict removes the specified subscriber from receiving any more messages.
func (p *Publisher) Evict(sub chan interface{}) {
	p.m.Lock()
	delete(p.subscribers, sub)
	close(sub)
	p.m.Unlock()
}

// Publish sends the data in v to all subscribers currently registered with the publisher.
func (p *Publisher) Publish(v interface{}) {
	p.m.RLock()
	for sub := range p.subscribers {
		// send under a select as to not block if the receiver is unavailable
		select {
		case sub <- v:
		case <-time.After(p.timeout):
		}
	}
	p.m.RUnlock()
}

// Close closes the channels to all subscribers registered with the publisher.
func (p *Publisher) Close() {
	p.m.Lock()
	for sub := range p.subscribers {
		delete(p.subscribers, sub)
		close(sub)
	}
	p.m.Unlock()
}
<|endoftext|>


<commit_before>package lifted

// Assign numbers to all things of type Name

import (
	"fmt"
	"os"
)

func (d *Domain) AssignNums(s *Symtab) os.Error {
	for i, _ := range d.Types {
		t := &d.Types[i]
		t.Name.numberType(s)
		for j, _ := range t.Type {
			t.Type[j].numberType(s)
		}
	}

	s.typeObjs = make([][]int, len(s.typeNames))
	numberConsts(s, d.Constants)

	for i, _ := range d.Predicates {
		p := &d.Predicates[i]
		p.Name.numberPred(s)
		for j, _ := range p.Parameters {
			parm := p.Parameters[j]
			for k, _ := range parm.Type {
				parm.Type[k].numberType(s)
			}
		}
	}

	for _, a := range d.Actions {
		if err := a.AssignNums(s); err != nil {
			return err
		}
	}
	return nil
}

func numberConsts(s *Symtab, consts []TypedName) {
	for i, _ := range consts {
		c := &consts[i]
		first := c.Name.numberConst(s)
		cnum := c.Name.Num
		for j, _ := range c.Type {
			c.Type[j].numberType(s)
			// If this is the 1st decl of this object
			// then add it to the table of all objects
			// of the given type
			if !first {
				tnum := c.Type[j].Num
				s.typeObjs[tnum] = append(s.typeObjs[tnum], cnum)
			}
		}
	}
}

func (a *Action) AssignNums(s *Symtab) os.Error {
	var f *numFrame
	for i, _ := range a.Parameters {
		p := &a.Parameters[i]
		f = p.Name.numberVar(s, f)
		for j, _ := range p.Type {
			p.Type[j].numberType(s)
		}
	}
	if err := a.Precondition.AssignNums(s, f); err != nil {
		return err
	}
	return a.Effect.AssignNums(s, f)
}

func (p *Problem) AssignNums(s *Symtab) os.Error {
	numberConsts(s, p.Objects)
	for _, init := range p.Init {
		if err := init.AssignNums(s, nil); err != nil {
			return err
		}
	}
	return p.Goal.AssignNums(s, nil)
}

func (l *Literal) AssignNums(s *Symtab, f *numFrame) os.Error {
	for i, t := range l.Parameters {
		name := &l.Parameters[i].Name
		switch t.Kind {
		case TermVariable:
			if fnxt := name.numberVar(s, f); fnxt == f {
				break
			}
			return fmt.Errorf("%s: Unbound variable %s\n", t.Loc, name.Str)
		case TermConstant:
			name.numberConst(s)
		}
	}
	l.Name.numberPred(s)
	return nil
}

func (e *ExprBinary) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (ExprTrue) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (ExprFalse) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *ExprAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprOr) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprNot) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Expr.AssignNums(s, f)
}

func (e *ExprQuant) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		e.Variable.Type[i].numberType(s)
	}
	return e.Expr.AssignNums(s, f)
}

func (e *ExprForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprExists) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectUnary) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Effect.AssignNums(s, f)
}

func (EffectNone) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *EffectAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (e *EffectForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		e.Variable.Type[i].numberType(s)
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectWhen) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Condition.AssignNums(s, f); err != nil {
		return err
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectAssign) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (i *InitLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(i).AssignNums(s, f)
}

func (i *InitEq) AssignNums(s *Symtab, f *numFrame) os.Error {
	return nil
}

func (name *Name) numberType(s *Symtab) {
	if n, ok := s.typeNums[name.Str]; ok {
		name.Num = n
	} else {
		name.Num = len(s.typeNames)
		s.typeNums[name.Str] = name.Num
		s.typeNames = append(s.typeNames, name.Str)
	}
}

func (name *Name) numberConst(s *Symtab) bool {
	if n, ok := s.constNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.constNames)
	s.constNums[name.Str] = name.Num
	s.constNames = append(s.constNames, name.Str)
	return false
}

func (name *Name) numberPred(s *Symtab) {
	if n, ok := s.predNums[name.Str]; ok {
		name.Num = n
	} else {
		name.Num = len(s.predNames)
		s.predNums[name.Str] = name.Num
		s.predNames = append(s.predNames, name.Str)
	}
}

func (name *Name) numberVar(s *Symtab, f *numFrame) *numFrame {
	if n, ok := f.lookup(name.Str); ok {
		name.Num = n
		return f
	}
	n := len(s.varNames)
	name.Num = n
	s.varNames = append(s.varNames, name.Str)
	return &numFrame{name: name.Str, num: n, up: f}
}

type numFrame struct {
	name string
	num  int
	up   *numFrame
}

func (f *numFrame) lookup(name string) (int, bool) {
	if f == nil {
		return 0, false
	}
	if f.name == name {
		return f.num, true
	}
	return f.up.lookup(name)
}<commit_msg>Return true or false for whether or not a predicate, const or type has already had a number assigned to it.<commit_after>package lifted

// Assign numbers to all things of type Name

import (
	"fmt"
	"os"
)

func (d *Domain) AssignNums(s *Symtab) os.Error {
	for i, _ := range d.Types {
		t := &d.Types[i]
		t.Name.numberType(s)
		for j, _ := range t.Type {
			t.Type[j].numberType(s)
		}
	}

	s.typeObjs = make([][]int, len(s.typeNames))
	numberConsts(s, d.Constants)

	for i, _ := range d.Predicates {
		p := &d.Predicates[i]
		p.Name.numberPred(s)
		for j, _ := range p.Parameters {
			parm := p.Parameters[j]
			for k, _ := range parm.Type {
				parm.Type[k].numberType(s)
			}
		}
	}

	for _, a := range d.Actions {
		if err := a.AssignNums(s); err != nil {
			return err
		}
	}
	return nil
}

func numberConsts(s *Symtab, consts []TypedName) {
	for i, _ := range consts {
		c := &consts[i]
		first := c.Name.numberConst(s)
		cnum := c.Name.Num
		for j, _ := range c.Type {
			c.Type[j].numberType(s)
			// If this is the 1st decl of this object
			// then add it to the table of all objects
			// of the given type
			if !first {
				tnum := c.Type[j].Num
				s.typeObjs[tnum] = append(s.typeObjs[tnum], cnum)
			}
		}
	}
}

func (a *Action) AssignNums(s *Symtab) os.Error {
	var f *numFrame
	for i, _ := range a.Parameters {
		p := &a.Parameters[i]
		f = p.Name.numberVar(s, f)
		for j, _ := range p.Type {
			p.Type[j].numberType(s)
		}
	}
	if err := a.Precondition.AssignNums(s, f); err != nil {
		return err
	}
	return a.Effect.AssignNums(s, f)
}

func (p *Problem) AssignNums(s *Symtab) os.Error {
	numberConsts(s, p.Objects)
	for _, init := range p.Init {
		if err := init.AssignNums(s, nil); err != nil {
			return err
		}
	}
	return p.Goal.AssignNums(s, nil)
}

func (l *Literal) AssignNums(s *Symtab, f *numFrame) os.Error {
	for i, t := range l.Parameters {
		name := &l.Parameters[i].Name
		switch t.Kind {
		case TermVariable:
			if fnxt := name.numberVar(s, f); fnxt == f {
				break
			}
			return fmt.Errorf("%s: Unbound variable %s\n", t.Loc, name.Str)
		case TermConstant:
			name.numberConst(s)
		}
	}
	l.Name.numberPred(s)
	return nil
}

func (e *ExprBinary) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (ExprTrue) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (ExprFalse) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *ExprAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprOr) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprBinary)(e).AssignNums(s, f)
}

func (e *ExprNot) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Expr.AssignNums(s, f)
}

func (e *ExprQuant) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		e.Variable.Type[i].numberType(s)
	}
	return e.Expr.AssignNums(s, f)
}

func (e *ExprForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprExists) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*ExprQuant)(e).AssignNums(s, f)
}

func (e *ExprLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectUnary) AssignNums(s *Symtab, f *numFrame) os.Error {
	return e.Effect.AssignNums(s, f)
}

func (EffectNone) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (e *EffectAnd) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Left.AssignNums(s, f); err != nil {
		return err
	}
	return e.Right.AssignNums(s, f)
}

func (e *EffectForall) AssignNums(s *Symtab, f *numFrame) os.Error {
	f = e.Variable.Name.numberVar(s, f)
	for i, _ := range e.Variable.Type {
		e.Variable.Type[i].numberType(s)
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectWhen) AssignNums(s *Symtab, f *numFrame) os.Error {
	if err := e.Condition.AssignNums(s, f); err != nil {
		return err
	}
	return e.Effect.AssignNums(s, f)
}

func (e *EffectLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(e).AssignNums(s, f)
}

func (e *EffectAssign) AssignNums(*Symtab, *numFrame) os.Error {
	return nil
}

func (i *InitLiteral) AssignNums(s *Symtab, f *numFrame) os.Error {
	return (*Literal)(i).AssignNums(s, f)
}

func (i *InitEq) AssignNums(s *Symtab, f *numFrame) os.Error {
	return nil
}

func (name *Name) numberType(s *Symtab) bool {
	if n, ok := s.typeNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.typeNames)
	s.typeNums[name.Str] = name.Num
	s.typeNames = append(s.typeNames, name.Str)
	return false
}

func (name *Name) numberConst(s *Symtab) bool {
	if n, ok := s.constNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.constNames)
	s.constNums[name.Str] = name.Num
	s.constNames = append(s.constNames, name.Str)
	return false
}

func (name *Name) numberPred(s *Symtab) bool {
	if n, ok := s.predNums[name.Str]; ok {
		name.Num = n
		return true
	}
	name.Num = len(s.predNames)
	s.predNums[name.Str] = name.Num
	s.predNames = append(s.predNames, name.Str)
	return false
}

func (name *Name) numberVar(s *Symtab, f *numFrame) *numFrame {
	if n, ok := f.lookup(name.Str); ok {
		name.Num = n
		return f
	}
	n := len(s.varNames)
	name.Num = n
	s.varNames = append(s.varNames, name.Str)
	return &numFrame{name: name.Str, num: n, up: f}
}

type numFrame struct {
	name string
	num  int
	up   *numFrame
}

func (f *numFrame) lookup(name string) (int, bool) {
	if f == nil {
		return 0, false
	}
	if f.name == name {
		return f.num, true
	}
	return f.up.lookup(name)
}<|endoftext|>


<commit_before>package linux

import (
	"io/ioutil"
	"os"
	"path/filepath"
)

type Process struct {
}

type ProcessStatus struct{}

func ReadProcess(pid int, baseDir string) (*Process, error) {
	var pidDir = filepath.Join(baseDir, strconv.Itoa(pid))

	if _, err := os.Stat(pidDir); err != nil {
		return nil, error
	}
	process := Process{}

	var ioFile = filepath.Join(pidDir, "io")
	var statFile = filepath.Join(pidDir, "stat")
	var statmFile = filepath.Join(pidDir, "statm")
	var statusFile = filepath.Join(pidDir, "status")

	return &process, nil
}

func ReadProcessStatus(path string) (*ProcessStatus, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	status := ProcessStatus{}
	return &status, nil
}

// process info reader
// https://github.com/sharyanto/scripts/blob/master/explain-proc-stat
<commit_msg>connect process info fields<commit_after>package linux

import (
	"io/ioutil"
	"os"
	"path/filepath"
)

type Process struct {
	Status ProcessStatus `json:"status"`
	Statm  ProcessStatm  `json:"statm"`
	Stat   ProcessStat   `json:"stat"`
	IO     ProcessIO     `json:"io"`
}

type ProcessStatus struct{}
type ProcessIO struct{}
type ProcessStatm struct{}
type ProcessStat struct{}

func ReadProcess(pid int, baseDir string) (*Process, error) {
	var pidDir = filepath.Join(baseDir, strconv.Itoa(pid))

	if _, err := os.Stat(pidDir); err != nil {
		return nil, error
	}
	process := Process{}

	var ioFile = filepath.Join(pidDir, "io")
	var statFile = filepath.Join(pidDir, "stat")
	var statmFile = filepath.Join(pidDir, "statm")
	var statusFile = filepath.Join(pidDir, "status")

	return &process, nil
}

func ReadProcessStatus(path string) (*ProcessStatus, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	status := ProcessStatus{}
	return &status, nil
}

// process info reader
// https://github.com/sharyanto/scripts/blob/master/explain-proc-stat
<|endoftext|>


<commit_before>package hoverfly

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

func TestHoverflyListener(t *testing.T) {
	server, dbClient := testTools(201, `{'message': 'here'}`)
	defer server.Close()

	proxyPort := "9777"

	dbClient.Cfg.ProxyPort = proxyPort
	// starting hoverfly
	proxy, _ := GetNewHoverfly(dbClient.Cfg, dbClient.Cache)
	StartHoverflyProxy(dbClient.Cfg, proxy)

	// checking whether it's running
	response, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	expect(t, err, nil)

	expect(t, response.StatusCode, 500)

	body, err := ioutil.ReadAll(response.Body)
	expect(t, err, nil)
	expect(t, strings.Contains(string(body), "is a proxy server"), true)
}

func TestStopHoverflyListener(t *testing.T) {
	server, dbClient := testTools(201, `{'message': 'here'}`)
	defer server.Close()

	proxyPort := "9778"

	dbClient.Cfg.ProxyPort = proxyPort
	// starting hoverfly
	proxy, _ := GetNewHoverfly(dbClient.Cfg, dbClient.Cache)
	StartHoverflyProxy(dbClient.Cfg, proxy)

	dbClient.Cfg.StopProxy()

	// checking whether it's stopped
	_, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	// should get error
	refute(t, err, nil)
}

func TestRestartHoverflyListener(t *testing.T) {
	server, dbClient := testTools(201, `{'message': 'here'}`)
	defer server.Close()

	proxyPort := "9779"

	dbClient.Cfg.ProxyPort = proxyPort
	// starting hoverfly
	proxy, _ := GetNewHoverfly(dbClient.Cfg, dbClient.Cache)
	StartHoverflyProxy(dbClient.Cfg, proxy)

	// checking whether it's running
	response, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	expect(t, err, nil)

	expect(t, response.StatusCode, 500)

	// stopping proxy
	dbClient.Cfg.StopProxy()

	// starting again
	StartHoverflyProxy(dbClient.Cfg, proxy)

	newResponse, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	expect(t, err, nil)
	expect(t, newResponse.StatusCode, 500)
}
<commit_msg>listener tests updated<commit_after>package hoverfly

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

func TestHoverflyListener(t *testing.T) {
	server, dbClient := testTools(201, `{'message': 'here'}`)
	defer server.Close()

	proxyPort := "9777"

	dbClient.Cfg.ProxyPort = proxyPort
	// starting hoverfly
	dbClient.UpdateProxy()
	dbClient.StartProxy()

	// checking whether it's running
	response, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	expect(t, err, nil)

	expect(t, response.StatusCode, 500)

	body, err := ioutil.ReadAll(response.Body)
	expect(t, err, nil)
	expect(t, strings.Contains(string(body), "is a proxy server"), true)
}

func TestStopHoverflyListener(t *testing.T) {
	server, dbClient := testTools(201, `{'message': 'here'}`)
	defer server.Close()

	proxyPort := "9778"

	dbClient.Cfg.ProxyPort = proxyPort
	// starting hoverfly
	dbClient.UpdateProxy()
	dbClient.StartProxy()

	dbClient.StopProxy()

	// checking whether it's stopped
	_, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	// should get error
	refute(t, err, nil)
}

func TestRestartHoverflyListener(t *testing.T) {
	server, dbClient := testTools(201, `{'message': 'here'}`)
	defer server.Close()

	proxyPort := "9779"

	dbClient.Cfg.ProxyPort = proxyPort
	// starting hoverfly
	dbClient.UpdateProxy()
	dbClient.StartProxy()

	// checking whether it's running
	response, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	expect(t, err, nil)

	expect(t, response.StatusCode, 500)

	// stopping proxy
	dbClient.StopProxy()

	// starting again
	dbClient.StartProxy()

	newResponse, err := http.Get(fmt.Sprintf("http://localhost:%s/", proxyPort))
	expect(t, err, nil)
	expect(t, newResponse.StatusCode, 500)
}
<|endoftext|>


<commit_before>package glesys

import (
	"context"
	"fmt"
)

// LoadBalancerService provides functions to interact with LoadBalancers
type LoadBalancerService struct {
	client clientInterface
}

// LoadBalancer represents a loadbalancer
type LoadBalancer struct {
	DataCenter string `json:"datacenter"`
	ID         string `json:"loadbalancerid"`
	Name       string `json:"name"`
}

// LoadBalancerDetails represents the detailed version of a load balancer
type LoadBalancerDetails struct {
	BackendsList  []LoadBalancerBackend  `json:"backends"`
	Blocklists    []string               `json:"blocklist"`
	Cost          LoadBalancerCost       `json:"cost"`
	DataCenter    string                 `json:"datacenter"`
	FrontendsList []LoadBalancerFrontend `json:"frontends"`
	ID            string                 `json:"loadbalancerid"`
	IPList        []LoadBalancerIP       `json:"ipaddress"`
	Name          string                 `json:"name"`
}

// LoadBalancerCost represents the cost details for a load balancer
type LoadBalancerCost struct {
	Amount     float64 `json:"amount"`
	Currency   string  `json:"currency"`
	Timeperiod string  `json:"timeperiod"`
}

// LoadBalancerIP represents a single load balancer IP
type LoadBalancerIP struct {
	Address         string  `json:"ipaddress"`
	Cost            float64 `json:"cost"`
	Currency        string  `json:"currency"`
	LockedToAccount bool    `json:"lockedtoaccount"`
	Version         int     `json:"version"`
}

// CreateLoadBalancerParams is used when creating a new loadbalancer
type CreateLoadBalancerParams struct {
	DataCenter string `json:"datacenter"`
	IPv4       string `json:"ip,omitempty"`
	IPv6       string `json:"ipv6,omitempty"`
	Name       string `json:"name"`
}

// EditLoadBalancerParams is used when editing a loadbalancer
type EditLoadBalancerParams struct {
	Name string `json:"name"`
}

// LoadBalancerBackend represents a LoadBalancer Backend
type LoadBalancerBackend struct {
	ConnectTimeout  int      `json:"connecttimeout"`
	Mode            string   `json:"mode"`
	Name            string   `json:"name"`
	ResponseTimeout int      `json:"responsetimeout"`
	Status          string   `json:"status"`
	StickySession   string   `json:"stickysessions"`
	Targets         []Target `json:"targets"`
}

// AddBackendParams used when creating backends
type AddBackendParams struct {
	ConnectTimeout  int    `json:"connecttimeout,omitempty"`
	Mode            string `json:"mode,omitempty"`
	Name            string `json:"name"`
	ResponseTimeout int    `json:"responsetimeout,omitempty"`
	StickySession   string `json:"stickysession,omitempty"`
}

// EditBackendParams used to edit a backend
type EditBackendParams struct {
	ConnectTimeout  int    `json:"connecttimeout,omitempty"`
	Mode            string `json:"mode,omitempty"`
	Name            string `json:"backendname"`
	ResponseTimeout int    `json:"responsetimeout,omitempty"`
	StickySession   string `json:"stickysession,omitempty"`
}

// RemoveBackendParams used when removing
type RemoveBackendParams struct {
	Name string `json:"backendname"`
}

// AddCertificateParams represents a certificate name and content
type AddCertificateParams struct {
	Name        string `json:"certificatename"`
	Certificate string `json:"certificate"`
}

// LoadBalancerFrontend represents a LoadBalancer Frontend
type LoadBalancerFrontend struct {
	Backend        string `json:"backend"`
	ClientTimeout  int    `json:"clienttimeout"`
	MaxConnections int    `json:"maxconnections"`
	Name           string `json:"name"`
	Port           int    `json:"port"`
	Status         string `json:"status"`
	SSLCertificate string `json:"sslcertificate"`
}

// AddFrontendParams used when creating frontends
type AddFrontendParams struct {
	Backend        string `json:"backendname"`
	ClientTimeout  int    `json:"clienttimeout,omitempty"`
	MaxConnections int    `json:"maxconnections,omitempty"`
	Name           string `json:"name"`
	Port           int    `json:"port"`
	SSLCertificate string `json:"sslcertificate,omitempty"`
}

// EditFrontendParams used to edit a frontend
type EditFrontendParams struct {
	ClientTimeout  int    `json:"clienttimeout,omitempty"`
	MaxConnections int    `json:"maxconnections,omitempty"`
	Name           string `json:"frontendname"`
	Port           int    `json:"port,omitempty"`
	SSLCertificate string `json:"sslcertificate,omitempty"`
}

// RemoveFrontendParams used when removing
type RemoveFrontendParams struct {
	Name string `json:"frontendname"`
}

// Target is used in backends
type Target struct {
	Enabled  bool   `json:"enabled"`
	Name     string `json:"name"`
	Port     int    `json:"port"`
	Status   string `json:"status"`
	TargetIP string `json:"ipaddress"`
	Weight   int    `json:"weight"`
}

// AddTargetParams used when creating targets
type AddTargetParams struct {
	Backend  string `json:"backendname"`
	Name     string `json:"name"`
	Port     int    `json:"port"`
	TargetIP string `json:"ipaddress"`
	Weight   int    `json:"weight"`
}

// EditTargetParams used when editing targets
type EditTargetParams struct {
	Backend  string `json:"backendname"`
	Name     string `json:"targetname"`
	Port     int    `json:"port,omitempty"`
	TargetIP string `json:"ipaddress,omitempty"`
	Weight   int    `json:"weight,omitempty"`
}

// RemoveTargetParams used when removing targets
type RemoveTargetParams struct {
	Backend string `json:"backendname"`
	Name    string `json:"name"`
}

// BlocklistParams set prefix to add/delete
type BlocklistParams struct {
	Prefix string `json:"prefix"`
}

// ToggleTargetParams used when enabling/disabling targets
type ToggleTargetParams struct {
	Backend string `json:"backendname"`
	Name    string `json:"targetname"`
}

// Create creates a new loadbalancer
func (lb *LoadBalancerService) Create(context context.Context, params CreateLoadBalancerParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/create", &data, params)
	return &data.Response.LoadBalancer, err
}

// Destroy deletes a loadbalancer
func (lb *LoadBalancerService) Destroy(context context.Context, loadbalancerID string) error {
	return lb.client.post(context, "loadbalancer/destroy", nil, struct {
		LoadBalancerID string `json:"loadbalancerid"`
	}{loadbalancerID})
}

// Details returns a detailed information about one loadbalancer
func (lb *LoadBalancerService) Details(context context.Context, loadbalancerID string) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.get(context, fmt.Sprintf("loadbalancer/details/loadbalancerid/%s", loadbalancerID), &data)
	return &data.Response.LoadBalancer, err
}

// Edit edits a loadbalancer
func (lb *LoadBalancerService) Edit(context context.Context, loadbalancerID string, params EditLoadBalancerParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/edit", &data, struct {
		EditLoadBalancerParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// List returns a list of loadbalancers
func (lb *LoadBalancerService) List(context context.Context) (*[]LoadBalancer, error) {
	data := struct {
		Response struct {
			LoadBalancers []LoadBalancer
		}
	}{}
	err := lb.client.get(context, "loadbalancer/list", &data)
	return &data.Response.LoadBalancers, err
}

// AddBackend creates a new backend used by the loadbalancer specified
func (lb *LoadBalancerService) AddBackend(context context.Context, loadbalancerID string, params AddBackendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addbackend", &data, struct {
		AddBackendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EditBackend edits a Backend
func (lb *LoadBalancerService) EditBackend(context context.Context, loadbalancerID string, params EditBackendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/editbackend", &data, struct {
		EditBackendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveBackend deletes a backend
func (lb *LoadBalancerService) RemoveBackend(context context.Context, loadbalancerID string, params RemoveBackendParams) error {
	return lb.client.post(context, "loadbalancer/removebackend", nil, struct {
		RemoveBackendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddFrontend creates a new frontend used by the loadbalancer specified
func (lb *LoadBalancerService) AddFrontend(context context.Context, loadbalancerID string, params AddFrontendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addfrontend", &data, struct {
		AddFrontendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EditFrontend edits a frontend
func (lb *LoadBalancerService) EditFrontend(context context.Context, loadbalancerID string, params EditFrontendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/editfrontend", &data, struct {
		EditFrontendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveFrontend deletes a frontend
func (lb *LoadBalancerService) RemoveFrontend(context context.Context, loadbalancerID string, params RemoveFrontendParams) error {
	return lb.client.post(context, "loadbalancer/removefrontend", nil, struct {
		RemoveFrontendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddCertificate adds a certificate to the loadbalancer specified
func (lb *LoadBalancerService) AddCertificate(context context.Context, loadbalancerID string, params AddCertificateParams) error {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	return lb.client.post(context, "loadbalancer/addcertificate", &data, struct {
		AddCertificateParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// ListCertificates list certificates for the LoadBalancer
func (lb *LoadBalancerService) ListCertificates(context context.Context, loadbalancerID string) (*[]string, error) {
	data := struct {
		Response struct {
			Certificates []string `json:"certificate"`
		}
	}{}
	err := lb.client.post(context, "loadbalancer/listcertificate", &data, struct {
		LoadBalancerID string `json:"loadbalancerid"`
	}{loadbalancerID})

	return &data.Response.Certificates, err
}

// RemoveCertificate deletes a certificate from the loadbalancer
func (lb *LoadBalancerService) RemoveCertificate(context context.Context, loadbalancerID string, params string) error {
	return lb.client.post(context, "loadbalancer/removecertificate", nil, struct {
		CertificateName string `json:"certificatename"`
		LoadBalancerID  string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddTarget adds a target to the backend specified
func (lb *LoadBalancerService) AddTarget(context context.Context, loadbalancerID string, params AddTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addtarget", &data, struct {
		AddTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EditTarget edits a target for the specified backend
func (lb *LoadBalancerService) EditTarget(context context.Context, loadbalancerID string, params EditTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/edittarget", &data, struct {
		EditTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EnableTarget enables a target for the specified LoadBalancerBackend
func (lb *LoadBalancerService) EnableTarget(context context.Context, loadbalancerID string, params ToggleTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/enabletarget", &data, struct {
		ToggleTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// DisableTarget disables the specified target for the LoadBalancerBackend
func (lb *LoadBalancerService) DisableTarget(context context.Context, loadbalancerID string, params ToggleTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/disabletarget", &data, struct {
		ToggleTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveTarget deletes a target from the specified LoadBalancerBackend
func (lb *LoadBalancerService) RemoveTarget(context context.Context, loadbalancerID string, params RemoveTargetParams) error {
	return lb.client.post(context, "loadbalancer/removetarget", nil, struct {
		RemoveTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddToBlocklist adds a prefix to loadbalancer blocklist
func (lb *LoadBalancerService) AddToBlocklist(context context.Context, loadbalancerID string, params BlocklistParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addtoblocklist", &data, struct {
		BlocklistParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveFromBlocklist deletes a prefix from the LoadBalancer blocklist
func (lb *LoadBalancerService) RemoveFromBlocklist(context context.Context, loadbalancerID string, params BlocklistParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/removefromblocklist", &data, struct {
		BlocklistParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}
<commit_msg>Remove unused data struct from lb AddCertificate<commit_after>package glesys

import (
	"context"
	"fmt"
)

// LoadBalancerService provides functions to interact with LoadBalancers
type LoadBalancerService struct {
	client clientInterface
}

// LoadBalancer represents a loadbalancer
type LoadBalancer struct {
	DataCenter string `json:"datacenter"`
	ID         string `json:"loadbalancerid"`
	Name       string `json:"name"`
}

// LoadBalancerDetails represents the detailed version of a load balancer
type LoadBalancerDetails struct {
	BackendsList  []LoadBalancerBackend  `json:"backends"`
	Blocklists    []string               `json:"blocklist"`
	Cost          LoadBalancerCost       `json:"cost"`
	DataCenter    string                 `json:"datacenter"`
	FrontendsList []LoadBalancerFrontend `json:"frontends"`
	ID            string                 `json:"loadbalancerid"`
	IPList        []LoadBalancerIP       `json:"ipaddress"`
	Name          string                 `json:"name"`
}

// LoadBalancerCost represents the cost details for a load balancer
type LoadBalancerCost struct {
	Amount     float64 `json:"amount"`
	Currency   string  `json:"currency"`
	Timeperiod string  `json:"timeperiod"`
}

// LoadBalancerIP represents a single load balancer IP
type LoadBalancerIP struct {
	Address         string  `json:"ipaddress"`
	Cost            float64 `json:"cost"`
	Currency        string  `json:"currency"`
	LockedToAccount bool    `json:"lockedtoaccount"`
	Version         int     `json:"version"`
}

// CreateLoadBalancerParams is used when creating a new loadbalancer
type CreateLoadBalancerParams struct {
	DataCenter string `json:"datacenter"`
	IPv4       string `json:"ip,omitempty"`
	IPv6       string `json:"ipv6,omitempty"`
	Name       string `json:"name"`
}

// EditLoadBalancerParams is used when editing a loadbalancer
type EditLoadBalancerParams struct {
	Name string `json:"name"`
}

// LoadBalancerBackend represents a LoadBalancer Backend
type LoadBalancerBackend struct {
	ConnectTimeout  int      `json:"connecttimeout"`
	Mode            string   `json:"mode"`
	Name            string   `json:"name"`
	ResponseTimeout int      `json:"responsetimeout"`
	Status          string   `json:"status"`
	StickySession   string   `json:"stickysessions"`
	Targets         []Target `json:"targets"`
}

// AddBackendParams used when creating backends
type AddBackendParams struct {
	ConnectTimeout  int    `json:"connecttimeout,omitempty"`
	Mode            string `json:"mode,omitempty"`
	Name            string `json:"name"`
	ResponseTimeout int    `json:"responsetimeout,omitempty"`
	StickySession   string `json:"stickysession,omitempty"`
}

// EditBackendParams used to edit a backend
type EditBackendParams struct {
	ConnectTimeout  int    `json:"connecttimeout,omitempty"`
	Mode            string `json:"mode,omitempty"`
	Name            string `json:"backendname"`
	ResponseTimeout int    `json:"responsetimeout,omitempty"`
	StickySession   string `json:"stickysession,omitempty"`
}

// RemoveBackendParams used when removing
type RemoveBackendParams struct {
	Name string `json:"backendname"`
}

// AddCertificateParams represents a certificate name and content
type AddCertificateParams struct {
	Name        string `json:"certificatename"`
	Certificate string `json:"certificate"`
}

// LoadBalancerFrontend represents a LoadBalancer Frontend
type LoadBalancerFrontend struct {
	Backend        string `json:"backend"`
	ClientTimeout  int    `json:"clienttimeout"`
	MaxConnections int    `json:"maxconnections"`
	Name           string `json:"name"`
	Port           int    `json:"port"`
	Status         string `json:"status"`
	SSLCertificate string `json:"sslcertificate"`
}

// AddFrontendParams used when creating frontends
type AddFrontendParams struct {
	Backend        string `json:"backendname"`
	ClientTimeout  int    `json:"clienttimeout,omitempty"`
	MaxConnections int    `json:"maxconnections,omitempty"`
	Name           string `json:"name"`
	Port           int    `json:"port"`
	SSLCertificate string `json:"sslcertificate,omitempty"`
}

// EditFrontendParams used to edit a frontend
type EditFrontendParams struct {
	ClientTimeout  int    `json:"clienttimeout,omitempty"`
	MaxConnections int    `json:"maxconnections,omitempty"`
	Name           string `json:"frontendname"`
	Port           int    `json:"port,omitempty"`
	SSLCertificate string `json:"sslcertificate,omitempty"`
}

// RemoveFrontendParams used when removing
type RemoveFrontendParams struct {
	Name string `json:"frontendname"`
}

// Target is used in backends
type Target struct {
	Enabled  bool   `json:"enabled"`
	Name     string `json:"name"`
	Port     int    `json:"port"`
	Status   string `json:"status"`
	TargetIP string `json:"ipaddress"`
	Weight   int    `json:"weight"`
}

// AddTargetParams used when creating targets
type AddTargetParams struct {
	Backend  string `json:"backendname"`
	Name     string `json:"name"`
	Port     int    `json:"port"`
	TargetIP string `json:"ipaddress"`
	Weight   int    `json:"weight"`
}

// EditTargetParams used when editing targets
type EditTargetParams struct {
	Backend  string `json:"backendname"`
	Name     string `json:"targetname"`
	Port     int    `json:"port,omitempty"`
	TargetIP string `json:"ipaddress,omitempty"`
	Weight   int    `json:"weight,omitempty"`
}

// RemoveTargetParams used when removing targets
type RemoveTargetParams struct {
	Backend string `json:"backendname"`
	Name    string `json:"name"`
}

// BlocklistParams set prefix to add/delete
type BlocklistParams struct {
	Prefix string `json:"prefix"`
}

// ToggleTargetParams used when enabling/disabling targets
type ToggleTargetParams struct {
	Backend string `json:"backendname"`
	Name    string `json:"targetname"`
}

// Create creates a new loadbalancer
func (lb *LoadBalancerService) Create(context context.Context, params CreateLoadBalancerParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/create", &data, params)
	return &data.Response.LoadBalancer, err
}

// Destroy deletes a loadbalancer
func (lb *LoadBalancerService) Destroy(context context.Context, loadbalancerID string) error {
	return lb.client.post(context, "loadbalancer/destroy", nil, struct {
		LoadBalancerID string `json:"loadbalancerid"`
	}{loadbalancerID})
}

// Details returns a detailed information about one loadbalancer
func (lb *LoadBalancerService) Details(context context.Context, loadbalancerID string) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.get(context, fmt.Sprintf("loadbalancer/details/loadbalancerid/%s", loadbalancerID), &data)
	return &data.Response.LoadBalancer, err
}

// Edit edits a loadbalancer
func (lb *LoadBalancerService) Edit(context context.Context, loadbalancerID string, params EditLoadBalancerParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/edit", &data, struct {
		EditLoadBalancerParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// List returns a list of loadbalancers
func (lb *LoadBalancerService) List(context context.Context) (*[]LoadBalancer, error) {
	data := struct {
		Response struct {
			LoadBalancers []LoadBalancer
		}
	}{}
	err := lb.client.get(context, "loadbalancer/list", &data)
	return &data.Response.LoadBalancers, err
}

// AddBackend creates a new backend used by the loadbalancer specified
func (lb *LoadBalancerService) AddBackend(context context.Context, loadbalancerID string, params AddBackendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addbackend", &data, struct {
		AddBackendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EditBackend edits a Backend
func (lb *LoadBalancerService) EditBackend(context context.Context, loadbalancerID string, params EditBackendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/editbackend", &data, struct {
		EditBackendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveBackend deletes a backend
func (lb *LoadBalancerService) RemoveBackend(context context.Context, loadbalancerID string, params RemoveBackendParams) error {
	return lb.client.post(context, "loadbalancer/removebackend", nil, struct {
		RemoveBackendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddFrontend creates a new frontend used by the loadbalancer specified
func (lb *LoadBalancerService) AddFrontend(context context.Context, loadbalancerID string, params AddFrontendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addfrontend", &data, struct {
		AddFrontendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EditFrontend edits a frontend
func (lb *LoadBalancerService) EditFrontend(context context.Context, loadbalancerID string, params EditFrontendParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/editfrontend", &data, struct {
		EditFrontendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveFrontend deletes a frontend
func (lb *LoadBalancerService) RemoveFrontend(context context.Context, loadbalancerID string, params RemoveFrontendParams) error {
	return lb.client.post(context, "loadbalancer/removefrontend", nil, struct {
		RemoveFrontendParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddCertificate adds a certificate to the loadbalancer specified
func (lb *LoadBalancerService) AddCertificate(context context.Context, loadbalancerID string, params AddCertificateParams) error {
	return lb.client.post(context, "loadbalancer/addcertificate", nil, struct {
		AddCertificateParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// ListCertificates list certificates for the LoadBalancer
func (lb *LoadBalancerService) ListCertificates(context context.Context, loadbalancerID string) (*[]string, error) {
	data := struct {
		Response struct {
			Certificates []string `json:"certificate"`
		}
	}{}
	err := lb.client.post(context, "loadbalancer/listcertificate", &data, struct {
		LoadBalancerID string `json:"loadbalancerid"`
	}{loadbalancerID})

	return &data.Response.Certificates, err
}

// RemoveCertificate deletes a certificate from the loadbalancer
func (lb *LoadBalancerService) RemoveCertificate(context context.Context, loadbalancerID string, params string) error {
	return lb.client.post(context, "loadbalancer/removecertificate", nil, struct {
		CertificateName string `json:"certificatename"`
		LoadBalancerID  string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddTarget adds a target to the backend specified
func (lb *LoadBalancerService) AddTarget(context context.Context, loadbalancerID string, params AddTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addtarget", &data, struct {
		AddTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EditTarget edits a target for the specified backend
func (lb *LoadBalancerService) EditTarget(context context.Context, loadbalancerID string, params EditTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/edittarget", &data, struct {
		EditTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// EnableTarget enables a target for the specified LoadBalancerBackend
func (lb *LoadBalancerService) EnableTarget(context context.Context, loadbalancerID string, params ToggleTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/enabletarget", &data, struct {
		ToggleTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// DisableTarget disables the specified target for the LoadBalancerBackend
func (lb *LoadBalancerService) DisableTarget(context context.Context, loadbalancerID string, params ToggleTargetParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/disabletarget", &data, struct {
		ToggleTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveTarget deletes a target from the specified LoadBalancerBackend
func (lb *LoadBalancerService) RemoveTarget(context context.Context, loadbalancerID string, params RemoveTargetParams) error {
	return lb.client.post(context, "loadbalancer/removetarget", nil, struct {
		RemoveTargetParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
}

// AddToBlocklist adds a prefix to loadbalancer blocklist
func (lb *LoadBalancerService) AddToBlocklist(context context.Context, loadbalancerID string, params BlocklistParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/addtoblocklist", &data, struct {
		BlocklistParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}

// RemoveFromBlocklist deletes a prefix from the LoadBalancer blocklist
func (lb *LoadBalancerService) RemoveFromBlocklist(context context.Context, loadbalancerID string, params BlocklistParams) (*LoadBalancerDetails, error) {
	data := struct {
		Response struct {
			LoadBalancer LoadBalancerDetails
		}
	}{}
	err := lb.client.post(context, "loadbalancer/removefromblocklist", &data, struct {
		BlocklistParams
		LoadBalancerID string `json:"loadbalancerid"`
	}{params, loadbalancerID})
	return &data.Response.LoadBalancer, err
}
<|endoftext|>