<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/mitchellh/goamz/autoscaling"
)

func resourceAwsAutoscalingGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAutoscalingGroupCreate,
		Read:   resourceAwsAutoscalingGroupRead,
		Update: resourceAwsAutoscalingGroupUpdate,
		Delete: resourceAwsAutoscalingGroupDelete,

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

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

			"desired_capacity": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"min_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"max_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"default_cooldown": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"force_delete": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_grace_period": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"availability_zones": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"load_balancers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"vpc_zone_identifier": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"termination_policies": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
		},
	}
}

func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroup
	autoScalingGroupOpts.Name = d.Get("name").(string)
	autoScalingGroupOpts.HealthCheckType = d.Get("health_check_type").(string)
	autoScalingGroupOpts.LaunchConfigurationName = d.Get("launch_configuration").(string)
	autoScalingGroupOpts.MinSize = d.Get("min_size").(int)
	autoScalingGroupOpts.MaxSize = d.Get("max_size").(int)
	autoScalingGroupOpts.SetMinSize = true
	autoScalingGroupOpts.SetMaxSize = true
	autoScalingGroupOpts.AvailZone = expandStringList(
		d.Get("availability_zones").(*schema.Set).List())

	if v, ok := d.GetOk("default_cooldown"); ok {
		autoScalingGroupOpts.DefaultCooldown = v.(int)
		autoScalingGroupOpts.SetDefaultCooldown = true
	}

	if v, ok := d.GetOk("desired_capacity"); ok {
		autoScalingGroupOpts.DesiredCapacity = v.(int)
		autoScalingGroupOpts.SetDesiredCapacity = true
	}

	if v, ok := d.GetOk("health_check_grace_period"); ok {
		autoScalingGroupOpts.HealthCheckGracePeriod = v.(int)
		autoScalingGroupOpts.SetHealthCheckGracePeriod = true
	}

	if v, ok := d.GetOk("load_balancers"); ok {
		autoScalingGroupOpts.LoadBalancerNames = expandStringList(
			v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("vpc_zone_identifier"); ok {
		autoScalingGroupOpts.VPCZoneIdentifier = expandStringList(
			v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("termination_policies"); ok {
		autoScalingGroupOpts.TerminationPolicies = expandStringList(
			v.(*schema.Set).List())
	}

	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
	_, err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
	if err != nil {
		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
	}

	d.SetId(d.Get("name").(string))
	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}

	d.Set("availability_zones", g.AvailabilityZones)
	d.Set("default_cooldown", g.DefaultCooldown)
	d.Set("desired_capacity", g.DesiredCapacity)
	d.Set("health_check_grace_period", g.HealthCheckGracePeriod)
	d.Set("health_check_type", g.HealthCheckType)
	d.Set("launch_configuration", g.LaunchConfigurationName)
	d.Set("load_balancers", g.LoadBalancerNames)
	d.Set("min_size", g.MinSize)
	d.Set("max_size", g.MaxSize)
	d.Set("name", g.Name)
	d.Set("vpc_zone_identifier", strings.Split(g.VPCZoneIdentifier, ","))

	return nil
}

func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	opts := autoscaling.UpdateAutoScalingGroup{
		Name: d.Id(),
	}

	if d.HasChange("desired_capacity") {
		opts.DesiredCapacity = d.Get("desired_capacity").(int)
		opts.SetDesiredCapacity = true
	}

	if d.HasChange("min_size") {
		opts.MinSize = d.Get("min_size").(int)
		opts.SetMinSize = true
	}

	if d.HasChange("max_size") {
		opts.MaxSize = d.Get("max_size").(int)
		opts.SetMaxSize = true
	}

	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
	_, err := autoscalingconn.UpdateAutoScalingGroup(&opts)
	if err != nil {
		d.Partial(true)
		return fmt.Errorf("Error updating Autoscaling group: %s", err)
	}

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// Read the autoscaling group first. If it doesn't exist, we're done.
	// We need the group in order to check if there are instances attached.
	// If so, we need to remove those first.
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}
	if len(g.Instances) > 0 || g.DesiredCapacity > 0 {
		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
			return err
		}
	}

	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
	deleteopts := autoscaling.DeleteAutoScalingGroup{Name: d.Id()}

	// You can force an autoscaling group to delete
	// even if it's in the process of scaling a resource.
	// Normally, you would set the min-size and max-size to 0,0
	// and then delete the group. This bypasses that and leaves
	// resources potentially dangling.
	if d.Get("force_delete").(bool) {
		deleteopts.ForceDelete = true
	}

	if _, err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
		autoscalingerr, ok := err.(*autoscaling.Error)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			return nil
		}

		return err
	}

	return nil
}

func getAwsAutoscalingGroup(
	d *schema.ResourceData,
	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	describeOpts := autoscaling.DescribeAutoScalingGroups{
		Names: []string{d.Id()},
	}

	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
	if err != nil {
		autoscalingerr, ok := err.(*autoscaling.Error)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			d.SetId("")
			return nil, nil
		}

		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
	}

	// Search for the autoscaling group
	for idx, asc := range describeGroups.AutoScalingGroups {
		if asc.Name == d.Id() {
			return &describeGroups.AutoScalingGroups[idx], nil
		}
	}

	// ASG not found
	d.SetId("")
	return nil, nil
}

func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// First, set the capacity to zero so the group will drain
	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
	opts := autoscaling.UpdateAutoScalingGroup{
		Name:               d.Id(),
		DesiredCapacity:    0,
		MinSize:            0,
		MaxSize:            0,
		SetDesiredCapacity: true,
		SetMinSize:         true,
		SetMaxSize:         true,
	}
	if _, err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
	}

	// Next, wait for the autoscale group to drain
	log.Printf("[DEBUG] Waiting for group to have zero instances")
	return resource.Retry(10*time.Minute, func() error {
		g, err := getAwsAutoscalingGroup(d, meta)
		if err != nil {
			return resource.RetryError{err}
		}
		if g == nil {
			return nil
		}

		if len(g.Instances) == 0 {
			return nil
		}

		return fmt.Errorf("group still has %d instances", len(g.Instances))
	})
}
<commit_msg>providers/aws: read ASG termination policies<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/mitchellh/goamz/autoscaling"
)

func resourceAwsAutoscalingGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAutoscalingGroupCreate,
		Read:   resourceAwsAutoscalingGroupRead,
		Update: resourceAwsAutoscalingGroupUpdate,
		Delete: resourceAwsAutoscalingGroupDelete,

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

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

			"desired_capacity": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},

			"min_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"max_size": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"default_cooldown": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"force_delete": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_grace_period": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"health_check_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"availability_zones": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"load_balancers": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"vpc_zone_identifier": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"termination_policies": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},
		},
	}
}

func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroup
	autoScalingGroupOpts.Name = d.Get("name").(string)
	autoScalingGroupOpts.HealthCheckType = d.Get("health_check_type").(string)
	autoScalingGroupOpts.LaunchConfigurationName = d.Get("launch_configuration").(string)
	autoScalingGroupOpts.MinSize = d.Get("min_size").(int)
	autoScalingGroupOpts.MaxSize = d.Get("max_size").(int)
	autoScalingGroupOpts.SetMinSize = true
	autoScalingGroupOpts.SetMaxSize = true
	autoScalingGroupOpts.AvailZone = expandStringList(
		d.Get("availability_zones").(*schema.Set).List())

	if v, ok := d.GetOk("default_cooldown"); ok {
		autoScalingGroupOpts.DefaultCooldown = v.(int)
		autoScalingGroupOpts.SetDefaultCooldown = true
	}

	if v, ok := d.GetOk("desired_capacity"); ok {
		autoScalingGroupOpts.DesiredCapacity = v.(int)
		autoScalingGroupOpts.SetDesiredCapacity = true
	}

	if v, ok := d.GetOk("health_check_grace_period"); ok {
		autoScalingGroupOpts.HealthCheckGracePeriod = v.(int)
		autoScalingGroupOpts.SetHealthCheckGracePeriod = true
	}

	if v, ok := d.GetOk("load_balancers"); ok {
		autoScalingGroupOpts.LoadBalancerNames = expandStringList(
			v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("vpc_zone_identifier"); ok {
		autoScalingGroupOpts.VPCZoneIdentifier = expandStringList(
			v.(*schema.Set).List())
	}

	if v, ok := d.GetOk("termination_policies"); ok {
		autoScalingGroupOpts.TerminationPolicies = expandStringList(
			v.(*schema.Set).List())
	}

	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
	_, err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
	if err != nil {
		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
	}

	d.SetId(d.Get("name").(string))
	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}

	d.Set("availability_zones", g.AvailabilityZones)
	d.Set("default_cooldown", g.DefaultCooldown)
	d.Set("desired_capacity", g.DesiredCapacity)
	d.Set("health_check_grace_period", g.HealthCheckGracePeriod)
	d.Set("health_check_type", g.HealthCheckType)
	d.Set("launch_configuration", g.LaunchConfigurationName)
	d.Set("load_balancers", g.LoadBalancerNames)
	d.Set("min_size", g.MinSize)
	d.Set("max_size", g.MaxSize)
	d.Set("name", g.Name)
	d.Set("vpc_zone_identifier", strings.Split(g.VPCZoneIdentifier, ","))
	d.Set("termination_policies", g.TerminationPolicies)

	return nil
}

func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	opts := autoscaling.UpdateAutoScalingGroup{
		Name: d.Id(),
	}

	if d.HasChange("desired_capacity") {
		opts.DesiredCapacity = d.Get("desired_capacity").(int)
		opts.SetDesiredCapacity = true
	}

	if d.HasChange("min_size") {
		opts.MinSize = d.Get("min_size").(int)
		opts.SetMinSize = true
	}

	if d.HasChange("max_size") {
		opts.MaxSize = d.Get("max_size").(int)
		opts.SetMaxSize = true
	}

	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
	_, err := autoscalingconn.UpdateAutoScalingGroup(&opts)
	if err != nil {
		d.Partial(true)
		return fmt.Errorf("Error updating Autoscaling group: %s", err)
	}

	return resourceAwsAutoscalingGroupRead(d, meta)
}

func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// Read the autoscaling group first. If it doesn't exist, we're done.
	// We need the group in order to check if there are instances attached.
	// If so, we need to remove those first.
	g, err := getAwsAutoscalingGroup(d, meta)
	if err != nil {
		return err
	}
	if g == nil {
		return nil
	}
	if len(g.Instances) > 0 || g.DesiredCapacity > 0 {
		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
			return err
		}
	}

	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
	deleteopts := autoscaling.DeleteAutoScalingGroup{Name: d.Id()}

	// You can force an autoscaling group to delete
	// even if it's in the process of scaling a resource.
	// Normally, you would set the min-size and max-size to 0,0
	// and then delete the group. This bypasses that and leaves
	// resources potentially dangling.
	if d.Get("force_delete").(bool) {
		deleteopts.ForceDelete = true
	}

	if _, err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
		autoscalingerr, ok := err.(*autoscaling.Error)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			return nil
		}

		return err
	}

	return nil
}

func getAwsAutoscalingGroup(
	d *schema.ResourceData,
	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	describeOpts := autoscaling.DescribeAutoScalingGroups{
		Names: []string{d.Id()},
	}

	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
	if err != nil {
		autoscalingerr, ok := err.(*autoscaling.Error)
		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
			d.SetId("")
			return nil, nil
		}

		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
	}

	// Search for the autoscaling group
	for idx, asc := range describeGroups.AutoScalingGroups {
		if asc.Name == d.Id() {
			return &describeGroups.AutoScalingGroups[idx], nil
		}
	}

	// ASG not found
	d.SetId("")
	return nil, nil
}

func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	// First, set the capacity to zero so the group will drain
	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
	opts := autoscaling.UpdateAutoScalingGroup{
		Name:               d.Id(),
		DesiredCapacity:    0,
		MinSize:            0,
		MaxSize:            0,
		SetDesiredCapacity: true,
		SetMinSize:         true,
		SetMaxSize:         true,
	}
	if _, err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
	}

	// Next, wait for the autoscale group to drain
	log.Printf("[DEBUG] Waiting for group to have zero instances")
	return resource.Retry(10*time.Minute, func() error {
		g, err := getAwsAutoscalingGroup(d, meta)
		if err != nil {
			return resource.RetryError{err}
		}
		if g == nil {
			return nil
		}

		if len(g.Instances) == 0 {
			return nil
		}

		return fmt.Errorf("group still has %d instances", len(g.Instances))
	})
}
<|endoftext|>


<commit_before>package relfile

import (
	"bytes"
	"encoding/gob"
	"github.com/DHowett/go-plist"
	"github.com/syndtr/goleveldb/leveldb"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"time"
)

const (
	ProvisioningTypeAdHoc       = "ad-hoc"
	ProvisioningTypeAppStore    = "app-store"
	ProvisioningTypeDevelopment = "development"
	ProvisioningTypeEnterprise  = "enterprise"

	CertificateTypeDeveloper    = "iPhone Developer"
	CertificateTypeDistribution = "iPhone Distribution"
)

// ProvisioningProfile struct
type ProvisioningProfile struct {
	AppIDName             string       `plist:"AppIDName"`
	CreationDate          time.Time    `plist:"CreationDate"`
	DeveloperCertificates [][]byte     `plist:"DeveloperCertificates"`
	Entitlements          Entitlements `plist:"Entitlements"`
	Name                  string       `plist:"Name"`
	ProvisionedDevices    []string     `plist:"ProvisionedDevices,omitempty"`
	ProvisionsAllDevices  bool         `plist:"ProvisionsAllDevices,omitempty"`
	TeamIdentifiers       []string     `plist:"TeamIdentifier"`
	TeamName              string       `plist:"TeamName"`
	TimeToLive            int          `plist:"TimeToLive"`
	UUID                  string       `plist:"UUID"`
	Version               int          `plist:"Version"`
}

/* Mested struct is not working in go-plist...
 */
type Entitlements struct {
	GetTaskAllow            bool   `plist:"get-task-allow"`
	ApplicationIdentifier   string `plist:"application-identifier"`
	DeveloperTeamIdentifier string `plist:"com.apple.developer.team-identifier"`
}

func (p ProvisioningProfile) TeamID() (s string) {
	return p.Entitlements.DeveloperTeamIdentifier
}

func (p ProvisioningProfile) AppID() (s string) {
	return strings.TrimLeft(p.Entitlements.ApplicationIdentifier, p.TeamID()+".")
}

func (p ProvisioningProfile) CertificateType() string {
	if p.ProvisioningType() == ProvisioningTypeDevelopment {
		return CertificateTypeDeveloper
	} else {
		return CertificateTypeDistribution
	}
}

func (p ProvisioningProfile) ProvisioningType() string {
	if p.ProvisionsAllDevices {
		return ProvisioningTypeEnterprise
	}

	if p.ProvisionedDevices == nil {
		return ProvisioningTypeAppStore
	} else {
		if p.Entitlements.GetTaskAllow {
			return ProvisioningTypeDevelopment
		} else {
			return ProvisioningTypeAdHoc
		}
	}
}

func decodeCMS(path string) string {
	out, err := ioutil.TempFile("", "relax/provisioning_profile")

	if err != nil {
		logger.Fatalf("error: %v", err)
	}

	if _, err = exec.Command("/usr/bin/security", "cms", "-D", "-i", path, "-o", out.Name()).Output(); err != nil {
		logger.Fatalf("error: %v", err)
	}

	return out.Name()
}

func newProvisioningProfile(path string) *ProvisioningProfile {
	file, err := os.Open(path)

	if err != nil {
		logger.Fatalf("error: %v", err)
	}

	decoder := plist.NewDecoder(file)

	pp := ProvisioningProfile{}
	if err := decoder.Decode(&pp); err != nil {
		logger.Fatalf("error: %v", err)
	}

	return &pp
}

func NewEntitlements(m map[string]interface{}) Entitlements {
	return Entitlements{
		GetTaskAllow:            m["get-task-allow"].(bool),
		ApplicationIdentifier:   m["application-identifier"].(string),
		DeveloperTeamIdentifier: m["com.apple.developer.team-identifier"].(string),
	}
}

type ProvisioningProfileInfo struct {
	Pp   ProvisioningProfile
	Name string
}

func getCacheDBName() string {
	return os.TempDir() + "relax/cachedb"
}

func getCacheDB() (*leveldb.DB, error) {
	path := getCacheDBName()
	db, err := leveldb.OpenFile(path, nil)
	if err != nil {
		// Prevent error<file missing [file=MANIFEST-000000]>
		ClearCache()
		return leveldb.OpenFile(path, nil)
	}
	return db, err
}

func ClearCache() error {
	return os.RemoveAll(getCacheDBName())
}

var PP_ROOT string = os.Getenv("HOME") + "/Library/MobileDevice/Provisioning Profiles"

func FindProvisioningProfile(pattern string, team string) []*ProvisioningProfileInfo {
	db, err := getCacheDB()
	if err != nil {
		logger.Fatalf("error: %v", err)
	}
	defer db.Close()

	files, err := ioutil.ReadDir(PP_ROOT)
	if err != nil {
		logger.Fatalf("error: %v", err)
	}

	s := make(chan bool, 32)
	c := make(chan *ProvisioningProfileInfo, len(files))
	count := 0
	for _, file := range files {
		s <- true

		name := file.Name()

		if false == strings.HasSuffix(name, "mobileprovision") {
			continue
		}

		go func() {
			defer func() { <-s }()
			var (
				info   ProvisioningProfileInfo
				buffer bytes.Buffer
			)
			defer func() { c <- &info }()

			if buf, err := db.Get([]byte(name), nil); err == nil {
				dec := gob.NewDecoder(bytes.NewBuffer(buf))
				if err = dec.Decode(&info); err == nil {
					if _, err = os.Stat(info.Name); err == nil {
						return
					}
				}
			}

			in := PP_ROOT + "/" + name
			out := decodeCMS(in)
			defer os.Remove(out)
			pp := newProvisioningProfile(out)
			info = ProvisioningProfileInfo{Pp: *pp, Name: in}

			enc := gob.NewEncoder(&buffer)
			if err = enc.Encode(info); err != nil {
				logger.Fatalf("error: %v", err)
			}
			if err = db.Put([]byte(name), buffer.Bytes(), nil); err != nil {
				logger.Fatalf("error: %v", err)
			}
		}()
		count++
	}

	var infos []*ProvisioningProfileInfo

	for i := 0; i < count; i++ {
		info := <-c
		if info == nil {
			continue
		}

		if team != "" && team != info.Pp.TeamID() {
			continue
		}

		if pattern != "" {
			if matched, err := regexp.MatchString(pattern, info.Pp.Name); err != nil || !matched {
				continue
			}
		}
		infos = append(infos, info)
	}

	return infos
}
<commit_msg>go: suppress lint warnings<commit_after>package relfile

import (
	"bytes"
	"encoding/gob"
	"github.com/DHowett/go-plist"
	"github.com/syndtr/goleveldb/leveldb"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"time"
)

const (
	// ProvisioningTypeAdHoc : Adhoc type
	ProvisioningTypeAdHoc = "ad-hoc"
	// ProvisioningTypeAppStore : AppStore type
	ProvisioningTypeAppStore = "app-store"
	// ProvisioningTypeDevelopment : Development type
	ProvisioningTypeDevelopment = "development"
	// ProvisioningTypeEnterprise : Enterprise type
	ProvisioningTypeEnterprise = "enterprise"

	// CertificateTypeDeveloper : development
	CertificateTypeDeveloper = "iPhone Developer"
	// CertificateTypeDistribution : distribution
	CertificateTypeDistribution = "iPhone Distribution"
)

// ProvisioningProfile struct
type ProvisioningProfile struct {
	AppIDName             string       `plist:"AppIDName"`
	CreationDate          time.Time    `plist:"CreationDate"`
	DeveloperCertificates [][]byte     `plist:"DeveloperCertificates"`
	Entitlements          Entitlements `plist:"Entitlements"`
	Name                  string       `plist:"Name"`
	ProvisionedDevices    []string     `plist:"ProvisionedDevices,omitempty"`
	ProvisionsAllDevices  bool         `plist:"ProvisionsAllDevices,omitempty"`
	TeamIdentifiers       []string     `plist:"TeamIdentifier"`
	TeamName              string       `plist:"TeamName"`
	TimeToLive            int          `plist:"TimeToLive"`
	UUID                  string       `plist:"UUID"`
	Version               int          `plist:"Version"`
}

// Entitlements : Nested struct is not working in go-plist...
type Entitlements struct {
	GetTaskAllow            bool   `plist:"get-task-allow"`
	ApplicationIdentifier   string `plist:"application-identifier"`
	DeveloperTeamIdentifier string `plist:"com.apple.developer.team-identifier"`
}

// TeamID :
func (p ProvisioningProfile) TeamID() (s string) {
	return p.Entitlements.DeveloperTeamIdentifier
}

// AppID :
func (p ProvisioningProfile) AppID() (s string) {
	return strings.TrimLeft(p.Entitlements.ApplicationIdentifier, p.TeamID()+".")
}

// CertificateType :
func (p ProvisioningProfile) CertificateType() string {
	if p.ProvisioningType() == ProvisioningTypeDevelopment {
		return CertificateTypeDeveloper
	}
	return CertificateTypeDistribution
}

// ProvisioningType :
func (p ProvisioningProfile) ProvisioningType() string {
	if p.ProvisionsAllDevices {
		return ProvisioningTypeEnterprise
	}

	if p.ProvisionedDevices == nil {
		return ProvisioningTypeAppStore
	}

	if p.Entitlements.GetTaskAllow {
		return ProvisioningTypeDevelopment
	}
	return ProvisioningTypeAdHoc
}

// GetIdentity :
func (p ProvisioningProfile) GetIdentity() []string {
	certs := []string{}
	return certs

}

func decodeCMS(path string) string {
	out, err := ioutil.TempFile("", "relax/provisioning_profile")

	if err != nil {
		logger.Fatalf("error: %v", err)
	}

	if _, err = exec.Command("/usr/bin/security", "cms", "-D", "-i", path, "-o", out.Name()).Output(); err != nil {
		logger.Fatalf("error: %v", err)
	}

	return out.Name()
}

func newProvisioningProfile(path string) *ProvisioningProfile {
	file, err := os.Open(path)

	if err != nil {
		logger.Fatalf("error: %v", err)
	}

	decoder := plist.NewDecoder(file)

	pp := ProvisioningProfile{}
	if err := decoder.Decode(&pp); err != nil {
		logger.Fatalf("error: %v", err)
	}

	return &pp
}

// NewEntitlements :
func NewEntitlements(m map[string]interface{}) Entitlements {
	return Entitlements{
		GetTaskAllow:            m["get-task-allow"].(bool),
		ApplicationIdentifier:   m["application-identifier"].(string),
		DeveloperTeamIdentifier: m["com.apple.developer.team-identifier"].(string),
	}
}

// ProvisioningProfileInfo :
type ProvisioningProfileInfo struct {
	Pp   ProvisioningProfile
	Name string
}

func getCacheDBName() string {
	return os.TempDir() + "relax/cachedb"
}

func getCacheDB() (*leveldb.DB, error) {
	path := getCacheDBName()
	db, err := leveldb.OpenFile(path, nil)
	if err != nil {
		// Prevent error<file missing [file=MANIFEST-000000]>
		ClearCache()
		return leveldb.OpenFile(path, nil)
	}
	return db, err
}

// ClearCache :
func ClearCache() error {
	return os.RemoveAll(getCacheDBName())
}

var ppRoot string = os.Getenv("HOME") + "/Library/MobileDevice/Provisioning Profiles"

// FindProvisioningProfile ;
func FindProvisioningProfile(pattern string, team string) []*ProvisioningProfileInfo {
	db, err := getCacheDB()
	if err != nil {
		logger.Fatalf("error: %v", err)
	}
	defer db.Close()

	files, err := ioutil.ReadDir(ppRoot)
	if err != nil {
		logger.Fatalf("error: %v", err)
	}

	s := make(chan bool, 32)
	c := make(chan *ProvisioningProfileInfo, len(files))
	count := 0
	for _, file := range files {
		s <- true

		name := file.Name()

		if false == strings.HasSuffix(name, "mobileprovision") {
			continue
		}

		go func() {
			defer func() { <-s }()
			var (
				info   ProvisioningProfileInfo
				buffer bytes.Buffer
			)
			defer func() { c <- &info }()

			if buf, err := db.Get([]byte(name), nil); err == nil {
				dec := gob.NewDecoder(bytes.NewBuffer(buf))
				if err = dec.Decode(&info); err == nil {
					if _, err = os.Stat(info.Name); err == nil {
						return
					}
				}
			}

			in := ppRoot + "/" + name
			out := decodeCMS(in)
			defer os.Remove(out)
			pp := newProvisioningProfile(out)
			info = ProvisioningProfileInfo{Pp: *pp, Name: in}

			enc := gob.NewEncoder(&buffer)
			if err = enc.Encode(info); err != nil {
				logger.Fatalf("error: %v", err)
			}
			if err = db.Put([]byte(name), buffer.Bytes(), nil); err != nil {
				logger.Fatalf("error: %v", err)
			}
		}()
		count++
	}

	var infos []*ProvisioningProfileInfo

	for i := 0; i < count; i++ {
		info := <-c
		if info == nil {
			continue
		}

		if team != "" && team != info.Pp.TeamID() {
			continue
		}

		if pattern != "" {
			if matched, err := regexp.MatchString(pattern, info.Pp.Name); err != nil || !matched {
				continue
			}
		}
		infos = append(infos, info)
	}

	return infos
}
<|endoftext|>


<commit_before>package rootd

import (
	"context"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/spf13/cobra"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	empty "google.golang.org/protobuf/types/known/emptypb"

	"github.com/datawire/dlib/derror"
	"github.com/datawire/dlib/dgroup"
	"github.com/datawire/dlib/dhttp"
	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/rpc/v2/common"
	rpc "github.com/telepresenceio/telepresence/rpc/v2/daemon"
	"github.com/telepresenceio/telepresence/rpc/v2/manager"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/client/logging"
	"github.com/telepresenceio/telepresence/v2/pkg/client/scout"
	"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
	"github.com/telepresenceio/telepresence/v2/pkg/log"
	"github.com/telepresenceio/telepresence/v2/pkg/proc"
)

const ProcessName = "daemon"
const titleName = "Daemon"

var help = `The Telepresence ` + titleName + ` is a long-lived background component that manages
connections and network state.

Launch the Telepresence ` + titleName + `:
    sudo telepresence service

Examine the ` + titleName + `'s log output in
    ` + filepath.Join(func() string { dir, _ := filelocation.AppUserLogDir(context.Background()); return dir }(), ProcessName+".log") + `
to troubleshoot problems.
`

// service represents the state of the Telepresence Daemon
type service struct {
	rpc.UnsafeDaemonServer
	quit          context.CancelFunc
	connectCh     chan *rpc.OutboundInfo
	connectErrCh  chan error
	sessionLock   sync.Mutex
	session       *session
	timedLogLevel log.TimedLevel

	scout *scout.Reporter
}

// Command returns the telepresence sub-command "daemon-foreground"
func Command() *cobra.Command {
	return &cobra.Command{
		Use:    ProcessName + "-foreground <logging dir> <config dir>",
		Short:  "Launch Telepresence " + titleName + " in the foreground (debug)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		Long:   help,
		RunE: func(cmd *cobra.Command, args []string) error {
			return run(cmd.Context(), args[0], args[1])
		},
	}
}

func (d *service) Version(_ context.Context, _ *empty.Empty) (*common.VersionInfo, error) {
	return &common.VersionInfo{
		ApiVersion: client.APIVersion,
		Version:    client.Version(),
	}, nil
}

func (d *service) Status(_ context.Context, _ *empty.Empty) (*rpc.DaemonStatus, error) {
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	r := &rpc.DaemonStatus{}
	if d.session != nil {
		r.OutboundConfig = d.session.getInfo()
	}
	return r, nil
}

func (d *service) Quit(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
	dlog.Debug(ctx, "Received gRPC Quit")
	d.quit()
	return &empty.Empty{}, nil
}

func (d *service) SetDnsSearchPath(ctx context.Context, paths *rpc.Paths) (*empty.Empty, error) {
	session, err := d.currentSession()
	if err != nil {
		return nil, err
	}
	session.SetSearchPath(ctx, paths.Paths, paths.Namespaces)
	return &empty.Empty{}, nil
}

func (d *service) Connect(ctx context.Context, info *rpc.OutboundInfo) (*empty.Empty, error) {
	dlog.Debug(ctx, "Received gRPC Connect")
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	if d.session != nil {
		return nil, status.Error(codes.AlreadyExists, "an active session exists")
	}
	select {
	case <-ctx.Done():
		return &empty.Empty{}, nil
	case d.connectCh <- info:
	}
	select {
	case <-ctx.Done():
	case err := <-d.connectErrCh:
		if err != nil {
			return nil, status.Error(codes.Internal, err.Error())
		}
	}
	return &empty.Empty{}, nil
}

func (d *service) Disconnect(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
	dlog.Debug(ctx, "Received gRPC Disconnect")
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	if d.session != nil {
		d.session.cancel()
	}
	return &empty.Empty{}, nil
}

func (d *service) currentSession() (*session, error) {
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	if d.session == nil {
		return nil, status.Error(codes.Unavailable, "no active session")
	}
	return d.session, nil
}

func (d *service) GetClusterSubnets(ctx context.Context, _ *empty.Empty) (*rpc.ClusterSubnets, error) {
	session, err := d.currentSession()
	if err != nil {
		return nil, err
	}

	// The manager can sometimes send the different subnets in different Sends, but after 5 seconds of listening to it
	// we should expect to have everything
	tCtx, tCancel := context.WithTimeout(ctx, 5*time.Second)
	defer tCancel()
	infoStream, err := session.managerClient.WatchClusterInfo(tCtx, session.session)
	if err != nil {
		return nil, err
	}
	podSubnets := []*manager.IPNet{}
	svcSubnets := []*manager.IPNet{}
	for {
		mgrInfo, err := infoStream.Recv()
		if err != nil {
			if tCtx.Err() == nil && !errors.Is(err, io.EOF) {
				return nil, err
			}
			break
		}
		if mgrInfo.ServiceSubnet != nil {
			svcSubnets = append(svcSubnets, mgrInfo.ServiceSubnet)
		}
		podSubnets = append(podSubnets, mgrInfo.PodSubnets...)
	}
	return &rpc.ClusterSubnets{PodSubnets: podSubnets, SvcSubnets: svcSubnets}, nil
}

func (d *service) SetLogLevel(ctx context.Context, request *manager.LogLevelRequest) (*empty.Empty, error) {
	duration := time.Duration(0)
	if request.Duration != nil {
		duration = request.Duration.AsDuration()
	}
	return &empty.Empty{}, logging.SetAndStoreTimedLevel(ctx, d.timedLogLevel, request.LogLevel, duration, ProcessName)
}

func (d *service) configReload(c context.Context) error {
	return client.Watch(c, func(c context.Context) error {
		return logging.ReloadDaemonConfig(c, true)
	})
}

// manageSessions is the counterpart to the Connect method. It reads the connectCh, creates
// a session and writes a reply to the connectErrCh. The session is then started if it was
// successfully created.
func (d *service) manageSessions(c context.Context) error {
	// The d.quit is called when we receive a Quit. Since it
	// terminates this function, it terminates the whole process.
	c, d.quit = context.WithCancel(c)
	for {
		// Wait for a connection request
		var oi *rpc.OutboundInfo
		select {
		case <-c.Done():
			return nil
		case oi = <-d.connectCh:
		}

		// Respond by setting the session and returning the error (or nil
		// if everything is ok)
		var err error
		d.session, err = newSession(c, d.scout, oi)
		select {
		case <-c.Done():
			return nil
		case d.connectErrCh <- err:
		}
		if err != nil {
			continue
		}

		// Run the session synchronously and ensure that it is cleaned
		// up properly when the context is cancelled
		func(c context.Context) {
			defer func() {
				d.sessionLock.Lock()
				d.session = nil
				d.sessionLock.Unlock()
			}()

			// The d.session.cancel is called from Disconnect
			c, d.session.cancel = context.WithCancel(c)
			if err := d.session.run(c); err != nil {
				dlog.Error(c, err)
			}
		}(c)
	}
}

func (d *service) serveGrpc(c context.Context, l net.Listener) error {
	defer func() {
		// Error recovery.
		if perr := derror.PanicToError(recover()); perr != nil {
			dlog.Error(c, perr)
		}
	}()

	var opts []grpc.ServerOption
	cfg := client.GetConfig(c)
	if !cfg.Grpc.MaxReceiveSize.IsZero() {
		if mz, ok := cfg.Grpc.MaxReceiveSize.AsInt64(); ok {
			opts = append(opts, grpc.MaxRecvMsgSize(int(mz)))
		}
	}
	svc := grpc.NewServer(opts...)
	rpc.RegisterDaemonServer(svc, d)

	sc := &dhttp.ServerConfig{
		Handler: svc,
	}
	dlog.Info(c, "gRPC server started")
	err := sc.Serve(c, l)
	if err != nil {
		dlog.Errorf(c, "gRPC server ended with: %v", err)
	} else {
		dlog.Debug(c, "gRPC server ended")
	}
	return err
}

// run is the main function when executing as the daemon
func run(c context.Context, loggingDir, configDir string) error {
	if !proc.IsAdmin() {
		return fmt.Errorf("telepresence %s must run with elevated privileges", ProcessName)
	}

	// seed random generator (used when shuffling IPs)
	rand.Seed(time.Now().UnixNano())

	// Spoof the AppUserLogDir and AppUserConfigDir so that they return the original user's
	// directories rather than directories for the root user.
	c = filelocation.WithAppUserLogDir(c, loggingDir)
	c = filelocation.WithAppUserConfigDir(c, configDir)

	cfg, err := client.LoadConfig(c)
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}
	c = client.WithConfig(c, cfg)

	c = dgroup.WithGoroutineName(c, "/"+ProcessName)
	c, err = logging.InitContext(c, ProcessName, logging.RotateDaily)
	if err != nil {
		return err
	}

	dlog.Info(c, "---")
	dlog.Infof(c, "Telepresence %s %s starting...", ProcessName, client.DisplayVersion())
	dlog.Infof(c, "PID is %d", os.Getpid())
	dlog.Info(c, "")

	// Listen on domain unix domain socket or windows named pipe. The listener must be opened
	// before other tasks because the CLI client will only wait for a short period of time for
	// the socket/pipe to appear before it gives up.
	grpcListener, err := client.ListenSocket(c, ProcessName, client.DaemonSocketName)
	if err != nil {
		return err
	}
	defer func() {
		_ = client.RemoveSocket(grpcListener)
	}()
	dlog.Debug(c, "Listener opened")

	d := &service{
		scout:         scout.NewReporter(c, "daemon"),
		timedLogLevel: log.NewTimedLevel(cfg.LogLevels.RootDaemon.String(), log.SetLevel),
		connectCh:     make(chan *rpc.OutboundInfo),
		connectErrCh:  make(chan error),
	}
	if err = logging.LoadTimedLevelFromCache(c, d.timedLogLevel, ProcessName); err != nil {
		return err
	}

	g := dgroup.NewGroup(c, dgroup.GroupConfig{
		SoftShutdownTimeout:  2 * time.Second,
		EnableSignalHandling: true,
		ShutdownOnNonError:   true,
	})

	// Add a reload function that triggers on create and write of the config.yml file.
	g.Go("config-reload", d.configReload)
	g.Go("session", d.manageSessions)
	g.Go("server-grpc", func(c context.Context) error { return d.serveGrpc(c, grpcListener) })
	g.Go("metriton", d.scout.Run)
	err = g.Wait()
	if err != nil {
		dlog.Error(c, err)
	}
	return err
}
<commit_msg>A Cancelled context should render a Canceled grpc status<commit_after>package rootd

import (
	"context"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/spf13/cobra"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	empty "google.golang.org/protobuf/types/known/emptypb"

	"github.com/datawire/dlib/derror"
	"github.com/datawire/dlib/dgroup"
	"github.com/datawire/dlib/dhttp"
	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/rpc/v2/common"
	rpc "github.com/telepresenceio/telepresence/rpc/v2/daemon"
	"github.com/telepresenceio/telepresence/rpc/v2/manager"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/client/logging"
	"github.com/telepresenceio/telepresence/v2/pkg/client/scout"
	"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
	"github.com/telepresenceio/telepresence/v2/pkg/log"
	"github.com/telepresenceio/telepresence/v2/pkg/proc"
)

const ProcessName = "daemon"
const titleName = "Daemon"

var help = `The Telepresence ` + titleName + ` is a long-lived background component that manages
connections and network state.

Launch the Telepresence ` + titleName + `:
    sudo telepresence service

Examine the ` + titleName + `'s log output in
    ` + filepath.Join(func() string { dir, _ := filelocation.AppUserLogDir(context.Background()); return dir }(), ProcessName+".log") + `
to troubleshoot problems.
`

// service represents the state of the Telepresence Daemon
type service struct {
	rpc.UnsafeDaemonServer
	quit          context.CancelFunc
	connectCh     chan *rpc.OutboundInfo
	connectErrCh  chan error
	sessionLock   sync.Mutex
	session       *session
	timedLogLevel log.TimedLevel

	scout *scout.Reporter
}

// Command returns the telepresence sub-command "daemon-foreground"
func Command() *cobra.Command {
	return &cobra.Command{
		Use:    ProcessName + "-foreground <logging dir> <config dir>",
		Short:  "Launch Telepresence " + titleName + " in the foreground (debug)",
		Args:   cobra.ExactArgs(2),
		Hidden: true,
		Long:   help,
		RunE: func(cmd *cobra.Command, args []string) error {
			return run(cmd.Context(), args[0], args[1])
		},
	}
}

func (d *service) Version(_ context.Context, _ *empty.Empty) (*common.VersionInfo, error) {
	return &common.VersionInfo{
		ApiVersion: client.APIVersion,
		Version:    client.Version(),
	}, nil
}

func (d *service) Status(_ context.Context, _ *empty.Empty) (*rpc.DaemonStatus, error) {
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	r := &rpc.DaemonStatus{}
	if d.session != nil {
		r.OutboundConfig = d.session.getInfo()
	}
	return r, nil
}

func (d *service) Quit(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
	dlog.Debug(ctx, "Received gRPC Quit")
	d.quit()
	return &empty.Empty{}, nil
}

func (d *service) SetDnsSearchPath(ctx context.Context, paths *rpc.Paths) (*empty.Empty, error) {
	session, err := d.currentSession()
	if err != nil {
		return nil, err
	}
	session.SetSearchPath(ctx, paths.Paths, paths.Namespaces)
	return &empty.Empty{}, nil
}

func (d *service) Connect(ctx context.Context, info *rpc.OutboundInfo) (*empty.Empty, error) {
	dlog.Debug(ctx, "Received gRPC Connect")
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	if d.session != nil {
		return nil, status.Error(codes.AlreadyExists, "an active session exists")
	}
	select {
	case <-ctx.Done():
		return nil, status.Error(codes.Canceled, ctx.Err().Error())
	case d.connectCh <- info:
	}
	select {
	case <-ctx.Done():
		return nil, status.Error(codes.Canceled, ctx.Err().Error())
	case err := <-d.connectErrCh:
		if err != nil {
			return nil, status.Error(codes.Internal, err.Error())
		}
	}
	return &empty.Empty{}, nil
}

func (d *service) Disconnect(ctx context.Context, _ *empty.Empty) (*empty.Empty, error) {
	dlog.Debug(ctx, "Received gRPC Disconnect")
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	if d.session != nil {
		d.session.cancel()
	}
	return &empty.Empty{}, nil
}

func (d *service) currentSession() (*session, error) {
	d.sessionLock.Lock()
	defer d.sessionLock.Unlock()
	if d.session == nil {
		return nil, status.Error(codes.Unavailable, "no active session")
	}
	return d.session, nil
}

func (d *service) GetClusterSubnets(ctx context.Context, _ *empty.Empty) (*rpc.ClusterSubnets, error) {
	session, err := d.currentSession()
	if err != nil {
		return nil, err
	}

	// The manager can sometimes send the different subnets in different Sends, but after 5 seconds of listening to it
	// we should expect to have everything
	tCtx, tCancel := context.WithTimeout(ctx, 5*time.Second)
	defer tCancel()
	infoStream, err := session.managerClient.WatchClusterInfo(tCtx, session.session)
	if err != nil {
		return nil, err
	}
	podSubnets := []*manager.IPNet{}
	svcSubnets := []*manager.IPNet{}
	for {
		mgrInfo, err := infoStream.Recv()
		if err != nil {
			if tCtx.Err() == nil && !errors.Is(err, io.EOF) {
				return nil, err
			}
			break
		}
		if mgrInfo.ServiceSubnet != nil {
			svcSubnets = append(svcSubnets, mgrInfo.ServiceSubnet)
		}
		podSubnets = append(podSubnets, mgrInfo.PodSubnets...)
	}
	return &rpc.ClusterSubnets{PodSubnets: podSubnets, SvcSubnets: svcSubnets}, nil
}

func (d *service) SetLogLevel(ctx context.Context, request *manager.LogLevelRequest) (*empty.Empty, error) {
	duration := time.Duration(0)
	if request.Duration != nil {
		duration = request.Duration.AsDuration()
	}
	return &empty.Empty{}, logging.SetAndStoreTimedLevel(ctx, d.timedLogLevel, request.LogLevel, duration, ProcessName)
}

func (d *service) configReload(c context.Context) error {
	return client.Watch(c, func(c context.Context) error {
		return logging.ReloadDaemonConfig(c, true)
	})
}

// manageSessions is the counterpart to the Connect method. It reads the connectCh, creates
// a session and writes a reply to the connectErrCh. The session is then started if it was
// successfully created.
func (d *service) manageSessions(c context.Context) error {
	// The d.quit is called when we receive a Quit. Since it
	// terminates this function, it terminates the whole process.
	c, d.quit = context.WithCancel(c)
	for {
		// Wait for a connection request
		var oi *rpc.OutboundInfo
		select {
		case <-c.Done():
			return nil
		case oi = <-d.connectCh:
		}

		// Respond by setting the session and returning the error (or nil
		// if everything is ok)
		var err error
		d.session, err = newSession(c, d.scout, oi)
		select {
		case <-c.Done():
			return nil
		case d.connectErrCh <- err:
		}
		if err != nil {
			continue
		}

		// Run the session synchronously and ensure that it is cleaned
		// up properly when the context is cancelled
		func(c context.Context) {
			defer func() {
				d.sessionLock.Lock()
				d.session = nil
				d.sessionLock.Unlock()
			}()

			// The d.session.cancel is called from Disconnect
			c, d.session.cancel = context.WithCancel(c)
			if err := d.session.run(c); err != nil {
				dlog.Error(c, err)
			}
		}(c)
	}
}

func (d *service) serveGrpc(c context.Context, l net.Listener) error {
	defer func() {
		// Error recovery.
		if perr := derror.PanicToError(recover()); perr != nil {
			dlog.Error(c, perr)
		}
	}()

	var opts []grpc.ServerOption
	cfg := client.GetConfig(c)
	if !cfg.Grpc.MaxReceiveSize.IsZero() {
		if mz, ok := cfg.Grpc.MaxReceiveSize.AsInt64(); ok {
			opts = append(opts, grpc.MaxRecvMsgSize(int(mz)))
		}
	}
	svc := grpc.NewServer(opts...)
	rpc.RegisterDaemonServer(svc, d)

	sc := &dhttp.ServerConfig{
		Handler: svc,
	}
	dlog.Info(c, "gRPC server started")
	err := sc.Serve(c, l)
	if err != nil {
		dlog.Errorf(c, "gRPC server ended with: %v", err)
	} else {
		dlog.Debug(c, "gRPC server ended")
	}
	return err
}

// run is the main function when executing as the daemon
func run(c context.Context, loggingDir, configDir string) error {
	if !proc.IsAdmin() {
		return fmt.Errorf("telepresence %s must run with elevated privileges", ProcessName)
	}

	// seed random generator (used when shuffling IPs)
	rand.Seed(time.Now().UnixNano())

	// Spoof the AppUserLogDir and AppUserConfigDir so that they return the original user's
	// directories rather than directories for the root user.
	c = filelocation.WithAppUserLogDir(c, loggingDir)
	c = filelocation.WithAppUserConfigDir(c, configDir)

	cfg, err := client.LoadConfig(c)
	if err != nil {
		return fmt.Errorf("failed to load config: %w", err)
	}
	c = client.WithConfig(c, cfg)

	c = dgroup.WithGoroutineName(c, "/"+ProcessName)
	c, err = logging.InitContext(c, ProcessName, logging.RotateDaily)
	if err != nil {
		return err
	}

	dlog.Info(c, "---")
	dlog.Infof(c, "Telepresence %s %s starting...", ProcessName, client.DisplayVersion())
	dlog.Infof(c, "PID is %d", os.Getpid())
	dlog.Info(c, "")

	// Listen on domain unix domain socket or windows named pipe. The listener must be opened
	// before other tasks because the CLI client will only wait for a short period of time for
	// the socket/pipe to appear before it gives up.
	grpcListener, err := client.ListenSocket(c, ProcessName, client.DaemonSocketName)
	if err != nil {
		return err
	}
	defer func() {
		_ = client.RemoveSocket(grpcListener)
	}()
	dlog.Debug(c, "Listener opened")

	d := &service{
		scout:         scout.NewReporter(c, "daemon"),
		timedLogLevel: log.NewTimedLevel(cfg.LogLevels.RootDaemon.String(), log.SetLevel),
		connectCh:     make(chan *rpc.OutboundInfo),
		connectErrCh:  make(chan error),
	}
	if err = logging.LoadTimedLevelFromCache(c, d.timedLogLevel, ProcessName); err != nil {
		return err
	}

	g := dgroup.NewGroup(c, dgroup.GroupConfig{
		SoftShutdownTimeout:  2 * time.Second,
		EnableSignalHandling: true,
		ShutdownOnNonError:   true,
	})

	// Add a reload function that triggers on create and write of the config.yml file.
	g.Go("config-reload", d.configReload)
	g.Go("session", d.manageSessions)
	g.Go("server-grpc", func(c context.Context) error { return d.serveGrpc(c, grpcListener) })
	g.Go("metriton", d.scout.Run)
	err = g.Wait()
	if err != nil {
		dlog.Error(c, err)
	}
	return err
}
<|endoftext|>


<commit_before>package datasource

import (
	"fmt"
	"strconv"

	"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
	"github.com/alexanderzobnin/grafana-zabbix/pkg/timeseries"
)

const RANGE_VARIABLE_VALUE = "range_series"

var (
	errFunctionNotSupported = func(name string) error {
		return fmt.Errorf("function not supported: %s", name)
	}
	errParsingFunctionParam = func(err error) error {
		return fmt.Errorf("failed to parse function param: %s", err)
	}
)

func MustString(p QueryFunctionParam) (string, error) {
	if pStr, ok := p.(string); ok {
		return pStr, nil
	}
	return "", fmt.Errorf("failed to convert value to string: %v", p)
}

func MustFloat64(p QueryFunctionParam) (float64, error) {
	if pFloat, ok := p.(float64); ok {
		return pFloat, nil
	} else if pStr, ok := p.(string); ok {
		if pFloat, err := strconv.ParseFloat(pStr, 64); err == nil {
			return pFloat, nil
		}
	}
	return 0, fmt.Errorf("failed to convert value to float: %v", p)
}

type DataProcessingFunc = func(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error)

type AggDataProcessingFunc = func(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error)

var seriesFuncMap map[string]DataProcessingFunc

var aggFuncMap map[string]AggDataProcessingFunc

var frontendFuncMap map[string]bool

func init() {
	seriesFuncMap = map[string]DataProcessingFunc{
		"groupBy": applyGroupBy,
		"scale":   applyScale,
		"offset":  applyOffset,
	}

	aggFuncMap = map[string]AggDataProcessingFunc{
		"aggregateBy":   applyAggregateBy,
		"sumSeries":     applySumSeries,
		"percentileAgg": applyPercentileAgg,
	}

	// Functions processing on the frontend
	frontendFuncMap = map[string]bool{
		"setAlias":        true,
		"replaceAlias":    true,
		"setAliasByRegex": true,
	}
}

func applyFunctions(series []*timeseries.TimeSeriesData, functions []QueryFunction) ([]*timeseries.TimeSeriesData, error) {
	for _, f := range functions {
		if applyFunc, ok := seriesFuncMap[f.Def.Name]; ok {
			for _, s := range series {
				result, err := applyFunc(s.TS, f.Params...)
				if err != nil {
					return nil, err
				}
				s.TS = result
			}
		} else if applyAggFunc, ok := aggFuncMap[f.Def.Name]; ok {
			result, err := applyAggFunc(series, f.Params...)
			if err != nil {
				return nil, err
			}
			series = result
		} else if _, ok := frontendFuncMap[f.Def.Name]; ok {
			continue
		} else {
			err := errFunctionNotSupported(f.Def.Name)
			return series, err
		}
	}
	return series, nil
}

func applyGroupBy(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	pInterval, err := MustString(params[0])
	pAgg, err := MustString(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := getAggFunc(pAgg)
	if pInterval == RANGE_VARIABLE_VALUE {
		s := series.GroupByRange(aggFunc)
		return s, nil
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	s := series.GroupBy(interval, aggFunc)
	return s, nil
}

func applyScale(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	pFactor, err := MustString(params[0])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}
	factor, err := strconv.ParseFloat(pFactor, 64)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	transformFunc := timeseries.TransformScale(factor)
	return series.Transform(transformFunc), nil
}

func applyOffset(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	offset, err := MustFloat64(params[0])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	transformFunc := timeseries.TransformOffset(offset)
	return series.Transform(transformFunc), nil
}

func applyAggregateBy(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) {
	pInterval, err := MustString(params[0])
	pAgg, err := MustString(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := getAggFunc(pAgg)
	aggregatedSeries := timeseries.AggregateBy(series, interval, aggFunc)
	aggregatedSeries.Meta.Name = fmt.Sprintf("aggregateBy(%s, %s)", pInterval, pAgg)

	return []*timeseries.TimeSeriesData{aggregatedSeries}, nil
}

func applySumSeries(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) {
	sum := timeseries.SumSeries(series)
	sum.Meta.Name = "sumSeries()"
	return []*timeseries.TimeSeriesData{sum}, nil
}

func applyPercentileAgg(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) {
	pInterval, err := MustString(params[0])
	percentile, err := MustFloat64(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := timeseries.AggPercentile(percentile)
	aggregatedSeries := timeseries.AggregateBy(series, interval, aggFunc)
	aggregatedSeries.Meta.Name = fmt.Sprintf("percentileAgg(%s, %v)", pInterval, percentile)

	return []*timeseries.TimeSeriesData{aggregatedSeries}, nil
}

func getAggFunc(agg string) timeseries.AggFunc {
	switch agg {
	case "avg":
		return timeseries.AggAvg
	case "max":
		return timeseries.AggMax
	case "min":
		return timeseries.AggMin
	case "sum":
		return timeseries.AggSum
	case "median":
		return timeseries.AggMedian
	case "count":
		return timeseries.AggCount
	case "first":
		return timeseries.AggFirst
	case "last":
		return timeseries.AggLast
	default:
		return timeseries.AggAvg
	}
}
<commit_msg>Implement percentile<commit_after>package datasource

import (
	"fmt"
	"strconv"

	"github.com/alexanderzobnin/grafana-zabbix/pkg/gtime"
	"github.com/alexanderzobnin/grafana-zabbix/pkg/timeseries"
)

const RANGE_VARIABLE_VALUE = "range_series"

var (
	errFunctionNotSupported = func(name string) error {
		return fmt.Errorf("function not supported: %s", name)
	}
	errParsingFunctionParam = func(err error) error {
		return fmt.Errorf("failed to parse function param: %s", err)
	}
)

func MustString(p QueryFunctionParam) (string, error) {
	if pStr, ok := p.(string); ok {
		return pStr, nil
	}
	return "", fmt.Errorf("failed to convert value to string: %v", p)
}

func MustFloat64(p QueryFunctionParam) (float64, error) {
	if pFloat, ok := p.(float64); ok {
		return pFloat, nil
	} else if pStr, ok := p.(string); ok {
		if pFloat, err := strconv.ParseFloat(pStr, 64); err == nil {
			return pFloat, nil
		}
	}
	return 0, fmt.Errorf("failed to convert value to float: %v", p)
}

type DataProcessingFunc = func(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error)

type AggDataProcessingFunc = func(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error)

var seriesFuncMap map[string]DataProcessingFunc

var aggFuncMap map[string]AggDataProcessingFunc

var frontendFuncMap map[string]bool

func init() {
	seriesFuncMap = map[string]DataProcessingFunc{
		"groupBy":    applyGroupBy,
		"scale":      applyScale,
		"offset":     applyOffset,
		"percentile": applyPercentile,
	}

	aggFuncMap = map[string]AggDataProcessingFunc{
		"aggregateBy":   applyAggregateBy,
		"sumSeries":     applySumSeries,
		"percentileAgg": applyPercentileAgg,
	}

	// Functions processing on the frontend
	frontendFuncMap = map[string]bool{
		"setAlias":        true,
		"replaceAlias":    true,
		"setAliasByRegex": true,
	}
}

func applyFunctions(series []*timeseries.TimeSeriesData, functions []QueryFunction) ([]*timeseries.TimeSeriesData, error) {
	for _, f := range functions {
		if applyFunc, ok := seriesFuncMap[f.Def.Name]; ok {
			for _, s := range series {
				result, err := applyFunc(s.TS, f.Params...)
				if err != nil {
					return nil, err
				}
				s.TS = result
			}
		} else if applyAggFunc, ok := aggFuncMap[f.Def.Name]; ok {
			result, err := applyAggFunc(series, f.Params...)
			if err != nil {
				return nil, err
			}
			series = result
		} else if _, ok := frontendFuncMap[f.Def.Name]; ok {
			continue
		} else {
			err := errFunctionNotSupported(f.Def.Name)
			return series, err
		}
	}
	return series, nil
}

func applyGroupBy(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	pInterval, err := MustString(params[0])
	pAgg, err := MustString(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := getAggFunc(pAgg)
	if pInterval == RANGE_VARIABLE_VALUE {
		s := series.GroupByRange(aggFunc)
		return s, nil
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	s := series.GroupBy(interval, aggFunc)
	return s, nil
}

func applyPercentile(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	pInterval, err := MustString(params[0])
	percentile, err := MustFloat64(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := timeseries.AggPercentile(percentile)
	if pInterval == RANGE_VARIABLE_VALUE {
		s := series.GroupByRange(aggFunc)
		return s, nil
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	s := series.GroupBy(interval, aggFunc)
	return s, nil
}

func applyScale(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	pFactor, err := MustString(params[0])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}
	factor, err := strconv.ParseFloat(pFactor, 64)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	transformFunc := timeseries.TransformScale(factor)
	return series.Transform(transformFunc), nil
}

func applyOffset(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) {
	offset, err := MustFloat64(params[0])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	transformFunc := timeseries.TransformOffset(offset)
	return series.Transform(transformFunc), nil
}

func applyAggregateBy(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) {
	pInterval, err := MustString(params[0])
	pAgg, err := MustString(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := getAggFunc(pAgg)
	aggregatedSeries := timeseries.AggregateBy(series, interval, aggFunc)
	aggregatedSeries.Meta.Name = fmt.Sprintf("aggregateBy(%s, %s)", pInterval, pAgg)

	return []*timeseries.TimeSeriesData{aggregatedSeries}, nil
}

func applySumSeries(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) {
	sum := timeseries.SumSeries(series)
	sum.Meta.Name = "sumSeries()"
	return []*timeseries.TimeSeriesData{sum}, nil
}

func applyPercentileAgg(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) {
	pInterval, err := MustString(params[0])
	percentile, err := MustFloat64(params[1])
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	interval, err := gtime.ParseInterval(pInterval)
	if err != nil {
		return nil, errParsingFunctionParam(err)
	}

	aggFunc := timeseries.AggPercentile(percentile)
	aggregatedSeries := timeseries.AggregateBy(series, interval, aggFunc)
	aggregatedSeries.Meta.Name = fmt.Sprintf("percentileAgg(%s, %v)", pInterval, percentile)

	return []*timeseries.TimeSeriesData{aggregatedSeries}, nil
}

func getAggFunc(agg string) timeseries.AggFunc {
	switch agg {
	case "avg":
		return timeseries.AggAvg
	case "max":
		return timeseries.AggMax
	case "min":
		return timeseries.AggMin
	case "sum":
		return timeseries.AggSum
	case "median":
		return timeseries.AggMedian
	case "count":
		return timeseries.AggCount
	case "first":
		return timeseries.AggFirst
	case "last":
		return timeseries.AggLast
	default:
		return timeseries.AggAvg
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Camlistore Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package dummy is an example importer for development purposes.
package dummy

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"camlistore.org/pkg/httputil"
	"camlistore.org/pkg/importer"
	"camlistore.org/pkg/jsonconfig"
)

func init() {
	importer.Register("dummy", newFromConfig)
}

func newFromConfig(cfg jsonconfig.Obj, host *importer.Host) (importer.Importer, error) {
	im := &imp{
		url:       cfg.RequiredString("url"),
		username:  cfg.RequiredString("username"),
		authToken: cfg.RequiredString("authToken"),
		host:      host,
	}
	if err := cfg.Validate(); err != nil {
		return nil, err
	}
	return im, nil
}

type imp struct {
	url       string
	username  string
	authToken string
	host      *importer.Host
}

func (im *imp) CanHandleURL(url string) bool { return false }
func (im *imp) ImportURL(url string) error   { panic("unused") }

func (im *imp) Prefix() string {
	return fmt.Sprintf("dummy:%s", im.username)
}

func (im *imp) Run(intr importer.Interrupt) error {
	log.Printf("running dummy importer")
	select {
	case <-time.After(5 * time.Second):
	case <-intr:
		log.Printf("dummy importer interrupted")
	}
	return nil
}

func (im *imp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	httputil.BadRequestError(w, "Unexpected path: %s", r.URL.Path)
}
<commit_msg>Remove some unnecessary code from dummy importer.<commit_after>/*
Copyright 2013 The Camlistore Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package dummy is an example importer for development purposes.
package dummy

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"camlistore.org/pkg/httputil"
	"camlistore.org/pkg/importer"
	"camlistore.org/pkg/jsonconfig"
)

func init() {
	importer.Register("dummy", newFromConfig)
}

func newFromConfig(cfg jsonconfig.Obj, host *importer.Host) (importer.Importer, error) {
	im := &imp{
		url:       cfg.RequiredString("url"),
		username:  cfg.RequiredString("username"),
		authToken: cfg.RequiredString("authToken"),
	}
	if err := cfg.Validate(); err != nil {
		return nil, err
	}
	return im, nil
}

type imp struct {
	url       string
	username  string
	authToken string
}

func (im *imp) CanHandleURL(url string) bool { return false }
func (im *imp) ImportURL(url string) error   { panic("unused") }

func (im *imp) Prefix() string {
	return fmt.Sprintf("dummy:%s", im.username)
}

func (im *imp) Run(intr importer.Interrupt) error {
	log.Printf("running dummy importer")
	select {
	case <-time.After(5 * time.Second):
	case <-intr:
		log.Printf("dummy importer interrupted")
	}
	return nil
}

func (im *imp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	httputil.BadRequestError(w, "Unexpected path: %s", r.URL.Path)
}
<|endoftext|>


<commit_before>package release

import (
	"bytes"
	"net/url"
	"testing"
)

func TestNew(t *testing.T) {
	meta := NewMeta([]*Binary{
		{
			Name:     "droot",
			Checksum: "ec9efb6249e0e4797bde75afbfe962e0db81c530b5bb1cfd2cbe0e2fc2c8cf48",
		},
		{
			Name:     "grabeni",
			Checksum: "3e30f16f0ec41ab92ceca57a527efff18b6bacabd12a842afda07b8329e32259",
		},
	})

	u, err := url.Parse("s3://binreptestbucket/github.com/yuuki/tools/20171019204009")
	if err != nil {
		panic(err)
	}
	rel := New(meta, u)

	if rel.Name() != "github.com/yuuki/tools" {
		t.Errorf("release.Name() = %q; want %q", rel.Name(), "github.com/yuuki/tools")
	}
	if rel.Timestamp() != "20171019204009" {
		t.Errorf("release.Name() = %q; want %q", rel.Timestamp(), "20171019204009")
	}
	if rel.Prefix() != "github.com/yuuki/tools/20171019204009" {
		t.Errorf("release.Name() = %q; want %q", rel.Prefix(), "github.com/yuuki/tools/20171019204009")
	}
	if rel.MetaPath() != "github.com/yuuki/tools/20171019204009/meta.yml" {
		t.Errorf("release.Name() = %q; want %q", rel.Prefix(), "github.com/yuuki/tools/20171019204009/meta.yml")
	}
}

func TestReleaseInspect(t *testing.T) {
	meta := NewMeta([]*Binary{
		{
			Name:     "droot",
			Checksum: "ec9efb6249e0e4797bde75afbfe962e0db81c530b5bb1cfd2cbe0e2fc2c8cf48",
		},
		{
			Name:     "grabeni",
			Checksum: "3e30f16f0ec41ab92ceca57a527efff18b6bacabd12a842afda07b8329e32259",
		},
	})

	u, err := url.Parse("s3://binreptestbucket/github.com/yuuki/tools/20171019204009")
	if err != nil {
		panic(err)
	}
	rel := New(meta, u)

	out := new(bytes.Buffer)

	rel.Inspect(out)

	expected := "NAME\tTIMESTAMP\tBINNARY1\tBINNARY2\t\ngithub.com/yuuki/tools\t20171019204009\tdroot/ec9efb6\tgrabeni/3e30f16\t\n"
	if out.String() != expected {
		t.Errorf("got: %q, want: %q", out.String(), expected)
	}
}

func TestParseName(t *testing.T) {
	tests := []struct {
		desc         string
		input        string
		expectedOk   bool
		expectedName string
	}{
		{
			desc:         "ok: 5 depth",
			input:        "github.com/yuuki/droot/20171017152508/droot",
			expectedOk:   true,
			expectedName: "github.com/yuuki/droot",
		},
		{
			desc:         "ok: 4 depth",
			input:        "github.com/yuuki/droot/20171017152508",
			expectedOk:   true,
			expectedName: "github.com/yuuki/droot",
		},
		{
			desc:         "ng: 3 depth",
			input:        "github.com/yuuki/droot",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: 2 depth",
			input:        "github.com/yuuki",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: 1 depth",
			input:        "github.com",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: empty",
			input:        "",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: invalid timestamp",
			input:        "github.com/yuuki/droot/2017/droot",
			expectedOk:   false,
			expectedName: "",
		},
	}
	for _, tt := range tests {
		ok, name := ParseName(tt.input)
		if ok != tt.expectedOk {
			t.Errorf("desc: %s, ParseName should be %v to %q", tt.desc, tt.expectedOk, tt.input)
		}
		if name != tt.expectedName {
			t.Errorf("desc: %s, got: %q, want: %q", tt.desc, name, tt.expectedName)
		}
	}
}
<commit_msg>Fix test failure<commit_after>package release

import (
	"bytes"
	"net/url"
	"testing"
)

func TestNew(t *testing.T) {
	meta := NewMeta([]*Binary{
		{
			Name:     "droot",
			Checksum: "ec9efb6249e0e4797bde75afbfe962e0db81c530b5bb1cfd2cbe0e2fc2c8cf48",
		},
		{
			Name:     "grabeni",
			Checksum: "3e30f16f0ec41ab92ceca57a527efff18b6bacabd12a842afda07b8329e32259",
		},
	})

	u, err := url.Parse("s3://binreptestbucket/github.com/yuuki/tools/20171019204009")
	if err != nil {
		panic(err)
	}
	rel := New(meta, u)

	if rel.Name() != "github.com/yuuki/tools" {
		t.Errorf("release.Name() = %q; want %q", rel.Name(), "github.com/yuuki/tools")
	}
	if rel.Timestamp() != "20171019204009" {
		t.Errorf("release.Name() = %q; want %q", rel.Timestamp(), "20171019204009")
	}
	if rel.Prefix() != "github.com/yuuki/tools/20171019204009" {
		t.Errorf("release.Name() = %q; want %q", rel.Prefix(), "github.com/yuuki/tools/20171019204009")
	}
	if rel.MetaPath() != "github.com/yuuki/tools/20171019204009/meta.yml" {
		t.Errorf("release.Name() = %q; want %q", rel.Prefix(), "github.com/yuuki/tools/20171019204009/meta.yml")
	}
}

func TestReleaseInspect(t *testing.T) {
	meta := NewMeta([]*Binary{
		{
			Name:     "droot",
			Checksum: "ec9efb6249e0e4797bde75afbfe962e0db81c530b5bb1cfd2cbe0e2fc2c8cf48",
			Mode:     0755,
		},
		{
			Name:     "grabeni",
			Checksum: "3e30f16f0ec41ab92ceca57a527efff18b6bacabd12a842afda07b8329e32259",
			Mode:     0755,
		},
	})

	u, err := url.Parse("s3://binreptestbucket/github.com/yuuki/tools/20171019204009")
	if err != nil {
		panic(err)
	}
	rel := New(meta, u)

	out := new(bytes.Buffer)

	rel.Inspect(out)

	expected := "NAME\tTIMESTAMP\tBINNARY1\tBINNARY2\t\ngithub.com/yuuki/tools\t20171019204009\tdroot/-rwxr-xr-x/ec9efb6\tgrabeni/-rwxr-xr-x/3e30f16\t\n"
	if out.String() != expected {
		t.Errorf("got: %q, want: %q", out.String(), expected)
	}
}

func TestParseName(t *testing.T) {
	tests := []struct {
		desc         string
		input        string
		expectedOk   bool
		expectedName string
	}{
		{
			desc:         "ok: 5 depth",
			input:        "github.com/yuuki/droot/20171017152508/droot",
			expectedOk:   true,
			expectedName: "github.com/yuuki/droot",
		},
		{
			desc:         "ok: 4 depth",
			input:        "github.com/yuuki/droot/20171017152508",
			expectedOk:   true,
			expectedName: "github.com/yuuki/droot",
		},
		{
			desc:         "ng: 3 depth",
			input:        "github.com/yuuki/droot",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: 2 depth",
			input:        "github.com/yuuki",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: 1 depth",
			input:        "github.com",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: empty",
			input:        "",
			expectedOk:   false,
			expectedName: "",
		},
		{
			desc:         "ng: invalid timestamp",
			input:        "github.com/yuuki/droot/2017/droot",
			expectedOk:   false,
			expectedName: "",
		},
	}
	for _, tt := range tests {
		ok, name := ParseName(tt.input)
		if ok != tt.expectedOk {
			t.Errorf("desc: %s, ParseName should be %v to %q", tt.desc, tt.expectedOk, tt.input)
		}
		if name != tt.expectedName {
			t.Errorf("desc: %s, got: %q, want: %q", tt.desc, name, tt.expectedName)
		}
	}
}
<|endoftext|>


<commit_before>// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.

package windowsconsole // import "github.com/docker/docker/pkg/term/windows"

import (
	"io/ioutil"
	"os"
	"sync"

	ansiterm "github.com/Azure/go-ansiterm"
	"github.com/sirupsen/logrus"
)

var logger *logrus.Logger
var initOnce sync.Once

func initLogger() {
	initOnce.Do(func() {
		logFile := ioutil.Discard

		if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
			logFile, _ = os.Create("ansiReaderWriter.log")
		}

		logger = &logrus.Logger{
			Out:       logFile,
			Formatter: new(logrus.TextFormatter),
			Level:     logrus.DebugLevel,
		}
	})
}
<commit_msg>pkg/term/windows: add missing build-tag<commit_after>// +build windows
// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.

package windowsconsole // import "github.com/docker/docker/pkg/term/windows"

import (
	"io/ioutil"
	"os"
	"sync"

	ansiterm "github.com/Azure/go-ansiterm"
	"github.com/sirupsen/logrus"
)

var logger *logrus.Logger
var initOnce sync.Once

func initLogger() {
	initOnce.Do(func() {
		logFile := ioutil.Discard

		if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
			logFile, _ = os.Create("ansiReaderWriter.log")
		}

		logger = &logrus.Logger{
			Out:       logFile,
			Formatter: new(logrus.TextFormatter),
			Level:     logrus.DebugLevel,
		}
	})
}
<|endoftext|>


<commit_before>package aws

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
	"testing"
	"text/template"
	"time"

	"github.com/aws/aws-sdk-go/service/budgets"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

func TestAwsBudget_basic(t *testing.T) {
	name := fmt.Sprintf("test-budget-%d", acctest.RandInt())
	configBasicDefaults := budgetTestConfig{
		BudgetName:               name,
		BudgetType:               "COST",
		LimitAmount:              "100",
		LimitUnit:                "USD",
		FilterKey:                "AZ",
		FilterValue:              "us-east-1",
		IncludeCredit:            "true",
		IncludeOtherSubscription: "true",
		IncludeRecurring:         "true",
		IncludeRefund:            "true",
		IncludeSubscription:      "true",
		IncludeSupport:           "true",
		IncludeTax:               "true",
		IncludeUpfront:           "true",
		UseBlended:               "false",
		TimeUnit:                 "MONTHLY",
		TimePeriodStart:          "2017-01-01_12:00",
		TimePeriodEnd:            "2087-06-15_12:00",
	}
	configBasicUpdate := budgetTestConfig{
		BudgetName:               name,
		BudgetType:               "COST",
		LimitAmount:              "500",
		LimitUnit:                "USD",
		FilterKey:                "AZ",
		FilterValue:              "us-east-2",
		IncludeCredit:            "true",
		IncludeOtherSubscription: "true",
		IncludeRecurring:         "true",
		IncludeRefund:            "true",
		IncludeSubscription:      "false",
		IncludeSupport:           "true",
		IncludeTax:               "false",
		IncludeUpfront:           "true",
		UseBlended:               "true",
		TimeUnit:                 "MONTHLY",
		TimePeriodStart:          "2017-01-01_12:00",
		TimePeriodEnd:            "2018-01-01_12:00",
	}

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return testCheckBudgetDestroy(name, testAccProvider)
		},
		Steps: []resource.TestStep{
			{
				Config: testBudgetHCLBasicUseDefaults(configBasicDefaults),
				Check:  newComposedBudgetTestCheck(configBasicDefaults, testAccProvider, "name"),
			},

			{
				Config: testBudgetHCLBasic(configBasicUpdate),
				Check:  newComposedBudgetTestCheck(configBasicUpdate, testAccProvider, "name"),
			},
		},
	})
}

func TestAwsBudget_prefix(t *testing.T) {
	name := "test-budget-"
	configBasicDefaults := budgetTestConfig{
		BudgetName:               name,
		BudgetType:               "COST",
		LimitAmount:              "100",
		LimitUnit:                "USD",
		FilterKey:                "AZ",
		FilterValue:              "us-east-1",
		IncludeCredit:            "true",
		IncludeOtherSubscription: "true",
		IncludeRecurring:         "true",
		IncludeRefund:            "true",
		IncludeSubscription:      "true",
		IncludeSupport:           "true",
		IncludeTax:               "true",
		IncludeUpfront:           "true",
		UseBlended:               "false",
		TimeUnit:                 "MONTHLY",
		TimePeriodStart:          "2017-01-01_12:00",
		TimePeriodEnd:            "2087-06-15_12:00",
	}

	configBasicUpdate := budgetTestConfig{
		BudgetName:               name,
		BudgetType:               "COST",
		LimitAmount:              "500",
		LimitUnit:                "USD",
		FilterKey:                "AZ",
		FilterValue:              "us-east-2",
		IncludeCredit:            "true",
		IncludeOtherSubscription: "true",
		IncludeRecurring:         "true",
		IncludeRefund:            "true",
		IncludeSubscription:      "false",
		IncludeSupport:           "true",
		IncludeTax:               "false",
		IncludeUpfront:           "true",
		UseBlended:               "true",
		TimeUnit:                 "MONTHLY",
		TimePeriodStart:          "2017-01-01_12:00",
		TimePeriodEnd:            "2018-01-01_12:00",
	}

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return testCheckBudgetDestroy(name, testAccProvider)
		},
		Steps: []resource.TestStep{
			{
				Config: testBudgetHCLPrefixUseDefaults(configBasicDefaults),
				Check:  newComposedBudgetTestCheck(configBasicDefaults, testAccProvider, "name_prefix"),
			},

			{
				Config: testBudgetHCLPrefix(configBasicUpdate),
				Check:  newComposedBudgetTestCheck(configBasicUpdate, testAccProvider, "name_prefix"),
			},
		},
	})
}

func newComposedBudgetTestCheck(config budgetTestConfig, provider *schema.Provider, nameField string) resource.TestCheckFunc {
	return resource.ComposeTestCheckFunc(
		resource.TestMatchResourceAttr("aws_budget.foo", nameField, regexp.MustCompile(config.BudgetName)),
		resource.TestCheckResourceAttr("aws_budget.foo", "budget_type", config.BudgetType),
		resource.TestCheckResourceAttr("aws_budget.foo", "limit_amount", config.LimitAmount),
		resource.TestCheckResourceAttr("aws_budget.foo", "limit_unit", config.LimitUnit),
		resource.TestCheckResourceAttr("aws_budget.foo", "time_period_start", config.TimePeriodStart),
		resource.TestCheckResourceAttr("aws_budget.foo", "time_period_end", config.TimePeriodEnd),
		resource.TestCheckResourceAttr("aws_budget.foo", "time_unit", config.TimeUnit),
		testBudgetExists(config, provider),
	)
}

func testBudgetExists(config budgetTestConfig, provider *schema.Provider) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources["aws_budget.foo"]
		if !ok {
			return fmt.Errorf("Not found: %s", "aws_budget.foo")
		}

		b, err := describeBudget(rs.Primary.ID, provider.Meta())
		if err != nil {
			return fmt.Errorf("Describebudget error: %v", err)
		}

		if b.Budget == nil {
			return fmt.Errorf("No budget returned %v in %v", b.Budget, b)
		}

		if *b.Budget.BudgetLimit.Amount != config.LimitAmount {
			return fmt.Errorf("budget limit incorrectly set %v != %v", config.LimitAmount, *b.Budget.BudgetLimit.Amount)
		}

		if err := checkBudgetCostTypes(config, *b.Budget.CostTypes); err != nil {
			return err
		}

		if err := checkBudgetTimePeriod(config, *b.Budget.TimePeriod); err != nil {
			return err
		}

		if v, ok := b.Budget.CostFilters[config.FilterKey]; !ok || *v[len(v)-1] != config.FilterValue {
			return fmt.Errorf("cost filter not set properly: %v", b.Budget.CostFilters)
		}

		return nil
	}
}

func checkBudgetTimePeriod(config budgetTestConfig, timePeriod budgets.TimePeriod) error {
	if end, _ := time.Parse("2006-01-02_15:04", config.TimePeriodEnd); *timePeriod.End != end {
		return fmt.Errorf("TimePeriodEnd not set properly '%v' should be '%v'", *timePeriod.End, end)
	}

	if start, _ := time.Parse("2006-01-02_15:04", config.TimePeriodStart); *timePeriod.Start != start {
		return fmt.Errorf("TimePeriodStart not set properly '%v' should be '%v'", *timePeriod.Start, start)
	}

	return nil
}

func checkBudgetCostTypes(config budgetTestConfig, costTypes budgets.CostTypes) error {
	if strconv.FormatBool(*costTypes.IncludeCredit) != config.IncludeCredit {
		return fmt.Errorf("IncludeCredit not set properly '%v' should be '%v'", *costTypes.IncludeCredit, config.IncludeCredit)
	}

	if strconv.FormatBool(*costTypes.IncludeOtherSubscription) != config.IncludeOtherSubscription {
		return fmt.Errorf("IncludeOtherSubscription not set properly '%v' should be '%v'", *costTypes.IncludeOtherSubscription, config.IncludeOtherSubscription)
	}

	if strconv.FormatBool(*costTypes.IncludeRecurring) != config.IncludeRecurring {
		return fmt.Errorf("IncludeRecurring not set properly  '%v' should be '%v'", *costTypes.IncludeRecurring, config.IncludeRecurring)
	}

	if strconv.FormatBool(*costTypes.IncludeRefund) != config.IncludeRefund {
		return fmt.Errorf("IncludeRefund not set properly '%v' should be '%v'", *costTypes.IncludeRefund, config.IncludeRefund)
	}

	if strconv.FormatBool(*costTypes.IncludeSubscription) != config.IncludeSubscription {
		return fmt.Errorf("IncludeSubscription not set properly '%v' should be '%v'", *costTypes.IncludeSubscription, config.IncludeSubscription)
	}

	if strconv.FormatBool(*costTypes.IncludeSupport) != config.IncludeSupport {
		return fmt.Errorf("IncludeSupport not set properly '%v' should be '%v'", *costTypes.IncludeSupport, config.IncludeSupport)
	}

	if strconv.FormatBool(*costTypes.IncludeTax) != config.IncludeTax {
		return fmt.Errorf("IncludeTax not set properly '%v' should be '%v'", *costTypes.IncludeTax, config.IncludeTax)
	}

	if strconv.FormatBool(*costTypes.IncludeUpfront) != config.IncludeUpfront {
		return fmt.Errorf("IncludeUpfront not set properly '%v' should be '%v'", *costTypes.IncludeUpfront, config.IncludeUpfront)
	}

	if strconv.FormatBool(*costTypes.UseBlended) != config.UseBlended {
		return fmt.Errorf("UseBlended not set properly '%v' should be '%v'", *costTypes.UseBlended, config.UseBlended)
	}

	return nil
}

func testCheckBudgetDestroy(budgetName string, provider *schema.Provider) error {
	if budgetExists(budgetName, provider.Meta()) {
		return fmt.Errorf("Budget '%s' was not deleted properly", budgetName)
	}

	return nil
}

type budgetTestConfig struct {
	BudgetName               string
	BudgetType               string
	LimitAmount              string
	LimitUnit                string
	IncludeCredit            string
	IncludeOtherSubscription string
	IncludeRecurring         string
	IncludeRefund            string
	IncludeSubscription      string
	IncludeSupport           string
	IncludeTax               string
	IncludeUpfront           string
	UseBlended               string
	TimeUnit                 string
	TimePeriodStart          string
	TimePeriodEnd            string
	FilterKey                string
	FilterValue              string
}

func testBudgetHCLPrefixUseDefaults(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name_prefix = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	time_period_start = "{{.TimePeriodStart}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}

func testBudgetHCLPrefix(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name_prefix = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	include_tax = "{{.IncludeTax}}"
	include_subscription = "{{.IncludeSubscription}}"
	use_blended = "{{.UseBlended}}"
	time_period_start = "{{.TimePeriodStart}}"
	time_period_end = "{{.TimePeriodEnd}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}

func testBudgetHCLBasicUseDefaults(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	time_period_start = "{{.TimePeriodStart}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}

func testBudgetHCLBasic(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	include_tax = "{{.IncludeTax}}"
	include_subscription = "{{.IncludeSubscription}}"
	use_blended = "{{.UseBlended}}"
	time_period_start = "{{.TimePeriodStart}}"
	time_period_end = "{{.TimePeriodEnd}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}
<commit_msg>dry up budget test configs<commit_after>package aws

import (
	"bytes"
	"fmt"
	"regexp"
	"strconv"
	"testing"
	"text/template"
	"time"

	"github.com/aws/aws-sdk-go/service/budgets"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

func TestAwsBudget_basic(t *testing.T) {
	name := fmt.Sprintf("test-budget-%d", acctest.RandInt())
	configBasicDefaults := newBudgetTestConfigDefaults(name)
	configBasicUpdate := newBudgetTestConfigUpdate(name)

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return testCheckBudgetDestroy(name, testAccProvider)
		},
		Steps: []resource.TestStep{
			{
				Config: testBudgetHCLBasicUseDefaults(configBasicDefaults),
				Check:  newComposedBudgetTestCheck(configBasicDefaults, testAccProvider, "name"),
			},

			{
				Config: testBudgetHCLBasic(configBasicUpdate),
				Check:  newComposedBudgetTestCheck(configBasicUpdate, testAccProvider, "name"),
			},
		},
	})
}

func TestAwsBudget_prefix(t *testing.T) {
	name := "test-budget-"
	configBasicDefaults := newBudgetTestConfigDefaults(name)
	configBasicUpdate := newBudgetTestConfigUpdate(name)

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		CheckDestroy: func(s *terraform.State) error {
			return testCheckBudgetDestroy(name, testAccProvider)
		},
		Steps: []resource.TestStep{
			{
				Config: testBudgetHCLPrefixUseDefaults(configBasicDefaults),
				Check:  newComposedBudgetTestCheck(configBasicDefaults, testAccProvider, "name_prefix"),
			},

			{
				Config: testBudgetHCLPrefix(configBasicUpdate),
				Check:  newComposedBudgetTestCheck(configBasicUpdate, testAccProvider, "name_prefix"),
			},
		},
	})
}

func newComposedBudgetTestCheck(config budgetTestConfig, provider *schema.Provider, nameField string) resource.TestCheckFunc {
	return resource.ComposeTestCheckFunc(
		resource.TestMatchResourceAttr("aws_budget.foo", nameField, regexp.MustCompile(config.BudgetName)),
		resource.TestCheckResourceAttr("aws_budget.foo", "budget_type", config.BudgetType),
		resource.TestCheckResourceAttr("aws_budget.foo", "limit_amount", config.LimitAmount),
		resource.TestCheckResourceAttr("aws_budget.foo", "limit_unit", config.LimitUnit),
		resource.TestCheckResourceAttr("aws_budget.foo", "time_period_start", config.TimePeriodStart),
		resource.TestCheckResourceAttr("aws_budget.foo", "time_period_end", config.TimePeriodEnd),
		resource.TestCheckResourceAttr("aws_budget.foo", "time_unit", config.TimeUnit),
		testBudgetExists(config, provider),
	)
}

func testBudgetExists(config budgetTestConfig, provider *schema.Provider) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources["aws_budget.foo"]
		if !ok {
			return fmt.Errorf("Not found: %s", "aws_budget.foo")
		}

		b, err := describeBudget(rs.Primary.ID, provider.Meta())
		if err != nil {
			return fmt.Errorf("Describebudget error: %v", err)
		}

		if b.Budget == nil {
			return fmt.Errorf("No budget returned %v in %v", b.Budget, b)
		}

		if *b.Budget.BudgetLimit.Amount != config.LimitAmount {
			return fmt.Errorf("budget limit incorrectly set %v != %v", config.LimitAmount, *b.Budget.BudgetLimit.Amount)
		}

		if err := checkBudgetCostTypes(config, *b.Budget.CostTypes); err != nil {
			return err
		}

		if err := checkBudgetTimePeriod(config, *b.Budget.TimePeriod); err != nil {
			return err
		}

		if v, ok := b.Budget.CostFilters[config.FilterKey]; !ok || *v[len(v)-1] != config.FilterValue {
			return fmt.Errorf("cost filter not set properly: %v", b.Budget.CostFilters)
		}

		return nil
	}
}

func checkBudgetTimePeriod(config budgetTestConfig, timePeriod budgets.TimePeriod) error {
	if end, _ := time.Parse("2006-01-02_15:04", config.TimePeriodEnd); *timePeriod.End != end {
		return fmt.Errorf("TimePeriodEnd not set properly '%v' should be '%v'", *timePeriod.End, end)
	}

	if start, _ := time.Parse("2006-01-02_15:04", config.TimePeriodStart); *timePeriod.Start != start {
		return fmt.Errorf("TimePeriodStart not set properly '%v' should be '%v'", *timePeriod.Start, start)
	}

	return nil
}

func checkBudgetCostTypes(config budgetTestConfig, costTypes budgets.CostTypes) error {
	if strconv.FormatBool(*costTypes.IncludeCredit) != config.IncludeCredit {
		return fmt.Errorf("IncludeCredit not set properly '%v' should be '%v'", *costTypes.IncludeCredit, config.IncludeCredit)
	}

	if strconv.FormatBool(*costTypes.IncludeOtherSubscription) != config.IncludeOtherSubscription {
		return fmt.Errorf("IncludeOtherSubscription not set properly '%v' should be '%v'", *costTypes.IncludeOtherSubscription, config.IncludeOtherSubscription)
	}

	if strconv.FormatBool(*costTypes.IncludeRecurring) != config.IncludeRecurring {
		return fmt.Errorf("IncludeRecurring not set properly  '%v' should be '%v'", *costTypes.IncludeRecurring, config.IncludeRecurring)
	}

	if strconv.FormatBool(*costTypes.IncludeRefund) != config.IncludeRefund {
		return fmt.Errorf("IncludeRefund not set properly '%v' should be '%v'", *costTypes.IncludeRefund, config.IncludeRefund)
	}

	if strconv.FormatBool(*costTypes.IncludeSubscription) != config.IncludeSubscription {
		return fmt.Errorf("IncludeSubscription not set properly '%v' should be '%v'", *costTypes.IncludeSubscription, config.IncludeSubscription)
	}

	if strconv.FormatBool(*costTypes.IncludeSupport) != config.IncludeSupport {
		return fmt.Errorf("IncludeSupport not set properly '%v' should be '%v'", *costTypes.IncludeSupport, config.IncludeSupport)
	}

	if strconv.FormatBool(*costTypes.IncludeTax) != config.IncludeTax {
		return fmt.Errorf("IncludeTax not set properly '%v' should be '%v'", *costTypes.IncludeTax, config.IncludeTax)
	}

	if strconv.FormatBool(*costTypes.IncludeUpfront) != config.IncludeUpfront {
		return fmt.Errorf("IncludeUpfront not set properly '%v' should be '%v'", *costTypes.IncludeUpfront, config.IncludeUpfront)
	}

	if strconv.FormatBool(*costTypes.UseBlended) != config.UseBlended {
		return fmt.Errorf("UseBlended not set properly '%v' should be '%v'", *costTypes.UseBlended, config.UseBlended)
	}

	return nil
}

func testCheckBudgetDestroy(budgetName string, provider *schema.Provider) error {
	if budgetExists(budgetName, provider.Meta()) {
		return fmt.Errorf("Budget '%s' was not deleted properly", budgetName)
	}

	return nil
}

type budgetTestConfig struct {
	BudgetName               string
	BudgetType               string
	LimitAmount              string
	LimitUnit                string
	IncludeCredit            string
	IncludeOtherSubscription string
	IncludeRecurring         string
	IncludeRefund            string
	IncludeSubscription      string
	IncludeSupport           string
	IncludeTax               string
	IncludeUpfront           string
	UseBlended               string
	TimeUnit                 string
	TimePeriodStart          string
	TimePeriodEnd            string
	FilterKey                string
	FilterValue              string
}

func newBudgetTestConfigUpdate(name string) budgetTestConfig {
	return budgetTestConfig{
		BudgetName:               name,
		BudgetType:               "COST",
		LimitAmount:              "500",
		LimitUnit:                "USD",
		FilterKey:                "AZ",
		FilterValue:              "us-east-2",
		IncludeCredit:            "true",
		IncludeOtherSubscription: "true",
		IncludeRecurring:         "true",
		IncludeRefund:            "true",
		IncludeSubscription:      "false",
		IncludeSupport:           "true",
		IncludeTax:               "false",
		IncludeUpfront:           "true",
		UseBlended:               "true",
		TimeUnit:                 "MONTHLY",
		TimePeriodStart:          "2017-01-01_12:00",
		TimePeriodEnd:            "2018-01-01_12:00",
	}
}

func newBudgetTestConfigDefaults(name string) budgetTestConfig {
	return budgetTestConfig{
		BudgetName:               name,
		BudgetType:               "COST",
		LimitAmount:              "100",
		LimitUnit:                "USD",
		FilterKey:                "AZ",
		FilterValue:              "us-east-1",
		IncludeCredit:            "true",
		IncludeOtherSubscription: "true",
		IncludeRecurring:         "true",
		IncludeRefund:            "true",
		IncludeSubscription:      "true",
		IncludeSupport:           "true",
		IncludeTax:               "true",
		IncludeUpfront:           "true",
		UseBlended:               "false",
		TimeUnit:                 "MONTHLY",
		TimePeriodStart:          "2017-01-01_12:00",
		TimePeriodEnd:            "2087-06-15_12:00",
	}
}

func testBudgetHCLPrefixUseDefaults(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name_prefix = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	time_period_start = "{{.TimePeriodStart}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}

func testBudgetHCLPrefix(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name_prefix = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	include_tax = "{{.IncludeTax}}"
	include_subscription = "{{.IncludeSubscription}}"
	use_blended = "{{.UseBlended}}"
	time_period_start = "{{.TimePeriodStart}}"
	time_period_end = "{{.TimePeriodEnd}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}

func testBudgetHCLBasicUseDefaults(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	time_period_start = "{{.TimePeriodStart}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}

func testBudgetHCLBasic(budgetConfig budgetTestConfig) string {
	t := template.Must(template.New("t1").
		Parse(`
resource "aws_budget" "foo" {
	name = "{{.BudgetName}}"
	budget_type = "{{.BudgetType}}"
 	limit_amount = "{{.LimitAmount}}"
 	limit_unit = "{{.LimitUnit}}"
	include_tax = "{{.IncludeTax}}"
	include_subscription = "{{.IncludeSubscription}}"
	use_blended = "{{.UseBlended}}"
	time_period_start = "{{.TimePeriodStart}}"
	time_period_end = "{{.TimePeriodEnd}}"
 	time_unit = "{{.TimeUnit}}"
	cost_filters {
		{{.FilterKey}} = "{{.FilterValue}}"
	}
}
`))
	var doc bytes.Buffer
	t.Execute(&doc, budgetConfig)
	return doc.String()
}
<|endoftext|>