<commit_before>/*
Copyright 2017 The Kedge Authors All rights reserved.

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 kubernetes

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/kedgeproject/kedge/pkg/spec"

	log "github.com/Sirupsen/logrus"
	"github.com/davecgh/go-spew/spew"
	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/pkg/api"
	api_v1 "k8s.io/client-go/pkg/api/v1"
	ext_v1beta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"

	// install api (register and add types to api.Schema)
	_ "k8s.io/client-go/pkg/api/install"
	_ "k8s.io/client-go/pkg/apis/extensions/install"
)

func getLabels(app *spec.App) map[string]string {
	labels := map[string]string{"app": app.Name}
	return labels
}

func createIngresses(app *spec.App) ([]runtime.Object, error) {
	var ings []runtime.Object

	for _, i := range app.Ingresses {
		ing := &ext_v1beta1.Ingress{
			ObjectMeta: metav1.ObjectMeta{
				Name:   i.Name,
				Labels: app.Labels,
			},
			Spec: i.IngressSpec,
		}
		ings = append(ings, ing)
	}
	return ings, nil
}

func createServices(app *spec.App) ([]runtime.Object, error) {
	var svcs []runtime.Object
	for _, s := range app.Services {
		svc := &api_v1.Service{
			ObjectMeta: metav1.ObjectMeta{
				Name:   s.Name,
				Labels: app.Labels,
			},
			Spec: s.ServiceSpec,
		}
		for _, servicePortMod := range s.Ports {
			svc.Spec.Ports = append(svc.Spec.Ports, servicePortMod.ServicePort)
		}
		if len(svc.Spec.Selector) == 0 {
			svc.Spec.Selector = app.Labels
		}
		svcs = append(svcs, svc)

		// Generate ingress if "endpoint" is mentioned in app.Services.Ports[].Endpoint
		for _, port := range s.Ports {
			if port.Endpoint != "" {
				var host string
				var path string
				endpoint := strings.SplitN(port.Endpoint, "/", 2)
				switch len(endpoint) {
				case 1:
					host = endpoint[0]
					path = "/"
				case 2:
					host = endpoint[0]
					path = "/" + endpoint[1]
				default:
					return nil, fmt.Errorf("Invalid syntax for endpoint: %v", port.Endpoint)
				}

				ingressName := s.Name + "-" + strconv.FormatInt(int64(port.Port), 10)
				endpointIngress := &ext_v1beta1.Ingress{
					ObjectMeta: metav1.ObjectMeta{
						Name:   ingressName,
						Labels: app.Labels,
					},
					Spec: ext_v1beta1.IngressSpec{
						Rules: []ext_v1beta1.IngressRule{
							{
								Host: host,
								IngressRuleValue: ext_v1beta1.IngressRuleValue{
									HTTP: &ext_v1beta1.HTTPIngressRuleValue{
										Paths: []ext_v1beta1.HTTPIngressPath{
											{
												Path: path,
												Backend: ext_v1beta1.IngressBackend{
													ServiceName: s.Name,
													ServicePort: intstr.IntOrString{
														IntVal: port.Port,
													},
												},
											},
										},
									},
								},
							},
						},
					},
				}
				svcs = append(svcs, endpointIngress)
			}
		}
	}
	return svcs, nil
}

// Creates a Deployment Kubernetes resource. The returned Deployment resource
// will be nil if it could not be generated due to insufficient input data.
func createDeployment(app *spec.App) (*ext_v1beta1.Deployment, error) {

	// We need to error out if both, app.PodSpec and app.DeploymentSpec are empty
	if reflect.DeepEqual(app.PodSpec, api_v1.PodSpec{}) && reflect.DeepEqual(app.DeploymentSpec, ext_v1beta1.DeploymentSpec{}) {
		log.Debug("Both, app.PodSpec and app.DeploymentSpec are empty, not enough data to create a deployment.")
		return nil, nil
	}

	// We are merging whole DeploymentSpec with PodSpec.
	// This means that someone could specify containers in template.spec and also in top level PodSpec.
	// This stupid check is supposed to make sure that only one of them set.
	// TODO: merge DeploymentSpec.Template.Spec and top level PodSpec
	if !(reflect.DeepEqual(app.DeploymentSpec.Template.Spec, api_v1.PodSpec{}) || reflect.DeepEqual(app.PodSpec, api_v1.PodSpec{})) {
		return nil, fmt.Errorf("Pod can't be specfied in two places. Use top level PodSpec or template.spec (DeploymentSpec.Template.Spec) not both")
	}

	deploymentSpec := app.DeploymentSpec

	// top level PodSpec is not empty, use it for deployment template
	// we already know that if app.PodSpec is not empty app.DeploymentSpec.Template.Spec is empty
	if !reflect.DeepEqual(app.PodSpec, api_v1.PodSpec{}) {
		deploymentSpec.Template.Spec = app.PodSpec
	}

	// TODO: check if this wasn't set by user, in that case we shouldn't ovewrite it
	deploymentSpec.Template.ObjectMeta.Name = app.Name

	// TODO: merge with already existing labels and avoid duplication
	deploymentSpec.Template.ObjectMeta.Labels = app.Labels

	deployment := ext_v1beta1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:   app.Name,
			Labels: app.Labels,
		},
		Spec: deploymentSpec,
	}

	return &deployment, nil
}

// create PVC reading the root level persistent volume field
func createPVC(v spec.VolumeClaim, labels map[string]string) (*api_v1.PersistentVolumeClaim, error) {
	// check for conditions where user has given both conflicting fields
	// or not given either fields
	if v.Size != "" && v.Resources.Requests != nil {
		return nil, fmt.Errorf("persistent volume %q, cannot provide size and resources at the same time", v.Name)
	}
	if v.Size == "" && v.Resources.Requests == nil {
		return nil, fmt.Errorf("persistent volume %q, please provide size or resources, none given", v.Name)
	}

	// if user has given size then create a "api_v1.ResourceRequirements"
	// because this can be fed to pvc directly
	if v.Size != "" {
		size, err := resource.ParseQuantity(v.Size)
		if err != nil {
			return nil, errors.Wrap(err, "could not read volume size")
		}
		// update the volume's resource so that it can be fed
		v.Resources = api_v1.ResourceRequirements{
			Requests: api_v1.ResourceList{
				api_v1.ResourceStorage: size,
			},
		}
	}
	// setting the default accessmode if none given by user
	if len(v.AccessModes) == 0 {
		v.AccessModes = []api_v1.PersistentVolumeAccessMode{api_v1.ReadWriteOnce}
	}
	pvc := &api_v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:   v.Name,
			Labels: labels,
		},
		// since we updated the pvc spec before so this can be directly fed
		// without having to do any addition extra
		Spec: api_v1.PersistentVolumeClaimSpec(v.PersistentVolumeClaimSpec),
	}
	return pvc, nil
}

// Since we are automatically creating pvc from
// root level persistent volume and entry in the container
// volume mount, we also need to update the pod's volume field
func populateVolumes(app *spec.App) error {
	for cn, c := range app.PodSpec.Containers {
		for vn, vm := range c.VolumeMounts {
			if isPVCDefined(app.VolumeClaims, vm.Name) && !isVolumeDefined(app.Volumes, vm.Name) {
				app.Volumes = append(app.Volumes, api_v1.Volume{
					Name: vm.Name,
					VolumeSource: api_v1.VolumeSource{
						PersistentVolumeClaim: &api_v1.PersistentVolumeClaimVolumeSource{
							ClaimName: vm.Name,
						},
					},
				})
			} else if !isVolumeDefined(app.Volumes, vm.Name) {
				// pvc is not defined so we need to check if the entry is made in the pod volumes
				// since a volumeMount entry without entry in pod level volumes might cause failure
				// while deployment since that would not be a complete configuration
				return fmt.Errorf("neither root level Persistent Volume"+
					" nor Volume in pod spec defined for %q, "+
					"in app.containers[%d].volumeMounts[%d]", vm.Name, cn, vn)
			}
		}
	}
	return nil
}

func createSecrets(app *spec.App) ([]runtime.Object, error) {
	var secrets []runtime.Object

	for _, s := range app.Secrets {
		secret := &api_v1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:   s.Name,
				Labels: app.Labels,
			},
			Data:       s.Data,
			StringData: s.StringData,
			Type:       s.Type,
		}
		secrets = append(secrets, secret)
	}
	return secrets, nil
}

// CreateK8sObjects, if given object spec.App, this function reads
// them and returns kubernetes objects as list of runtime.Object
// If the app is using field 'extraResources' then it will
// also return file names mentioned there as list of string
func CreateK8sObjects(app *spec.App) ([]runtime.Object, []string, error) {
	var objects []runtime.Object

	if app.Labels == nil {
		app.Labels = getLabels(app)
	}

	svcs, err := createServices(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Unable to create Kubernetes Service")
	}

	ings, err := createIngresses(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Unable to create Kubernetes Ingresses")
	}

	secs, err := createSecrets(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Unable to create Kubernetes Secrets")
	}

	app.PodSpec.Containers, err = populateContainers(app.Containers, app.ConfigMaps, app.Secrets)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}
	log.Debugf("object after population: %#v\n", app)

	app.PodSpec.InitContainers, err = populateContainers(app.InitContainers, app.ConfigMaps, app.Secrets)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}
	log.Debugf("object after population: %#v\n", app)

	// create pvc for each root level persistent volume
	var pvcs []runtime.Object
	for _, v := range app.VolumeClaims {
		pvc, err := createPVC(v, app.Labels)
		if err != nil {
			return nil, nil, errors.Wrapf(err, "app %q", app.Name)
		}
		pvcs = append(pvcs, pvc)
	}
	if err := populateVolumes(app); err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}

	var configMap []runtime.Object
	for _, cd := range app.ConfigMaps {
		cm := &api_v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name:   cd.Name,
				Labels: app.Labels,
			},
			Data: cd.Data,
		}

		configMap = append(configMap, cm)
	}

	deployment, err := createDeployment(app)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}

	// deployment will be nil if no deployment is generated and no error occurs,
	// so we only need to append this when a legit deployment resource is returned
	if deployment != nil {
		objects = append(objects, deployment)
		log.Debugf("app: %s, deployment: %s\n", app.Name, spew.Sprint(deployment))
	}
	objects = append(objects, configMap...)
	log.Debugf("app: %s, configMap: %s\n", app.Name, spew.Sprint(configMap))

	objects = append(objects, svcs...)
	log.Debugf("app: %s, service: %s\n", app.Name, spew.Sprint(svcs))

	objects = append(objects, ings...)
	log.Debugf("app: %s, ingress: %s\n", app.Name, spew.Sprint(ings))

	objects = append(objects, pvcs...)
	log.Debugf("app: %s, pvc: %s\n", app.Name, spew.Sprint(pvcs))

	objects = append(objects, secs...)
	log.Debugf("app: %s, secret: %s\n", app.Name, spew.Sprint(secs))

	return objects, app.ExtraResources, nil
}

// Transform function if given spec.App data creates the versioned
// kubernetes objects and returns them in list of runtime.Object
// And if the field in spec.App called 'extraResources' is used
// then it returns the filenames mentioned there as list of string
func Transform(app *spec.App) ([]runtime.Object, []string, error) {

	runtimeObjects, extraResources, err := CreateK8sObjects(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "failed to create Kubernetes objects")
	}

	if len(runtimeObjects) == 0 {
		return nil, nil, errors.New("No runtime objects created, possibly because not enough input data was passed")
	}

	for _, runtimeObject := range runtimeObjects {

		gvk, isUnversioned, err := api.Scheme.ObjectKind(runtimeObject)
		if err != nil {
			return nil, nil, errors.Wrap(err, "ConvertToVersion failed")
		}
		if isUnversioned {
			return nil, nil, fmt.Errorf("ConvertToVersion failed: can't output unversioned type: %T", runtimeObject)
		}

		runtimeObject.GetObjectKind().SetGroupVersionKind(gvk)
	}

	return runtimeObjects, extraResources, nil
}
<commit_msg>order artifacts generation<commit_after>/*
Copyright 2017 The Kedge Authors All rights reserved.

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 kubernetes

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/kedgeproject/kedge/pkg/spec"

	log "github.com/Sirupsen/logrus"
	"github.com/davecgh/go-spew/spew"
	"github.com/pkg/errors"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/pkg/api"
	api_v1 "k8s.io/client-go/pkg/api/v1"
	ext_v1beta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1"

	// install api (register and add types to api.Schema)
	_ "k8s.io/client-go/pkg/api/install"
	_ "k8s.io/client-go/pkg/apis/extensions/install"
)

func getLabels(app *spec.App) map[string]string {
	labels := map[string]string{"app": app.Name}
	return labels
}

func createIngresses(app *spec.App) ([]runtime.Object, error) {
	var ings []runtime.Object

	for _, i := range app.Ingresses {
		ing := &ext_v1beta1.Ingress{
			ObjectMeta: metav1.ObjectMeta{
				Name:   i.Name,
				Labels: app.Labels,
			},
			Spec: i.IngressSpec,
		}
		ings = append(ings, ing)
	}
	return ings, nil
}

func createServices(app *spec.App) ([]runtime.Object, error) {
	var svcs []runtime.Object
	for _, s := range app.Services {
		svc := &api_v1.Service{
			ObjectMeta: metav1.ObjectMeta{
				Name:   s.Name,
				Labels: app.Labels,
			},
			Spec: s.ServiceSpec,
		}
		for _, servicePortMod := range s.Ports {
			svc.Spec.Ports = append(svc.Spec.Ports, servicePortMod.ServicePort)
		}
		if len(svc.Spec.Selector) == 0 {
			svc.Spec.Selector = app.Labels
		}
		svcs = append(svcs, svc)

		// Generate ingress if "endpoint" is mentioned in app.Services.Ports[].Endpoint
		for _, port := range s.Ports {
			if port.Endpoint != "" {
				var host string
				var path string
				endpoint := strings.SplitN(port.Endpoint, "/", 2)
				switch len(endpoint) {
				case 1:
					host = endpoint[0]
					path = "/"
				case 2:
					host = endpoint[0]
					path = "/" + endpoint[1]
				default:
					return nil, fmt.Errorf("Invalid syntax for endpoint: %v", port.Endpoint)
				}

				ingressName := s.Name + "-" + strconv.FormatInt(int64(port.Port), 10)
				endpointIngress := &ext_v1beta1.Ingress{
					ObjectMeta: metav1.ObjectMeta{
						Name:   ingressName,
						Labels: app.Labels,
					},
					Spec: ext_v1beta1.IngressSpec{
						Rules: []ext_v1beta1.IngressRule{
							{
								Host: host,
								IngressRuleValue: ext_v1beta1.IngressRuleValue{
									HTTP: &ext_v1beta1.HTTPIngressRuleValue{
										Paths: []ext_v1beta1.HTTPIngressPath{
											{
												Path: path,
												Backend: ext_v1beta1.IngressBackend{
													ServiceName: s.Name,
													ServicePort: intstr.IntOrString{
														IntVal: port.Port,
													},
												},
											},
										},
									},
								},
							},
						},
					},
				}
				svcs = append(svcs, endpointIngress)
			}
		}
	}
	return svcs, nil
}

// Creates a Deployment Kubernetes resource. The returned Deployment resource
// will be nil if it could not be generated due to insufficient input data.
func createDeployment(app *spec.App) (*ext_v1beta1.Deployment, error) {

	// We need to error out if both, app.PodSpec and app.DeploymentSpec are empty
	if reflect.DeepEqual(app.PodSpec, api_v1.PodSpec{}) && reflect.DeepEqual(app.DeploymentSpec, ext_v1beta1.DeploymentSpec{}) {
		log.Debug("Both, app.PodSpec and app.DeploymentSpec are empty, not enough data to create a deployment.")
		return nil, nil
	}

	// We are merging whole DeploymentSpec with PodSpec.
	// This means that someone could specify containers in template.spec and also in top level PodSpec.
	// This stupid check is supposed to make sure that only one of them set.
	// TODO: merge DeploymentSpec.Template.Spec and top level PodSpec
	if !(reflect.DeepEqual(app.DeploymentSpec.Template.Spec, api_v1.PodSpec{}) || reflect.DeepEqual(app.PodSpec, api_v1.PodSpec{})) {
		return nil, fmt.Errorf("Pod can't be specfied in two places. Use top level PodSpec or template.spec (DeploymentSpec.Template.Spec) not both")
	}

	deploymentSpec := app.DeploymentSpec

	// top level PodSpec is not empty, use it for deployment template
	// we already know that if app.PodSpec is not empty app.DeploymentSpec.Template.Spec is empty
	if !reflect.DeepEqual(app.PodSpec, api_v1.PodSpec{}) {
		deploymentSpec.Template.Spec = app.PodSpec
	}

	// TODO: check if this wasn't set by user, in that case we shouldn't ovewrite it
	deploymentSpec.Template.ObjectMeta.Name = app.Name

	// TODO: merge with already existing labels and avoid duplication
	deploymentSpec.Template.ObjectMeta.Labels = app.Labels

	deployment := ext_v1beta1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:   app.Name,
			Labels: app.Labels,
		},
		Spec: deploymentSpec,
	}

	return &deployment, nil
}

// create PVC reading the root level persistent volume field
func createPVC(v spec.VolumeClaim, labels map[string]string) (*api_v1.PersistentVolumeClaim, error) {
	// check for conditions where user has given both conflicting fields
	// or not given either fields
	if v.Size != "" && v.Resources.Requests != nil {
		return nil, fmt.Errorf("persistent volume %q, cannot provide size and resources at the same time", v.Name)
	}
	if v.Size == "" && v.Resources.Requests == nil {
		return nil, fmt.Errorf("persistent volume %q, please provide size or resources, none given", v.Name)
	}

	// if user has given size then create a "api_v1.ResourceRequirements"
	// because this can be fed to pvc directly
	if v.Size != "" {
		size, err := resource.ParseQuantity(v.Size)
		if err != nil {
			return nil, errors.Wrap(err, "could not read volume size")
		}
		// update the volume's resource so that it can be fed
		v.Resources = api_v1.ResourceRequirements{
			Requests: api_v1.ResourceList{
				api_v1.ResourceStorage: size,
			},
		}
	}
	// setting the default accessmode if none given by user
	if len(v.AccessModes) == 0 {
		v.AccessModes = []api_v1.PersistentVolumeAccessMode{api_v1.ReadWriteOnce}
	}
	pvc := &api_v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:   v.Name,
			Labels: labels,
		},
		// since we updated the pvc spec before so this can be directly fed
		// without having to do any addition extra
		Spec: api_v1.PersistentVolumeClaimSpec(v.PersistentVolumeClaimSpec),
	}
	return pvc, nil
}

// Since we are automatically creating pvc from
// root level persistent volume and entry in the container
// volume mount, we also need to update the pod's volume field
func populateVolumes(app *spec.App) error {
	for cn, c := range app.PodSpec.Containers {
		for vn, vm := range c.VolumeMounts {
			if isPVCDefined(app.VolumeClaims, vm.Name) && !isVolumeDefined(app.Volumes, vm.Name) {
				app.Volumes = append(app.Volumes, api_v1.Volume{
					Name: vm.Name,
					VolumeSource: api_v1.VolumeSource{
						PersistentVolumeClaim: &api_v1.PersistentVolumeClaimVolumeSource{
							ClaimName: vm.Name,
						},
					},
				})
			} else if !isVolumeDefined(app.Volumes, vm.Name) {
				// pvc is not defined so we need to check if the entry is made in the pod volumes
				// since a volumeMount entry without entry in pod level volumes might cause failure
				// while deployment since that would not be a complete configuration
				return fmt.Errorf("neither root level Persistent Volume"+
					" nor Volume in pod spec defined for %q, "+
					"in app.containers[%d].volumeMounts[%d]", vm.Name, cn, vn)
			}
		}
	}
	return nil
}

func createSecrets(app *spec.App) ([]runtime.Object, error) {
	var secrets []runtime.Object

	for _, s := range app.Secrets {
		secret := &api_v1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:   s.Name,
				Labels: app.Labels,
			},
			Data:       s.Data,
			StringData: s.StringData,
			Type:       s.Type,
		}
		secrets = append(secrets, secret)
	}
	return secrets, nil
}

// CreateK8sObjects, if given object spec.App, this function reads
// them and returns kubernetes objects as list of runtime.Object
// If the app is using field 'extraResources' then it will
// also return file names mentioned there as list of string
func CreateK8sObjects(app *spec.App) ([]runtime.Object, []string, error) {
	var objects []runtime.Object

	if app.Labels == nil {
		app.Labels = getLabels(app)
	}

	svcs, err := createServices(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Unable to create Kubernetes Service")
	}

	ings, err := createIngresses(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Unable to create Kubernetes Ingresses")
	}

	secs, err := createSecrets(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "Unable to create Kubernetes Secrets")
	}

	app.PodSpec.Containers, err = populateContainers(app.Containers, app.ConfigMaps, app.Secrets)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}
	log.Debugf("object after population: %#v\n", app)

	app.PodSpec.InitContainers, err = populateContainers(app.InitContainers, app.ConfigMaps, app.Secrets)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}
	log.Debugf("object after population: %#v\n", app)

	// create pvc for each root level persistent volume
	var pvcs []runtime.Object
	for _, v := range app.VolumeClaims {
		pvc, err := createPVC(v, app.Labels)
		if err != nil {
			return nil, nil, errors.Wrapf(err, "app %q", app.Name)
		}
		pvcs = append(pvcs, pvc)
	}
	if err := populateVolumes(app); err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}

	var configMap []runtime.Object
	for _, cd := range app.ConfigMaps {
		cm := &api_v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name:   cd.Name,
				Labels: app.Labels,
			},
			Data: cd.Data,
		}

		configMap = append(configMap, cm)
	}

	deployment, err := createDeployment(app)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "app %q", app.Name)
	}

	// please keep the order of the artifacts addition as it is

	// adding non-controller objects
	objects = append(objects, pvcs...)
	log.Debugf("app: %s, pvc: %s\n", app.Name, spew.Sprint(pvcs))

	objects = append(objects, svcs...)
	log.Debugf("app: %s, service: %s\n", app.Name, spew.Sprint(svcs))

	objects = append(objects, ings...)
	log.Debugf("app: %s, ingress: %s\n", app.Name, spew.Sprint(ings))

	objects = append(objects, secs...)
	log.Debugf("app: %s, secret: %s\n", app.Name, spew.Sprint(secs))

	objects = append(objects, configMap...)
	log.Debugf("app: %s, configMap: %s\n", app.Name, spew.Sprint(configMap))

	// add new non-controller objects after this

	// adding controller objects
	// deployment will be nil if no deployment is generated and no error occurs,
	// so we only need to append this when a legit deployment resource is returned
	if deployment != nil {
		objects = append(objects, deployment)
		log.Debugf("app: %s, deployment: %s\n", app.Name, spew.Sprint(deployment))
	}
	// add new controllers after this

	return objects, app.ExtraResources, nil
}

// Transform function if given spec.App data creates the versioned
// kubernetes objects and returns them in list of runtime.Object
// And if the field in spec.App called 'extraResources' is used
// then it returns the filenames mentioned there as list of string
func Transform(app *spec.App) ([]runtime.Object, []string, error) {

	runtimeObjects, extraResources, err := CreateK8sObjects(app)
	if err != nil {
		return nil, nil, errors.Wrap(err, "failed to create Kubernetes objects")
	}

	if len(runtimeObjects) == 0 {
		return nil, nil, errors.New("No runtime objects created, possibly because not enough input data was passed")
	}

	for _, runtimeObject := range runtimeObjects {

		gvk, isUnversioned, err := api.Scheme.ObjectKind(runtimeObject)
		if err != nil {
			return nil, nil, errors.Wrap(err, "ConvertToVersion failed")
		}
		if isUnversioned {
			return nil, nil, fmt.Errorf("ConvertToVersion failed: can't output unversioned type: %T", runtimeObject)
		}

		runtimeObject.GetObjectKind().SetGroupVersionKind(gvk)
	}

	return runtimeObjects, extraResources, nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/dmnlk/gomadare"
	"github.com/k0kubun/pp"
)

func main() {
	ck := os.Getenv("ck")
	cs := os.Getenv("cs")
	at := os.Getenv("at")
	as := os.Getenv("as")
	client := gomadare.NewClient(ck, cs, at, as)
	client.GetUserStream(nil, func (s Status, e Event) {
		if s != nil {
			pp.Println(s)
		}
		if e != nil {
			pp.Print(e)
		}
	})
}

<commit_msg>fix sample<commit_after>package main

import (
	"os"

	"github.com/dmnlk/gomadare"
	"github.com/k0kubun/pp"
	"fmt"
)

func main() {
	ck := os.Getenv("ck")
	cs := os.Getenv("cs")
	at := os.Getenv("at")
	as := os.Getenv("as")
	client := gomadare.NewClient(ck, cs, at, as)
	client.GetUserStream(nil, func(s gomadare.Status, e gomadare.Event) {
		if &s != nil {
			fmt.Println("return status")
			pp.Print(s)
		}
		if &e != nil {
			fmt.Println("return event")
			pp.Print(e)
		}
	})
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type ChannelMessageList struct {
	// unique identifier of the channel message list
	Id int64 `json:"id"`

	// Id of the channel
	ChannelId int64 `json:"channelId"     sql:"NOT NULL"`

	// Id of the message
	MessageId int64 `json:"messageId"     sql:"NOT NULL"`

	// Addition date of the message to the channel
	AddedAt time.Time `json:"addedAt"     sql:"NOT NULL"`
}

func (c *ChannelMessageList) BeforeCreate() {
	c.AddedAt = time.Now()
}

func (c *ChannelMessageList) BeforeUpdate() {
	c.AddedAt = time.Now()
}

func (c *ChannelMessageList) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *ChannelMessageList) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *ChannelMessageList) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *ChannelMessageList) GetId() int64 {
	return c.Id
}

func (c ChannelMessageList) TableName() string {
	return "api.channel_message_list"
}

func NewChannelMessageList() *ChannelMessageList {
	return &ChannelMessageList{}
}

func (c *ChannelMessageList) Fetch() error {
	return bongo.B.Fetch(c)
}

func (c *ChannelMessageList) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *ChannelMessageList) UnreadCount(cp *ChannelParticipant) (int, error) {
	if cp.ChannelId == 0 {
		return 0, errors.New("ChannelId is not set")
	}

	if cp.AccountId == 0 {
		return 0, errors.New("AccountId is not set")
	}

	if cp.LastSeenAt.IsZero() {
		return 0, errors.New("Last seen at date is not valid - it is zero")
	}

	return bongo.B.Count(c,
		"channel_id = ? and added_at > ?",
		cp.ChannelId,
		// todo change this format to get from a specific place
		cp.LastSeenAt.UTC().Format(time.RFC822Z),
	)
}

func (c *ChannelMessageList) Create() error {
	return bongo.B.Create(c)
}

func (c *ChannelMessageList) Delete() error {
	return bongo.B.Delete(c)
}

func (c *ChannelMessageList) List(q *Query) (*HistoryResponse, error) {
	messageList, err := c.getMessages(q)
	if err != nil {
		return nil, err
	}

	hr := NewHistoryResponse()
	hr.MessageList = messageList

	unreadCount := 0
	cp := NewChannelParticipant()
	cp.ChannelId = c.ChannelId
	cp.AccountId = q.AccountId
	err = cp.FetchParticipant()
	// we are forcing unread count to 0 if user is not a participant
	// of the channel
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	if err == nil {
		unreadCount, err = c.UnreadCount(cp)
		if err != nil {
			return nil, err
		}
	}

	hr.UnreadCount = unreadCount
	return hr, nil
}

func (c *ChannelMessageList) getMessages(q *Query) ([]*ChannelMessageContainer, error) {
	var messages []int64

	if c.ChannelId == 0 {
		return nil, errors.New("ChannelId is not set")
	}

	if err := bongo.B.DB.Table(c.TableName()).
		Order("added_at desc").
		Where("channel_id = ?", c.ChannelId).
		Offset(q.Skip).
		Limit(q.Limit).
		Pluck("message_id", &messages).
		Error; err != nil {
		return nil, err
	}

	parent := NewChannelMessage()
	channelMessages, err := parent.FetchByIds(messages)
	if err != nil {
		return nil, err
	}

	populatedChannelMessages, err := c.populateChannelMessages(channelMessages)
	if err != nil {
		return nil, err
	}

	return populatedChannelMessages, nil
}

func (c *ChannelMessageList) populateChannelMessages(channelMessages []ChannelMessage) ([]*ChannelMessageContainer, error) {
	channelMessageCount := len(channelMessages)

	populatedChannelMessages := make([]*ChannelMessageContainer, channelMessageCount)

	if channelMessageCount == 0 {
		return populatedChannelMessages, nil
	}

	for i := 0; i < channelMessageCount; i++ {
		cm := channelMessages[i]
		cmc, err := cm.FetchRelatives()
		if err != nil {
			return nil, err
		}

		populatedChannelMessages[i] = cmc

		mr := NewMessageReply()
		mr.MessageId = cm.Id
		replies, err := mr.List()
		if err != nil {
			return nil, err
		}

		populatedChannelMessagesReplies := make([]*ChannelMessageContainer, len(replies))

		for rl := 0; rl < len(replies); rl++ {
			cmrc, err := replies[rl].FetchRelatives()
			if err != nil {
				return nil, err
			}
			populatedChannelMessagesReplies[rl] = cmrc
		}
		populatedChannelMessages[i].Replies = populatedChannelMessagesReplies

	}
	return populatedChannelMessages, nil

}

func (c *ChannelMessageList) FetchMessageChannels(messageId int64) ([]Channel, error) {
	var channelIds []int64

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": messageId,
		},
		Pluck: "channel_id",
	}

	err := bongo.B.Some(c, &channelIds, q)
	if err != nil {
		return nil, err
	}

	return NewChannel().FetchByIds(channelIds)
}

// seperate this fucntion into modelhelper
// as setting it to a variadic function
func (c *ChannelMessageList) DeleteMessagesBySelector(selector map[string]interface{}) error {
	var cmls []ChannelMessageList

	err := bongo.B.Some(c, &cmls, &bongo.Query{Selector: selector})
	if err != nil {
		return err
	}

	for _, cml := range cmls {
		if err := cml.Delete(); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Social: use buildMessage function instead of handling all required operations<commit_after>package models

import (
	"errors"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
)

type ChannelMessageList struct {
	// unique identifier of the channel message list
	Id int64 `json:"id"`

	// Id of the channel
	ChannelId int64 `json:"channelId"     sql:"NOT NULL"`

	// Id of the message
	MessageId int64 `json:"messageId"     sql:"NOT NULL"`

	// Addition date of the message to the channel
	AddedAt time.Time `json:"addedAt"     sql:"NOT NULL"`
}

func (c *ChannelMessageList) BeforeCreate() {
	c.AddedAt = time.Now()
}

func (c *ChannelMessageList) BeforeUpdate() {
	c.AddedAt = time.Now()
}

func (c *ChannelMessageList) AfterCreate() {
	bongo.B.AfterCreate(c)
}

func (c *ChannelMessageList) AfterUpdate() {
	bongo.B.AfterUpdate(c)
}

func (c *ChannelMessageList) AfterDelete() {
	bongo.B.AfterDelete(c)
}

func (c *ChannelMessageList) GetId() int64 {
	return c.Id
}

func (c ChannelMessageList) TableName() string {
	return "api.channel_message_list"
}

func NewChannelMessageList() *ChannelMessageList {
	return &ChannelMessageList{}
}

func (c *ChannelMessageList) Fetch() error {
	return bongo.B.Fetch(c)
}

func (c *ChannelMessageList) One(q *bongo.Query) error {
	return bongo.B.One(c, c, q)
}

func (c *ChannelMessageList) UnreadCount(cp *ChannelParticipant) (int, error) {
	if cp.ChannelId == 0 {
		return 0, errors.New("ChannelId is not set")
	}

	if cp.AccountId == 0 {
		return 0, errors.New("AccountId is not set")
	}

	if cp.LastSeenAt.IsZero() {
		return 0, errors.New("Last seen at date is not valid - it is zero")
	}

	return bongo.B.Count(c,
		"channel_id = ? and added_at > ?",
		cp.ChannelId,
		// todo change this format to get from a specific place
		cp.LastSeenAt.UTC().Format(time.RFC822Z),
	)
}

func (c *ChannelMessageList) Create() error {
	return bongo.B.Create(c)
}

func (c *ChannelMessageList) Delete() error {
	return bongo.B.Delete(c)
}

func (c *ChannelMessageList) List(q *Query) (*HistoryResponse, error) {
	messageList, err := c.getMessages(q)
	if err != nil {
		return nil, err
	}

	hr := NewHistoryResponse()
	hr.MessageList = messageList

	unreadCount := 0
	cp := NewChannelParticipant()
	cp.ChannelId = c.ChannelId
	cp.AccountId = q.AccountId
	err = cp.FetchParticipant()
	// we are forcing unread count to 0 if user is not a participant
	// of the channel
	if err != nil && err != gorm.RecordNotFound {
		return nil, err
	}

	if err == nil {
		unreadCount, err = c.UnreadCount(cp)
		if err != nil {
			return nil, err
		}
	}

	hr.UnreadCount = unreadCount
	return hr, nil
}

func (c *ChannelMessageList) getMessages(q *Query) ([]*ChannelMessageContainer, error) {
	var messages []int64

	if c.ChannelId == 0 {
		return nil, errors.New("ChannelId is not set")
	}

	if err := bongo.B.DB.Table(c.TableName()).
		Order("added_at desc").
		Where("channel_id = ?", c.ChannelId).
		Offset(q.Skip).
		Limit(q.Limit).
		Pluck("message_id", &messages).
		Error; err != nil {
		return nil, err
	}

	parent := NewChannelMessage()
	channelMessages, err := parent.FetchByIds(messages)
	if err != nil {
		return nil, err
	}

	populatedChannelMessages, err := c.populateChannelMessages(channelMessages)
	if err != nil {
		return nil, err
	}

	return populatedChannelMessages, nil
}

func (c *ChannelMessageList) populateChannelMessages(channelMessages []ChannelMessage) ([]*ChannelMessageContainer, error) {
	channelMessageCount := len(channelMessages)

	populatedChannelMessages := make([]*ChannelMessageContainer, channelMessageCount)

	if channelMessageCount == 0 {
		return populatedChannelMessages, nil
	}

	for i := 0; i < channelMessageCount; i++ {
		cm := channelMessages[i]
		cmc, err := cm.BuildMessage()
		if err != nil {
			return nil, err
		}

		populatedChannelMessages[i] = cmc
	}
	return populatedChannelMessages, nil

}

func (c *ChannelMessageList) FetchMessageChannels(messageId int64) ([]Channel, error) {
	var channelIds []int64

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": messageId,
		},
		Pluck: "channel_id",
	}

	err := bongo.B.Some(c, &channelIds, q)
	if err != nil {
		return nil, err
	}

	return NewChannel().FetchByIds(channelIds)
}

// seperate this fucntion into modelhelper
// as setting it to a variadic function
func (c *ChannelMessageList) DeleteMessagesBySelector(selector map[string]interface{}) error {
	var cmls []ChannelMessageList

	err := bongo.B.Some(c, &cmls, &bongo.Query{Selector: selector})
	if err != nil {
		return err
	}

	for _, cml := range cmls {
		if err := cml.Delete(); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package fs

import (
	"bufio"
	"bytes"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"subutai/config"
	"subutai/log"
)

func IsSubvolumeReadonly(path string) bool {
	out, _ := exec.Command("btrfs", "property", "get", "-ts", path).Output()
	if strings.Contains(string(out), "true") {
		return true
	}
	return false
}

func SubvolumeClone(src, dst string) {
	err := exec.Command("btrfs", "subvolume", "snapshot", config.Agent.LxcPrefix+src, config.Agent.LxcPrefix+dst).Run()
	log.Check(log.FatalLevel, "Creating snapshot", err)
}

func SubvolumeDestroy(path string) {
	nestedvol, err := exec.Command("btrfs", "subvolume", "list", "-o", config.Agent.LxcPrefix+path).Output()
	log.Check(log.WarnLevel, "Getting nested subvolumes in "+config.Agent.LxcPrefix+path, err)
	scanner := bufio.NewScanner(bytes.NewReader(nestedvol))
	for scanner.Scan() {
		line := strings.Fields(scanner.Text())
		if len(line) > 8 {
			subvol := strings.Split(line[8], path)
			if len(subvol) > 1 {
				SubvolumeDestroy(path + subvol[1])
			}
		}
	}
	qgroupDestroy(path)
	err = exec.Command("btrfs", "subvolume", "delete", config.Agent.LxcPrefix+path).Run()
	log.Check(log.FatalLevel, "Destroying subvolume "+path, err)
}

func qgroupDestroy(path string) {
	index := id(path)
	err := exec.Command("btrfs", "qgroup", "destroy", index, config.Agent.LxcPrefix).Run()
	log.Check(log.FatalLevel, "Destroying qgroup "+path+" "+index, err)
}

func id(path string) string {
	out, _ := exec.Command("btrfs", "subvolume", "list", config.Agent.LxcPrefix).Output()
	scanner := bufio.NewScanner(bytes.NewReader(out))
	for scanner.Scan() {
		line := strings.Fields(scanner.Text())
		if len(line) > 8 {
			if strings.HasSuffix(line[8], path) {
				return line[1]
			}
		}
	}
	return ""
}

func Receive(src, dst, delta string, parent bool) {
	args := []string{"receive", "-p", config.Agent.LxcPrefix + src, config.Agent.LxcPrefix + dst}
	if !parent {
		args = []string{"receive", config.Agent.LxcPrefix + dst}
	}
	receive := exec.Command("btrfs", args...)
	input, err := os.Open(config.Agent.LxcPrefix + "lxc-data/tmpdir/" + delta)
	receive.Stdin = input
	log.Check(log.FatalLevel, "Opening delta "+delta, err)
	log.Check(log.FatalLevel, "Receiving delta "+delta, receive.Run())
}

func Send(src, dst, delta string) {
	newdelta, err := os.Create(delta)
	log.Check(log.FatalLevel, "Creating delta "+delta, err)
	args := []string{"send", "-p", src, dst}
	if src == dst {
		args = []string{"send", dst}
	}
	send := exec.Command("btrfs", args...)
	send.Stdout = newdelta
	log.Check(log.FatalLevel, "Sending delta "+delta, send.Run())
}

func ReadOnly(container string, flag bool) {
	for _, path := range []string{container + "/rootfs/", "lxc/" + container + "-opt", "lxc-data/" + container + "-var", "lxc-data/" + container + "-home"} {
		arg := []string{"property", "set", "-ts", config.Agent.LxcPrefix + path, "ro", strconv.FormatBool(flag)}
		log.Check(log.FatalLevel, "Setting readonly: "+strconv.FormatBool(flag), exec.Command("btrfs", arg...).Run())
	}
}

func Stat(path, index string, raw bool) string {
	var row = map[string]int{
		"quota": 3,
		"usage": 2,
	}

	args := []string{"qgroup", "show", "-r", config.Agent.LxcPrefix}
	if raw {
		args = []string{"qgroup", "show", "-r", "--raw", config.Agent.LxcPrefix}
	}
	out, err := exec.Command("btrfs", args...).Output()
	log.Check(log.FatalLevel, "Getting btrfs stats", err)
	ind := id(path)
	scanner := bufio.NewScanner(bytes.NewReader(out))
	for scanner.Scan() {
		line := strings.Fields(scanner.Text())
		if len(line) > 3 {
			if line[0] == "0/"+ind {
				return line[row[index]]
			}
		}
	}
	return ""
}

func Quota(path string, size ...string) string {
	if size != nil {
		exec.Command("btrfs", "qgroup", "limit", size[0]+"G", config.Agent.LxcPrefix+path).Run()
	}
	return Stat(path, "quota", false)
}

func GetContainerUUID(contanierName string) string {
	var uuid string
	result, err := exec.Command("btrfs", "subvolume", "list", "-u", config.Agent.LxcPrefix).CombinedOutput()
	if err != nil {
		log.Error("btrfs command execute", err.Error())
	}
	resArr := strings.Split(string(result), "\n")
	for _, r := range resArr {
		if strings.Contains(r, contanierName+"/rootfs") {
			rArr := strings.Fields(r)
			uuid = rArr[8]
		}

	}
	return uuid
}

func GetChildren(uuid string) []string {
	var child []string
	result, err := exec.Command("btrfs", "subvolume", "list", "-q", config.Agent.LxcPrefix).CombinedOutput()
	if err != nil {
		log.Error("btrfs -q command execute", err.Error())
	}
	resultArr := strings.Split(string(result), "\n")
	for _, v := range resultArr {
		if strings.Contains(v, uuid) {
			vArr := strings.Fields(v)
			child = append(child, vArr[10])
		}
	}
	return child
}
<commit_msg>lxc destroy small fix. SS-4083<commit_after>package fs

import (
	"bufio"
	"bytes"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"subutai/config"
	"subutai/log"
)

func IsSubvolumeReadonly(path string) bool {
	out, _ := exec.Command("btrfs", "property", "get", "-ts", path).Output()
	if strings.Contains(string(out), "true") {
		return true
	}
	return false
}

func SubvolumeClone(src, dst string) {
	err := exec.Command("btrfs", "subvolume", "snapshot", config.Agent.LxcPrefix+src, config.Agent.LxcPrefix+dst).Run()
	log.Check(log.FatalLevel, "Creating snapshot", err)
}

func SubvolumeDestroy(path string) {
	nestedvol, err := exec.Command("btrfs", "subvolume", "list", "-o", config.Agent.LxcPrefix+path).Output()
	log.Check(log.WarnLevel, "Getting nested subvolumes in "+config.Agent.LxcPrefix+path, err)
	scanner := bufio.NewScanner(bytes.NewReader(nestedvol))
	for scanner.Scan() {
		line := strings.Fields(scanner.Text())
		if len(line) > 8 {
			subvol := strings.Split(line[8], path)
			if len(subvol) > 1 {
				SubvolumeDestroy(path + subvol[1])
			}
		}
	}
	qgroupDestroy(path)
	err = exec.Command("btrfs", "subvolume", "delete", config.Agent.LxcPrefix+path).Run()
	log.Check(log.WarnLevel, "Destroying subvolume "+path, err)
}

func qgroupDestroy(path string) {
	index := id(path)
	err := exec.Command("btrfs", "qgroup", "destroy", index, config.Agent.LxcPrefix).Run()
	log.Check(log.WarnLevel, "Destroying qgroup "+path+" "+index, err)
}

func id(path string) string {
	out, _ := exec.Command("btrfs", "subvolume", "list", config.Agent.LxcPrefix).Output()
	scanner := bufio.NewScanner(bytes.NewReader(out))
	for scanner.Scan() {
		line := strings.Fields(scanner.Text())
		if len(line) > 8 {
			if strings.HasSuffix(line[8], path) {
				return line[1]
			}
		}
	}
	return ""
}

func Receive(src, dst, delta string, parent bool) {
	args := []string{"receive", "-p", config.Agent.LxcPrefix + src, config.Agent.LxcPrefix + dst}
	if !parent {
		args = []string{"receive", config.Agent.LxcPrefix + dst}
	}
	receive := exec.Command("btrfs", args...)
	input, err := os.Open(config.Agent.LxcPrefix + "lxc-data/tmpdir/" + delta)
	receive.Stdin = input
	log.Check(log.FatalLevel, "Opening delta "+delta, err)
	log.Check(log.FatalLevel, "Receiving delta "+delta, receive.Run())
}

func Send(src, dst, delta string) {
	newdelta, err := os.Create(delta)
	log.Check(log.FatalLevel, "Creating delta "+delta, err)
	args := []string{"send", "-p", src, dst}
	if src == dst {
		args = []string{"send", dst}
	}
	send := exec.Command("btrfs", args...)
	send.Stdout = newdelta
	log.Check(log.FatalLevel, "Sending delta "+delta, send.Run())
}

func ReadOnly(container string, flag bool) {
	for _, path := range []string{container + "/rootfs/", "lxc/" + container + "-opt", "lxc-data/" + container + "-var", "lxc-data/" + container + "-home"} {
		arg := []string{"property", "set", "-ts", config.Agent.LxcPrefix + path, "ro", strconv.FormatBool(flag)}
		log.Check(log.FatalLevel, "Setting readonly: "+strconv.FormatBool(flag), exec.Command("btrfs", arg...).Run())
	}
}

func Stat(path, index string, raw bool) string {
	var row = map[string]int{
		"quota": 3,
		"usage": 2,
	}

	args := []string{"qgroup", "show", "-r", config.Agent.LxcPrefix}
	if raw {
		args = []string{"qgroup", "show", "-r", "--raw", config.Agent.LxcPrefix}
	}
	out, err := exec.Command("btrfs", args...).Output()
	log.Check(log.FatalLevel, "Getting btrfs stats", err)
	ind := id(path)
	scanner := bufio.NewScanner(bytes.NewReader(out))
	for scanner.Scan() {
		line := strings.Fields(scanner.Text())
		if len(line) > 3 {
			if line[0] == "0/"+ind {
				return line[row[index]]
			}
		}
	}
	return ""
}

func Quota(path string, size ...string) string {
	if size != nil {
		exec.Command("btrfs", "qgroup", "limit", size[0]+"G", config.Agent.LxcPrefix+path).Run()
	}
	return Stat(path, "quota", false)
}

func GetContainerUUID(contanierName string) string {
	var uuid string
	result, err := exec.Command("btrfs", "subvolume", "list", "-u", config.Agent.LxcPrefix).CombinedOutput()
	if err != nil {
		log.Error("btrfs command execute", err.Error())
	}
	resArr := strings.Split(string(result), "\n")
	for _, r := range resArr {
		if strings.Contains(r, contanierName+"/rootfs") {
			rArr := strings.Fields(r)
			uuid = rArr[8]
		}

	}
	return uuid
}

func GetChildren(uuid string) []string {
	var child []string
	result, err := exec.Command("btrfs", "subvolume", "list", "-q", config.Agent.LxcPrefix).CombinedOutput()
	if err != nil {
		log.Error("btrfs -q command execute", err.Error())
	}
	resultArr := strings.Split(string(result), "\n")
	for _, v := range resultArr {
		if strings.Contains(v, uuid) {
			vArr := strings.Fields(v)
			child = append(child, vArr[10])
		}
	}
	return child
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sort_test

import (
	"fmt"
	"sort"
)

// A Change is a record of source code changes, recording user, language, and delta size.
type Change struct {
	user     string
	language string
	lines    int
}

type lessFunc func(p1, p2 *Change) bool

// multiSorter implements the Sort interface, sorting the changes within.
type multiSorter struct {
	changes []Change
	less    []lessFunc
}

// Sort sorts the argument slice according to the less functions passed to OrderedBy.
func (ms *multiSorter) Sort(changes []Change) {
	sort.Sort(ms)
}

// OrderedBy returns a Sorter that sorts using the less functions, in order.
// Call its Sort method to sort the data.
func OrderedBy(less ...lessFunc) *multiSorter {
	return &multiSorter{
		changes: changes,
		less:    less,
	}
}

// Len is part of sort.Interface.
func (ms *multiSorter) Len() int {
	return len(ms.changes)
}

// Swap is part of sort.Interface.
func (ms *multiSorter) Swap(i, j int) {
	ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i]
}

// Less is part of sort.Interface. It is implemented by looping along the
// less functions until it finds a comparison that is either Less or
// !Less. Note that it can call the less functions twice per call. We
// could change the functions to return -1, 0, 1 and reduce the
// number of calls for greater efficiency: an exercise for the reader.
func (ms *multiSorter) Less(i, j int) bool {
	p, q := &ms.changes[i], &ms.changes[j]
	// Try all but the last comparison.
	var k int
	for k = 0; k < len(ms.less)-1; k++ {
		less := ms.less[k]
		switch {
		case less(p, q):
			// p < q, so we have a decision.
			return true
		case less(q, p):
			// p > q, so we have a decision.
			return false
		}
		// p == q; try the next comparison.
	}
	// All comparisons to here said "equal", so just return whatever
	// the final comparison reports.
	return ms.less[k](p, q)
}

var changes = []Change{
	{"gri", "Go", 100},
	{"ken", "C", 150},
	{"glenda", "Go", 200},
	{"rsc", "Go", 200},
	{"r", "Go", 100},
	{"ken", "Go", 200},
	{"dmr", "C", 100},
	{"r", "C", 150},
	{"gri", "Smalltalk", 80},
}

// ExampleMultiKeys demonstrates a technique for sorting a struct type using different
// sets of multiple fields in the comparison. We chain together "Less" functions, each of
// which compares a single field.
func Example_sortMultiKeys() {
	// Closures that order the Change structure.
	user := func(c1, c2 *Change) bool {
		return c1.user < c2.user
	}
	language := func(c1, c2 *Change) bool {
		return c1.language < c2.language
	}
	increasingLines := func(c1, c2 *Change) bool {
		return c1.lines < c2.lines
	}
	decreasingLines := func(c1, c2 *Change) bool {
		return c1.lines > c2.lines // Note: > orders downwards.
	}

	// Simple use: Sort by user.
	OrderedBy(user).Sort(changes)
	fmt.Println("By user:", changes)

	// multiSorter implements the Sort interface, so we can also do this.
	sort.Sort(OrderedBy(user, increasingLines))
	fmt.Println("By user,<lines:", changes)

	// More examples.
	OrderedBy(user, decreasingLines).Sort(changes)
	fmt.Println("By user,>lines:", changes)

	OrderedBy(language, increasingLines).Sort(changes)
	fmt.Println("By language,<lines:", changes)

	OrderedBy(language, increasingLines, user).Sort(changes)
	fmt.Println("By language,<lines,user:", changes)

	// Output:
	//By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}]
	//By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
	//By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
	//By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
	//By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]

}
<commit_msg>sort: fix Example_sortMultiKeys<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sort_test

import (
	"fmt"
	"sort"
)

// A Change is a record of source code changes, recording user, language, and delta size.
type Change struct {
	user     string
	language string
	lines    int
}

type lessFunc func(p1, p2 *Change) bool

// multiSorter implements the Sort interface, sorting the changes within.
type multiSorter struct {
	changes []Change
	less    []lessFunc
}

// Sort sorts the argument slice according to the less functions passed to OrderedBy.
func (ms *multiSorter) Sort(changes []Change) {
	ms.changes = changes
	sort.Sort(ms)
}

// OrderedBy returns a Sorter that sorts using the less functions, in order.
// Call its Sort method to sort the data.
func OrderedBy(less ...lessFunc) *multiSorter {
	return &multiSorter{
		less: less,
	}
}

// Len is part of sort.Interface.
func (ms *multiSorter) Len() int {
	return len(ms.changes)
}

// Swap is part of sort.Interface.
func (ms *multiSorter) Swap(i, j int) {
	ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i]
}

// Less is part of sort.Interface. It is implemented by looping along the
// less functions until it finds a comparison that is either Less or
// !Less. Note that it can call the less functions twice per call. We
// could change the functions to return -1, 0, 1 and reduce the
// number of calls for greater efficiency: an exercise for the reader.
func (ms *multiSorter) Less(i, j int) bool {
	p, q := &ms.changes[i], &ms.changes[j]
	// Try all but the last comparison.
	var k int
	for k = 0; k < len(ms.less)-1; k++ {
		less := ms.less[k]
		switch {
		case less(p, q):
			// p < q, so we have a decision.
			return true
		case less(q, p):
			// p > q, so we have a decision.
			return false
		}
		// p == q; try the next comparison.
	}
	// All comparisons to here said "equal", so just return whatever
	// the final comparison reports.
	return ms.less[k](p, q)
}

var changes = []Change{
	{"gri", "Go", 100},
	{"ken", "C", 150},
	{"glenda", "Go", 200},
	{"rsc", "Go", 200},
	{"r", "Go", 100},
	{"ken", "Go", 200},
	{"dmr", "C", 100},
	{"r", "C", 150},
	{"gri", "Smalltalk", 80},
}

// ExampleMultiKeys demonstrates a technique for sorting a struct type using different
// sets of multiple fields in the comparison. We chain together "Less" functions, each of
// which compares a single field.
func Example_sortMultiKeys() {
	// Closures that order the Change structure.
	user := func(c1, c2 *Change) bool {
		return c1.user < c2.user
	}
	language := func(c1, c2 *Change) bool {
		return c1.language < c2.language
	}
	increasingLines := func(c1, c2 *Change) bool {
		return c1.lines < c2.lines
	}
	decreasingLines := func(c1, c2 *Change) bool {
		return c1.lines > c2.lines // Note: > orders downwards.
	}

	// Simple use: Sort by user.
	OrderedBy(user).Sort(changes)
	fmt.Println("By user:", changes)

	// More examples.
	OrderedBy(user, increasingLines).Sort(changes)
	fmt.Println("By user,<lines:", changes)

	OrderedBy(user, decreasingLines).Sort(changes)
	fmt.Println("By user,>lines:", changes)

	OrderedBy(language, increasingLines).Sort(changes)
	fmt.Println("By language,<lines:", changes)

	OrderedBy(language, increasingLines, user).Sort(changes)
	fmt.Println("By language,<lines,user:", changes)

	// Output:
	//By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}]
	//By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
	//By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
	//By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
	//By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]

}
<|endoftext|>


<commit_before>package alerts

import (
	"encoding/json"
	"fmt"
	"github.com/libopenstorage/openstorage/api"
	"github.com/portworx/kvdb"
	"go.pedge.io/proto/time"
	"strconv"
	"strings"
	"time"
)

type watcherStatus int
type watcher struct {
	kvcb   kvdb.WatchCB
	status watcherStatus
	cb     AlertsWatcherFunc
	clusterId string
	kvdb kvdb.Kvdb
}
type KvAlerts struct {
	kvdbName string
	kvdbDomain string
	kvdbMachines []string
	clusterId string
}

const (
	alertsKey       = "alerts/"
	nextAlertsIdKey = "nextAlertsId"
	clusterKey      = "cluster/"
	volumeKey       = "volume/"
	nodeKey         = "node/"
	bootstrap       = "bootstrap"
	// Name of this alerts client implementation
	Name            = "alerts_kvdb"
	// NameTest :  This alert instance used only for unit tests
	NameTest        = "alerts_kvdb_test"
)

const (
	watchBootstrap = watcherStatus(iota)
	watchReady
	watchError
)

var (
	kvdbMap          map[string]kvdb.Kvdb
	watcherMap       map[string]*watcher
	alertsWatchIndex uint64
	watchErrors      int
)

// GetKvdbInstance - Returns a kvdb instance associated with this alert client and clusterId combination
func (kva *KvAlerts) GetKvdbInstance() kvdb.Kvdb {
	return kvdbMap[kva.clusterId]
}

// Init initializes a AlertsClient interface implementation
func Init(name string, domain string, machines []string, clusterId string) (AlertsClient, error) {
	_, ok := kvdbMap[clusterId]
	if !ok {
		kv, err := kvdb.New(name, domain + "/" + clusterId, machines, nil)
		if err != nil {
			return nil, err
		}
		kvdbMap[clusterId] = kv
	}

	return &KvAlerts{kvdbName: name, kvdbDomain: domain, kvdbMachines: machines, clusterId: clusterId}, nil
}

// Raise raises an Alert
func (kva *KvAlerts) Raise(a api.Alerts) (api.Alerts, error) {
	kv := kva.GetKvdbInstance()
	if a.Resource == api.ResourceType_UNKNOWN_RESOURCE {
		return api.Alerts{}, ErrResourceNotFound
	}
	alertId, err := kva.getNextIdFromKVDB()
	if err != nil {
		return a, err
	}
	a.Id = alertId
	a.Timestamp = prototime.Now()
	a.Cleared = false
	_, err = kv.Create(getResourceKey(a.Resource)+strconv.FormatInt(a.Id, 10), &a, 0)
	return a, err
}

// Erase erases an alert
func (kva *KvAlerts) Erase(resourceType api.ResourceType, alertId int64) error {
	kv := kva.GetKvdbInstance()

	if resourceType == api.ResourceType_UNKNOWN_RESOURCE {
		return ErrResourceNotFound
	}
	var err error

	_, err = kv.Delete(getResourceKey(resourceType) + strconv.FormatInt(alertId, 10))
	return err
}

// Clear clears an alert
func (kva *KvAlerts) Clear(resourceType api.ResourceType, alertId int64) error {
	kv := kva.GetKvdbInstance()
	var (
		err   error
		alert api.Alerts
	)
	if resourceType == api.ResourceType_UNKNOWN_RESOURCE {
		return ErrResourceNotFound
	}

	_, err = kv.GetVal(getResourceKey(resourceType)+strconv.FormatInt(alertId, 10), &alert)
	if err != nil {
		return err
	}
	alert.Cleared = true

	_, err = kv.Update(getResourceKey(resourceType)+strconv.FormatInt(alertId, 10), &alert, 0)
	return err
}

// Retrieve retrieves a specific alert
func (kva *KvAlerts) Retrieve(resourceType api.ResourceType, alertId int64) (api.Alerts, error) {
	var (
		alert api.Alerts
		err   error
	)
	if resourceType == api.ResourceType_UNKNOWN_RESOURCE {
		return api.Alerts{}, ErrResourceNotFound
	}
	kv := kva.GetKvdbInstance()

	_, err = kv.GetVal(getResourceKey(resourceType)+strconv.FormatInt(alertId, 10), &alert)

	return alert, err
}

// Enumerate enumerates alerts
func (kva *KvAlerts) Enumerate(filter api.Alerts) ([]*api.Alerts, error) {
	allAlerts := []*api.Alerts{}
	resourceAlerts := []*api.Alerts{}
	var err error

	if filter.Resource != api.ResourceType_UNKNOWN_RESOURCE {
		resourceAlerts, err = kva.getResourceSpecificAlerts(filter.Resource)
		if err != nil {
			return nil, err
		}
	} else {
		resourceAlerts, err = kva.getAllAlerts()
	}

	if filter.Severity != 0 {
		for _, v := range resourceAlerts {
			if v.Severity <= filter.Severity {
				allAlerts = append(allAlerts, v)
			}
		}
	} else {
		allAlerts = append(allAlerts, resourceAlerts...)
	}

	return allAlerts, err
}

// EnumerateWithinTimeRange enumerates alerts between timeStart and timeEnd
func (kva *KvAlerts) EnumerateWithinTimeRange(
	timeStart time.Time,
	timeEnd time.Time,
	resourceType api.ResourceType,
) ([]*api.Alerts, error) {
	allAlerts := []*api.Alerts{}
	resourceAlerts := []*api.Alerts{}
	var err error

	if resourceType != 0 {
		resourceAlerts, err = kva.getResourceSpecificAlerts(resourceType)
		if err != nil {
			return nil, err
		}
	} else {
		resourceAlerts, err = kva.getAllAlerts()
		if err != nil {
			return nil, err
		}
	}
	for _, v := range resourceAlerts {
		alertTime := prototime.TimestampToTime(v.Timestamp)
		if alertTime.Before(timeEnd) && alertTime.After(timeStart) {
			allAlerts = append(allAlerts, v)
		}
	}
	return allAlerts, nil
}

// Watch on all alerts
func (kva *KvAlerts) Watch(clusterId string, alertsWatcherFunc AlertsWatcherFunc) error {
	_, ok := kvdbMap[clusterId]
	if !ok {
		kv, err := kvdb.New(kva.kvdbName, kva.kvdbDomain + "/" + clusterId, kva.kvdbMachines, nil)
		if err != nil {
			return err
		}
		kvdbMap[clusterId] = kv
	}

	kv := kvdbMap[clusterId]
	alertsWatcher := &watcher{status: watchBootstrap, cb: alertsWatcherFunc, kvcb: kvdbWatch, kvdb: kv}
	watcherKey := clusterId
	watcherMap[watcherKey] = alertsWatcher

	if err := subscribeWatch(watcherKey); err != nil {
		return err
	}

	// Subscribe for a watch can be in a goroutine. Bootstrap by writing to the key and waiting for an update
	retries := 0

	for alertsWatcher.status == watchBootstrap {
		_, err := kv.Put(alertsKey+bootstrap, time.Now(), 1)
		if err != nil {
			return err
		}
		if alertsWatcher.status == watchBootstrap {
			retries++
			time.Sleep(time.Millisecond * 100)
		}
		if retries == 5 {
			return fmt.Errorf("Failed to bootstrap watch on %s", clusterId)
		}
	}
	if alertsWatcher.status != watchReady {
		return fmt.Errorf("Failed to watch on %s", clusterId)
	}

	return nil
}

// Shutdown
func (kva *KvAlerts) Shutdown() {
}

// String
func (kva *KvAlerts) String() string {
	return Name
}

func getResourceKey(resourceType api.ResourceType) string {
	if resourceType == api.ResourceType_VOLUMES {
		return alertsKey + volumeKey
	}
	if resourceType == api.ResourceType_NODE {
		return alertsKey + nodeKey
	}
	return alertsKey + clusterKey
}

func getNextAlertsIdKey() string {
	return alertsKey + nextAlertsIdKey
}

func (kva *KvAlerts) getNextIdFromKVDB() (int64, error) {
	kv := kva.GetKvdbInstance()
	nextAlertsId := 0
	kvp, err := kv.Create(getNextAlertsIdKey(), strconv.FormatInt(int64(nextAlertsId+1), 10), 0)

	for err != nil {
		kvp, err = kv.GetVal(getNextAlertsIdKey(), &nextAlertsId)
		if err != nil {
			err = ErrNotInitialized
			return -1, err
		}
		prevValue := kvp.Value
		newKvp := *kvp
		newKvp.Value = []byte(strconv.FormatInt(int64(nextAlertsId+1), 10))
		kvp, err = kv.CompareAndSet(&newKvp, kvdb.KVFlags(0), prevValue)
	}
	return int64(nextAlertsId), err
}

func (kva *KvAlerts) getResourceSpecificAlerts(resourceType api.ResourceType) ([]*api.Alerts, error) {
	kv := kva.GetKvdbInstance()
	allAlerts := []*api.Alerts{}
	kvp, err := kv.Enumerate(getResourceKey(resourceType))
	if err != nil {
		return nil, err
	}

	for _, v := range kvp {
		var elem *api.Alerts
		err = json.Unmarshal(v.Value, &elem)
		if err != nil {
			return nil, err
		}
		allAlerts = append(allAlerts, elem)
	}
	return allAlerts, nil
}

func (kva *KvAlerts) getAllAlerts() ([]*api.Alerts, error) {
	allAlerts := []*api.Alerts{}
	clusterAlerts := []*api.Alerts{}
	nodeAlerts := []*api.Alerts{}
	volumeAlerts := []*api.Alerts{}
	var err error

	nodeAlerts, err = kva.getResourceSpecificAlerts(api.ResourceType_NODE)
	if err == nil {
		allAlerts = append(allAlerts, nodeAlerts...)
	}
	volumeAlerts, err = kva.getResourceSpecificAlerts(api.ResourceType_VOLUMES)
	if err == nil {
		allAlerts = append(allAlerts, volumeAlerts...)
	}
	clusterAlerts, err = kva.getResourceSpecificAlerts(api.ResourceType_CLUSTER)
	if err == nil {
		allAlerts = append(allAlerts, clusterAlerts...)
	}

	if len(allAlerts) > 0 {
		return allAlerts, nil
	} else if len(allAlerts) == 0 {
		return nil, fmt.Errorf("No alerts raised yet")
	}
	return allAlerts, err
}

func kvdbWatch(prefix string, opaque interface{}, kvp *kvdb.KVPair, err error) error {
	mutex.Lock()
	defer mutex.Unlock()

	watcherKey := strings.Split(prefix, "/")[1]

	if err == nil && strings.HasSuffix(kvp.Key, bootstrap) {
		w := watcherMap[watcherKey]
		w.status = watchReady
		return nil
	}

	if err != nil {
		if w:= watcherMap[watcherKey]; w.status == watchBootstrap {
			w.status = watchError
			return err
		}
		if watchErrors == 5 {
			return fmt.Errorf("Too many watch errors (%v)", watchErrors)
		}
		watchErrors++
		if err := subscribeWatch(watcherKey); err != nil {
			return fmt.Errorf("Failed to resubscribe")
		}
	}

	if strings.HasSuffix(kvp.Key, nextAlertsIdKey) {
		// Ignore write on this key
		// Todo : Add a map of ignore keys
		return nil
	}
	watchErrors = 0

	if kvp.ModifiedIndex > alertsWatchIndex {
		alertsWatchIndex = kvp.ModifiedIndex
	}

	w := watcherMap[watcherKey]

	if kvp.Action == kvdb.KVDelete {
		err = w.cb(nil, AlertDeleteAction, prefix, kvp.Key)
		return err
	}

	var alert api.Alerts
	err = json.Unmarshal(kvp.Value, &alert)
	if err != nil {
		return fmt.Errorf("Failed to unmarshal Alert")
	}

	switch kvp.Action {
	case kvdb.KVCreate:
		err = w.cb(&alert, AlertCreateAction, prefix, kvp.Key)
	case kvdb.KVSet:
		err = w.cb(&alert, AlertUpdateAction, prefix, kvp.Key)
	default:
		err = fmt.Errorf("Unhandled KV Action")
	}
	return err
}

func subscribeWatch(key string) error {
	watchIndex := alertsWatchIndex
	if watchIndex != 0 {
		watchIndex = alertsWatchIndex + 1
	}

	w, ok := watcherMap[key]
	if !ok {
		return fmt.Errorf("Failed to find a watch on cluster : %v", key)
	}

	kv := w.kvdb
	if err := kv.WatchTree(alertsKey, watchIndex, nil, w.kvcb); err != nil {
		return err
	}
	return nil
}

func init() {
	kvdbMap = make(map[string]kvdb.Kvdb)
	watcherMap = make(map[string]*watcher)
	Register(Name, Init)
	Register(NameTest, Init)
}
<commit_msg>Return an error when we get a dubious kvdb callback<commit_after>package alerts

import (
	"encoding/json"
	"fmt"
	"github.com/libopenstorage/openstorage/api"
	"github.com/portworx/kvdb"
	"go.pedge.io/proto/time"
	"go.pedge.io/dlog"
	"strconv"
	"strings"
	"time"
)

type watcherStatus int
type watcher struct {
	kvcb   kvdb.WatchCB
	status watcherStatus
	cb     AlertsWatcherFunc
	clusterId string
	kvdb kvdb.Kvdb
}
type KvAlerts struct {
	kvdbName string
	kvdbDomain string
	kvdbMachines []string
	clusterId string
}

const (
	alertsKey       = "alerts/"
	nextAlertsIdKey = "nextAlertsId"
	clusterKey      = "cluster/"
	volumeKey       = "volume/"
	nodeKey         = "node/"
	bootstrap       = "bootstrap"
	// Name of this alerts client implementation
	Name            = "alerts_kvdb"
	// NameTest :  This alert instance used only for unit tests
	NameTest        = "alerts_kvdb_test"
)

const (
	watchBootstrap = watcherStatus(iota)
	watchReady
	watchError
)

var (
	kvdbMap          map[string]kvdb.Kvdb
	watcherMap       map[string]*watcher
	alertsWatchIndex uint64
	watchErrors      int
)

// GetKvdbInstance - Returns a kvdb instance associated with this alert client and clusterId combination
func (kva *KvAlerts) GetKvdbInstance() kvdb.Kvdb {
	return kvdbMap[kva.clusterId]
}

// Init initializes a AlertsClient interface implementation
func Init(name string, domain string, machines []string, clusterId string) (AlertsClient, error) {
	_, ok := kvdbMap[clusterId]
	if !ok {
		kv, err := kvdb.New(name, domain + "/" + clusterId, machines, nil)
		if err != nil {
			return nil, err
		}
		kvdbMap[clusterId] = kv
	}

	return &KvAlerts{kvdbName: name, kvdbDomain: domain, kvdbMachines: machines, clusterId: clusterId}, nil
}

// Raise raises an Alert
func (kva *KvAlerts) Raise(a api.Alerts) (api.Alerts, error) {
	kv := kva.GetKvdbInstance()
	if a.Resource == api.ResourceType_UNKNOWN_RESOURCE {
		return api.Alerts{}, ErrResourceNotFound
	}
	alertId, err := kva.getNextIdFromKVDB()
	if err != nil {
		return a, err
	}
	a.Id = alertId
	a.Timestamp = prototime.Now()
	a.Cleared = false
	_, err = kv.Create(getResourceKey(a.Resource)+strconv.FormatInt(a.Id, 10), &a, 0)
	return a, err
}

// Erase erases an alert
func (kva *KvAlerts) Erase(resourceType api.ResourceType, alertId int64) error {
	kv := kva.GetKvdbInstance()

	if resourceType == api.ResourceType_UNKNOWN_RESOURCE {
		return ErrResourceNotFound
	}
	var err error

	_, err = kv.Delete(getResourceKey(resourceType) + strconv.FormatInt(alertId, 10))
	return err
}

// Clear clears an alert
func (kva *KvAlerts) Clear(resourceType api.ResourceType, alertId int64) error {
	kv := kva.GetKvdbInstance()
	var (
		err   error
		alert api.Alerts
	)
	if resourceType == api.ResourceType_UNKNOWN_RESOURCE {
		return ErrResourceNotFound
	}

	_, err = kv.GetVal(getResourceKey(resourceType)+strconv.FormatInt(alertId, 10), &alert)
	if err != nil {
		return err
	}
	alert.Cleared = true

	_, err = kv.Update(getResourceKey(resourceType)+strconv.FormatInt(alertId, 10), &alert, 0)
	return err
}

// Retrieve retrieves a specific alert
func (kva *KvAlerts) Retrieve(resourceType api.ResourceType, alertId int64) (api.Alerts, error) {
	var (
		alert api.Alerts
		err   error
	)
	if resourceType == api.ResourceType_UNKNOWN_RESOURCE {
		return api.Alerts{}, ErrResourceNotFound
	}
	kv := kva.GetKvdbInstance()

	_, err = kv.GetVal(getResourceKey(resourceType)+strconv.FormatInt(alertId, 10), &alert)

	return alert, err
}

// Enumerate enumerates alerts
func (kva *KvAlerts) Enumerate(filter api.Alerts) ([]*api.Alerts, error) {
	allAlerts := []*api.Alerts{}
	resourceAlerts := []*api.Alerts{}
	var err error

	if filter.Resource != api.ResourceType_UNKNOWN_RESOURCE {
		resourceAlerts, err = kva.getResourceSpecificAlerts(filter.Resource)
		if err != nil {
			return nil, err
		}
	} else {
		resourceAlerts, err = kva.getAllAlerts()
	}

	if filter.Severity != 0 {
		for _, v := range resourceAlerts {
			if v.Severity <= filter.Severity {
				allAlerts = append(allAlerts, v)
			}
		}
	} else {
		allAlerts = append(allAlerts, resourceAlerts...)
	}

	return allAlerts, err
}

// EnumerateWithinTimeRange enumerates alerts between timeStart and timeEnd
func (kva *KvAlerts) EnumerateWithinTimeRange(
	timeStart time.Time,
	timeEnd time.Time,
	resourceType api.ResourceType,
) ([]*api.Alerts, error) {
	allAlerts := []*api.Alerts{}
	resourceAlerts := []*api.Alerts{}
	var err error

	if resourceType != 0 {
		resourceAlerts, err = kva.getResourceSpecificAlerts(resourceType)
		if err != nil {
			return nil, err
		}
	} else {
		resourceAlerts, err = kva.getAllAlerts()
		if err != nil {
			return nil, err
		}
	}
	for _, v := range resourceAlerts {
		alertTime := prototime.TimestampToTime(v.Timestamp)
		if alertTime.Before(timeEnd) && alertTime.After(timeStart) {
			allAlerts = append(allAlerts, v)
		}
	}
	return allAlerts, nil
}

// Watch on all alerts
func (kva *KvAlerts) Watch(clusterId string, alertsWatcherFunc AlertsWatcherFunc) error {
	_, ok := kvdbMap[clusterId]
	if !ok {
		kv, err := kvdb.New(kva.kvdbName, kva.kvdbDomain + "/" + clusterId, kva.kvdbMachines, nil)
		if err != nil {
			return err
		}
		kvdbMap[clusterId] = kv
	}

	kv := kvdbMap[clusterId]
	alertsWatcher := &watcher{status: watchBootstrap, cb: alertsWatcherFunc, kvcb: kvdbWatch, kvdb: kv}
	watcherKey := clusterId
	watcherMap[watcherKey] = alertsWatcher

	if err := subscribeWatch(watcherKey); err != nil {
		return err
	}

	// Subscribe for a watch can be in a goroutine. Bootstrap by writing to the key and waiting for an update
	retries := 0

	for alertsWatcher.status == watchBootstrap {
		_, err := kv.Put(alertsKey+bootstrap, time.Now(), 1)
		if err != nil {
			return err
		}
		if alertsWatcher.status == watchBootstrap {
			retries++
			time.Sleep(time.Millisecond * 100)
		}
		if retries == 5 {
			return fmt.Errorf("Failed to bootstrap watch on %s", clusterId)
		}
	}
	if alertsWatcher.status != watchReady {
		return fmt.Errorf("Failed to watch on %s", clusterId)
	}

	return nil
}

// Shutdown
func (kva *KvAlerts) Shutdown() {
}

// String
func (kva *KvAlerts) String() string {
	return Name
}

func getResourceKey(resourceType api.ResourceType) string {
	if resourceType == api.ResourceType_VOLUMES {
		return alertsKey + volumeKey
	}
	if resourceType == api.ResourceType_NODE {
		return alertsKey + nodeKey
	}
	return alertsKey + clusterKey
}

func getNextAlertsIdKey() string {
	return alertsKey + nextAlertsIdKey
}

func (kva *KvAlerts) getNextIdFromKVDB() (int64, error) {
	kv := kva.GetKvdbInstance()
	nextAlertsId := 0
	kvp, err := kv.Create(getNextAlertsIdKey(), strconv.FormatInt(int64(nextAlertsId+1), 10), 0)

	for err != nil {
		kvp, err = kv.GetVal(getNextAlertsIdKey(), &nextAlertsId)
		if err != nil {
			err = ErrNotInitialized
			return -1, err
		}
		prevValue := kvp.Value
		newKvp := *kvp
		newKvp.Value = []byte(strconv.FormatInt(int64(nextAlertsId+1), 10))
		kvp, err = kv.CompareAndSet(&newKvp, kvdb.KVFlags(0), prevValue)
	}
	return int64(nextAlertsId), err
}

func (kva *KvAlerts) getResourceSpecificAlerts(resourceType api.ResourceType) ([]*api.Alerts, error) {
	kv := kva.GetKvdbInstance()
	allAlerts := []*api.Alerts{}
	kvp, err := kv.Enumerate(getResourceKey(resourceType))
	if err != nil {
		return nil, err
	}

	for _, v := range kvp {
		var elem *api.Alerts
		err = json.Unmarshal(v.Value, &elem)
		if err != nil {
			return nil, err
		}
		allAlerts = append(allAlerts, elem)
	}
	return allAlerts, nil
}

func (kva *KvAlerts) getAllAlerts() ([]*api.Alerts, error) {
	allAlerts := []*api.Alerts{}
	clusterAlerts := []*api.Alerts{}
	nodeAlerts := []*api.Alerts{}
	volumeAlerts := []*api.Alerts{}
	var err error

	nodeAlerts, err = kva.getResourceSpecificAlerts(api.ResourceType_NODE)
	if err == nil {
		allAlerts = append(allAlerts, nodeAlerts...)
	}
	volumeAlerts, err = kva.getResourceSpecificAlerts(api.ResourceType_VOLUMES)
	if err == nil {
		allAlerts = append(allAlerts, volumeAlerts...)
	}
	clusterAlerts, err = kva.getResourceSpecificAlerts(api.ResourceType_CLUSTER)
	if err == nil {
		allAlerts = append(allAlerts, clusterAlerts...)
	}

	if len(allAlerts) > 0 {
		return allAlerts, nil
	} else if len(allAlerts) == 0 {
		return nil, fmt.Errorf("No alerts raised yet")
	}
	return allAlerts, err
}

func kvdbWatch(prefix string, opaque interface{}, kvp *kvdb.KVPair, err error) error {
	mutex.Lock()
	defer mutex.Unlock()

	watcherKey := strings.Split(prefix, "/")[1]

	if err == nil && strings.HasSuffix(kvp.Key, bootstrap) {
		w := watcherMap[watcherKey]
		w.status = watchReady
		return nil
	}

	if err != nil {
		if w:= watcherMap[watcherKey]; w.status == watchBootstrap {
			w.status = watchError
			return err
		}
		if watchErrors == 5 {
			dlog.Warnf("Too many watch errors : %v. Error is %s", watchErrors, err.Error())
		}
		watchErrors++
		if err := subscribeWatch(watcherKey); err != nil {
			dlog.Warnf("Failed to resubscribe : %s", err.Error())
		}
		return err
	}

	if strings.HasSuffix(kvp.Key, nextAlertsIdKey) {
		// Ignore write on this key
		// Todo : Add a map of ignore keys
		return nil
	}
	watchErrors = 0

	if kvp.ModifiedIndex > alertsWatchIndex {
		alertsWatchIndex = kvp.ModifiedIndex
	}

	w := watcherMap[watcherKey]

	if kvp.Action == kvdb.KVDelete {
		err = w.cb(nil, AlertDeleteAction, prefix, kvp.Key)
		return err
	}

	var alert api.Alerts
	err = json.Unmarshal(kvp.Value, &alert)
	if err != nil {
		return fmt.Errorf("Failed to unmarshal Alert")
	}

	switch kvp.Action {
	case kvdb.KVCreate:
		err = w.cb(&alert, AlertCreateAction, prefix, kvp.Key)
	case kvdb.KVSet:
		err = w.cb(&alert, AlertUpdateAction, prefix, kvp.Key)
	default:
		err = fmt.Errorf("Unhandled KV Action")
	}
	return err
}

func subscribeWatch(key string) error {
	watchIndex := alertsWatchIndex
	if watchIndex != 0 {
		watchIndex = alertsWatchIndex + 1
	}

	w, ok := watcherMap[key]
	if !ok {
		return fmt.Errorf("Failed to find a watch on cluster : %v", key)
	}

	kv := w.kvdb
	if err := kv.WatchTree(alertsKey, watchIndex, nil, w.kvcb); err != nil {
		return err
	}
	return nil
}

func init() {
	kvdbMap = make(map[string]kvdb.Kvdb)
	watcherMap = make(map[string]*watcher)
	Register(Name, Init)
	Register(NameTest, Init)
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmStorageAccount() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmStorageAccountCreate,
		Read:   resourceArmStorageAccountRead,
		Update: resourceArmStorageAccountUpdate,
		Delete: resourceArmStorageAccountDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArmStorageAccountName,
			},

			"resource_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": &schema.Schema{
				Type:      schema.TypeString,
				Required:  true,
				ForceNew:  true,
				StateFunc: azureRMNormalizeLocation,
			},

			"account_type": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateArmStorageAccountType,
			},

			"primary_location": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_location": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_blob_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_blob_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_queue_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_queue_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_table_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_table_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			// NOTE: The API does not appear to expose a secondary file endpoint
			"primary_file_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	resourceGroupName := d.Get("resource_group_name").(string)
	storageAccountName := d.Get("name").(string)
	accountType := d.Get("account_type").(string)
	location := d.Get("location").(string)
	tags := d.Get("tags").(map[string]interface{})

	opts := storage.AccountCreateParameters{
		Location: &location,
		Properties: &storage.AccountPropertiesCreateParameters{
			AccountType: storage.AccountType(accountType),
		},
		Tags: expandTags(tags),
	}

	accResp, err := client.Create(resourceGroupName, storageAccountName, opts)
	if err != nil {
		return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err)
	}
	_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK)
	if err != nil {
		return fmt.Errorf("Error creating Azure Storage Account %q: %s", storageAccountName, err)
	}

	// The only way to get the ID back apparently is to read the resource again
	account, err := client.GetProperties(resourceGroupName, storageAccountName)
	if err != nil {
		return fmt.Errorf("Error retrieving Azure Storage Account %q: %s", storageAccountName, err)
	}

	d.SetId(*account.ID)

	return resourceArmStorageAccountRead(d, meta)
}

// resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined
// and idempotent operation for CreateOrUpdate. In particular updating all of the parameters
// available requires a call to Update per parameter...
func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient
	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	storageAccountName := id.Path["storageAccounts"]
	resourceGroupName := id.ResourceGroup

	d.Partial(true)

	if d.HasChange("account_type") {
		accountType := d.Get("account_type").(string)

		opts := storage.AccountUpdateParameters{
			Properties: &storage.AccountPropertiesUpdateParameters{
				AccountType: storage.AccountType(accountType),
			},
		}
		accResp, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err)
		}
		_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err)
		}

		d.SetPartial("account_type")
	}

	if d.HasChange("tags") {
		tags := d.Get("tags").(map[string]interface{})

		opts := storage.AccountUpdateParameters{
			Tags: expandTags(tags),
		}
		accResp, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err)
		}
		_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err)
		}

		d.SetPartial("tags")
	}

	d.Partial(false)
	return nil
}

func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	resp, err := client.GetProperties(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %s", name, err)
	}

	d.Set("location", resp.Location)
	d.Set("account_type", resp.Properties.AccountType)
	d.Set("primary_location", resp.Properties.PrimaryLocation)
	d.Set("secondary_location", resp.Properties.SecondaryLocation)

	if resp.Properties.PrimaryEndpoints != nil {
		d.Set("primary_blob_endpoint", resp.Properties.PrimaryEndpoints.Blob)
		d.Set("primary_queue_endpoint", resp.Properties.PrimaryEndpoints.Queue)
		d.Set("primary_table_endpoint", resp.Properties.PrimaryEndpoints.Table)
		d.Set("primary_file_endpoint", resp.Properties.PrimaryEndpoints.File)
	}

	if resp.Properties.SecondaryEndpoints != nil {
		if resp.Properties.SecondaryEndpoints.Blob != nil {
			d.Set("secondary_blob_endpoint", resp.Properties.SecondaryEndpoints.Blob)
		} else {
			d.Set("secondary_blob_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Queue != nil {
			d.Set("secondary_queue_endpoint", resp.Properties.SecondaryEndpoints.Queue)
		} else {
			d.Set("secondary_queue_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Table != nil {
			d.Set("secondary_table_endpoint", resp.Properties.SecondaryEndpoints.Table)
		} else {
			d.Set("secondary_table_endpoint", "")
		}
	}

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	accResp, err := client.Delete(resGroup, name)
	if err != nil {
		return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %s", name, err)
	}
	_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response, http.StatusNotFound)
	if err != nil {
		return fmt.Errorf("Error polling for AzureRM delete request for storage account %q: %s", name, err)
	}

	return nil
}

func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) {
	input := v.(string)

	if !regexp.MustCompile(`\A([a-z0-9]{3,24})\z`).MatchString(input) {
		es = append(es, fmt.Errorf("name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long"))
	}

	return
}

func validateArmStorageAccountType(v interface{}, k string) (ws []string, es []error) {
	validAccountTypes := []string{"standard_lrs", "standard_zrs",
		"standard_grs", "standard_ragrs", "premium_lrs"}

	input := strings.ToLower(v.(string))

	for _, valid := range validAccountTypes {
		if valid == input {
			return
		}
	}

	es = append(es, fmt.Errorf("Invalid storage account type %q", input))
	return
}
<commit_msg>provider/azurerm: Add support for exporting the (#6742)<commit_after>package azurerm

import (
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmStorageAccount() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmStorageAccountCreate,
		Read:   resourceArmStorageAccountRead,
		Update: resourceArmStorageAccountUpdate,
		Delete: resourceArmStorageAccountDelete,

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArmStorageAccountName,
			},

			"resource_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"location": &schema.Schema{
				Type:      schema.TypeString,
				Required:  true,
				ForceNew:  true,
				StateFunc: azureRMNormalizeLocation,
			},

			"account_type": &schema.Schema{
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateArmStorageAccountType,
			},

			"primary_location": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_location": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_blob_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_blob_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_queue_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_queue_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_table_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_table_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			// NOTE: The API does not appear to expose a secondary file endpoint
			"primary_file_endpoint": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"primary_access_key": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"secondary_access_key": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	resourceGroupName := d.Get("resource_group_name").(string)
	storageAccountName := d.Get("name").(string)
	accountType := d.Get("account_type").(string)
	location := d.Get("location").(string)
	tags := d.Get("tags").(map[string]interface{})

	opts := storage.AccountCreateParameters{
		Location: &location,
		Properties: &storage.AccountPropertiesCreateParameters{
			AccountType: storage.AccountType(accountType),
		},
		Tags: expandTags(tags),
	}

	accResp, err := client.Create(resourceGroupName, storageAccountName, opts)
	if err != nil {
		return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err)
	}
	_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK)
	if err != nil {
		return fmt.Errorf("Error creating Azure Storage Account %q: %s", storageAccountName, err)
	}

	// The only way to get the ID back apparently is to read the resource again
	account, err := client.GetProperties(resourceGroupName, storageAccountName)
	if err != nil {
		return fmt.Errorf("Error retrieving Azure Storage Account %q: %s", storageAccountName, err)
	}

	d.SetId(*account.ID)

	return resourceArmStorageAccountRead(d, meta)
}

// resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined
// and idempotent operation for CreateOrUpdate. In particular updating all of the parameters
// available requires a call to Update per parameter...
func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient
	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	storageAccountName := id.Path["storageAccounts"]
	resourceGroupName := id.ResourceGroup

	d.Partial(true)

	if d.HasChange("account_type") {
		accountType := d.Get("account_type").(string)

		opts := storage.AccountUpdateParameters{
			Properties: &storage.AccountPropertiesUpdateParameters{
				AccountType: storage.AccountType(accountType),
			},
		}
		accResp, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err)
		}
		_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err)
		}

		d.SetPartial("account_type")
	}

	if d.HasChange("tags") {
		tags := d.Get("tags").(map[string]interface{})

		opts := storage.AccountUpdateParameters{
			Tags: expandTags(tags),
		}
		accResp, err := client.Update(resourceGroupName, storageAccountName, opts)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err)
		}
		_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK)
		if err != nil {
			return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err)
		}

		d.SetPartial("tags")
	}

	d.Partial(false)
	return nil
}

func resourceArmStorageAccountRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	resp, err := client.GetProperties(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error reading the state of AzureRM Storage Account %q: %s", name, err)
	}

	keys, err := client.ListKeys(resGroup, name)
	if err != nil {
		return err
	}

	d.Set("primary_access_key", keys.Key1)
	d.Set("secondary_access_key", keys.Key2)
	d.Set("location", resp.Location)
	d.Set("account_type", resp.Properties.AccountType)
	d.Set("primary_location", resp.Properties.PrimaryLocation)
	d.Set("secondary_location", resp.Properties.SecondaryLocation)

	if resp.Properties.PrimaryEndpoints != nil {
		d.Set("primary_blob_endpoint", resp.Properties.PrimaryEndpoints.Blob)
		d.Set("primary_queue_endpoint", resp.Properties.PrimaryEndpoints.Queue)
		d.Set("primary_table_endpoint", resp.Properties.PrimaryEndpoints.Table)
		d.Set("primary_file_endpoint", resp.Properties.PrimaryEndpoints.File)
	}

	if resp.Properties.SecondaryEndpoints != nil {
		if resp.Properties.SecondaryEndpoints.Blob != nil {
			d.Set("secondary_blob_endpoint", resp.Properties.SecondaryEndpoints.Blob)
		} else {
			d.Set("secondary_blob_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Queue != nil {
			d.Set("secondary_queue_endpoint", resp.Properties.SecondaryEndpoints.Queue)
		} else {
			d.Set("secondary_queue_endpoint", "")
		}
		if resp.Properties.SecondaryEndpoints.Table != nil {
			d.Set("secondary_table_endpoint", resp.Properties.SecondaryEndpoints.Table)
		} else {
			d.Set("secondary_table_endpoint", "")
		}
	}

	flattenAndSetTags(d, resp.Tags)

	return nil
}

func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient).storageServiceClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	name := id.Path["storageAccounts"]
	resGroup := id.ResourceGroup

	accResp, err := client.Delete(resGroup, name)
	if err != nil {
		return fmt.Errorf("Error issuing AzureRM delete request for storage account %q: %s", name, err)
	}
	_, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response, http.StatusNotFound)
	if err != nil {
		return fmt.Errorf("Error polling for AzureRM delete request for storage account %q: %s", name, err)
	}

	return nil
}

func validateArmStorageAccountName(v interface{}, k string) (ws []string, es []error) {
	input := v.(string)

	if !regexp.MustCompile(`\A([a-z0-9]{3,24})\z`).MatchString(input) {
		es = append(es, fmt.Errorf("name can only consist of lowercase letters and numbers, and must be between 3 and 24 characters long"))
	}

	return
}

func validateArmStorageAccountType(v interface{}, k string) (ws []string, es []error) {
	validAccountTypes := []string{"standard_lrs", "standard_zrs",
		"standard_grs", "standard_ragrs", "premium_lrs"}

	input := strings.ToLower(v.(string))

	for _, valid := range validAccountTypes {
		if valid == input {
			return
		}
	}

	es = append(es, fmt.Errorf("Invalid storage account type %q", input))
	return
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestHandlerForDisallowedMethods(t *testing.T) {
	disallowedMethods := []string{"GET", "DELETE", "PUT", "TRACE", "PATCH"}
	randomUrls := []string{"/", "/blah"}

	for _, method := range disallowedMethods {
		for _, url := range randomUrls {
			t.Run(method+url, func(t *testing.T) {
				request, err := http.NewRequest(method, "localhost:8080"+url, nil)
				if err != nil {
					t.Fatalf("Failed to create request: %v", err)
				}
				recorder := httptest.NewRecorder()
				handleViolationReport(recorder, request)

				response := recorder.Result()
				defer response.Body.Close()

				if response.StatusCode != http.StatusMethodNotAllowed {
					t.Errorf("Expected HTTP status %v; got %v", http.StatusMethodNotAllowed, response.Status)
				}
			})
		}
	}
}
<commit_msg>Don't include hostname in `urlStr` param<commit_after>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestHandlerForDisallowedMethods(t *testing.T) {
	disallowedMethods := []string{"GET", "DELETE", "PUT", "TRACE", "PATCH"}
	randomUrls := []string{"/", "/blah"}

	for _, method := range disallowedMethods {
		for _, url := range randomUrls {
			t.Run(method+url, func(t *testing.T) {
				request, err := http.NewRequest(method, url, nil)
				if err != nil {
					t.Fatalf("Failed to create request: %v", err)
				}
				recorder := httptest.NewRecorder()
				handleViolationReport(recorder, request)

				response := recorder.Result()
				defer response.Body.Close()

				if response.StatusCode != http.StatusMethodNotAllowed {
					t.Errorf("Expected HTTP status %v; got %v", http.StatusMethodNotAllowed, response.StatusCode)
				}
			})
		}
	}
}
<|endoftext|>