<commit_before>package aws

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

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/structure"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsApiGatewayRestApi() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsApiGatewayRestApiCreate,
		Read:   resourceAwsApiGatewayRestApiRead,
		Update: resourceAwsApiGatewayRestApiUpdate,
		Delete: resourceAwsApiGatewayRestApiDelete,

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

			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"api_key_source": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "HEADER",
			},

			"policy": {
				Type:             schema.TypeString,
				Optional:         true,
				ValidateFunc:     validateJsonString,
				DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
			},

			"binary_media_types": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"body": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"minimum_compression_size": {
				Type:         schema.TypeInt,
				Optional:     true,
				Default:      -1,
				ValidateFunc: validateIntegerInRange(-1, 10485760),
			},

			"root_resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"execution_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"endpoint_configuration": {
				Type:     schema.TypeList,
				Optional: true,
				Computed: true,
				MinItems: 1,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"types": {
							Type:     schema.TypeList,
							Required: true,
							MinItems: 1,
							MaxItems: 1,
							Elem: &schema.Schema{
								Type: schema.TypeString,
								ValidateFunc: validation.StringInSlice([]string{
									apigateway.EndpointTypeEdge,
									apigateway.EndpointTypeRegional,
								}, false),
							},
						},
					},
				},
			},
		},
	}
}

func resourceAwsApiGatewayRestApiCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Creating API Gateway")

	var description *string
	if d.Get("description").(string) != "" {
		description = aws.String(d.Get("description").(string))
	}

	params := &apigateway.CreateRestApiInput{
		Name:        aws.String(d.Get("name").(string)),
		Description: description,
	}

	if v, ok := d.GetOk("endpoint_configuration"); ok {
		params.EndpointConfiguration = expandApiGatewayEndpointConfiguration(v.([]interface{}))
	}

	if v, ok := d.GetOk("api_key_source"); ok && v.(string) != "" {
		params.ApiKeySource = aws.String(v.(string))
	}

	if v, ok := d.GetOk("policy"); ok && v.(string) != "" {
		params.Policy = aws.String(v.(string))
	}

	binaryMediaTypes, binaryMediaTypesOk := d.GetOk("binary_media_types")
	if binaryMediaTypesOk {
		params.BinaryMediaTypes = expandStringList(binaryMediaTypes.([]interface{}))
	}

	minimumCompressionSize := d.Get("minimum_compression_size").(int)
	if minimumCompressionSize > -1 {
		params.MinimumCompressionSize = aws.Int64(int64(minimumCompressionSize))
	}

	gateway, err := conn.CreateRestApi(params)
	if err != nil {
		return fmt.Errorf("Error creating API Gateway: %s", err)
	}

	d.SetId(*gateway.Id)

	if body, ok := d.GetOk("body"); ok {
		log.Printf("[DEBUG] Initializing API Gateway from OpenAPI spec %s", d.Id())
		_, err := conn.PutRestApi(&apigateway.PutRestApiInput{
			RestApiId: gateway.Id,
			Mode:      aws.String(apigateway.PutModeOverwrite),
			Body:      []byte(body.(string)),
		})
		if err != nil {
			return errwrap.Wrapf("Error creating API Gateway specification: {{err}}", err)
		}
	}

	if err = resourceAwsApiGatewayRestApiRefreshResources(d, meta); err != nil {
		return err
	}

	return resourceAwsApiGatewayRestApiRead(d, meta)
}

func resourceAwsApiGatewayRestApiRefreshResources(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway

	resp, err := conn.GetResources(&apigateway.GetResourcesInput{
		RestApiId: aws.String(d.Id()),
	})
	if err != nil {
		return err
	}

	for _, item := range resp.Items {
		if *item.Path == "/" {
			d.Set("root_resource_id", item.Id)
			break
		}
	}

	return nil
}

func resourceAwsApiGatewayRestApiRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Reading API Gateway %s", d.Id())

	api, err := conn.GetRestApi(&apigateway.GetRestApiInput{
		RestApiId: aws.String(d.Id()),
	})
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" {
			log.Printf("[WARN] API Gateway (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return err
	}

	d.Set("name", api.Name)
	d.Set("description", api.Description)
	d.Set("api_key_source", api.ApiKeySource)

	// The API returns policy as an escaped JSON string
	// {\\\"Version\\\":\\\"2012-10-17\\\",...}
	// The string must be normalized before unquoting as it may contain escaped
	// forward slashes in CIDR blocks, which will break strconv.Unquote

	// I'm not sure why it needs to be wrapped with double quotes first, but it does
	normalized_policy, err := structure.NormalizeJsonString(`"` + aws.StringValue(api.Policy) + `"`)
	if err != nil {
		fmt.Printf("error normalizing policy JSON: %s\n", err)
	}
	policy, err := strconv.Unquote(normalized_policy)
	if err != nil {
		return fmt.Errorf("error unescaping policy: %s", err)
	}
	d.Set("policy", policy)

	d.Set("binary_media_types", api.BinaryMediaTypes)

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Service:   "execute-api",
		Region:    meta.(*AWSClient).region,
		AccountID: meta.(*AWSClient).accountid,
		Resource:  d.Id(),
	}.String()
	d.Set("execution_arn", arn)

	if api.MinimumCompressionSize == nil {
		d.Set("minimum_compression_size", -1)
	} else {
		d.Set("minimum_compression_size", api.MinimumCompressionSize)
	}
	if err := d.Set("created_date", api.CreatedDate.Format(time.RFC3339)); err != nil {
		log.Printf("[DEBUG] Error setting created_date: %s", err)
	}

	if err := d.Set("endpoint_configuration", flattenApiGatewayEndpointConfiguration(api.EndpointConfiguration)); err != nil {
		return fmt.Errorf("error setting endpoint_configuration: %s", err)
	}

	return nil
}

func resourceAwsApiGatewayRestApiUpdateOperations(d *schema.ResourceData) []*apigateway.PatchOperation {
	operations := make([]*apigateway.PatchOperation, 0)

	if d.HasChange("name") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/name"),
			Value: aws.String(d.Get("name").(string)),
		})
	}

	if d.HasChange("description") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/description"),
			Value: aws.String(d.Get("description").(string)),
		})
	}

	if d.HasChange("api_key_source") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/api_key_source"),
			Value: aws.String(d.Get("api_key_source").(string)),
		})
	}

	if d.HasChange("policy") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/policy"),
			Value: aws.String(d.Get("policy").(string)),
		})
	}

	if d.HasChange("minimum_compression_size") {
		minimumCompressionSize := d.Get("minimum_compression_size").(int)
		var value string
		if minimumCompressionSize > -1 {
			value = strconv.Itoa(minimumCompressionSize)
		}
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/minimumCompressionSize"),
			Value: aws.String(value),
		})
	}

	if d.HasChange("binary_media_types") {
		o, n := d.GetChange("binary_media_types")
		prefix := "binaryMediaTypes"

		old := o.([]interface{})
		new := n.([]interface{})

		// Remove every binary media types. Simpler to remove and add new ones,
		// since there are no replacings.
		for _, v := range old {
			operations = append(operations, &apigateway.PatchOperation{
				Op:   aws.String("remove"),
				Path: aws.String(fmt.Sprintf("/%s/%s", prefix, escapeJsonPointer(v.(string)))),
			})
		}

		// Handle additions
		if len(new) > 0 {
			for _, v := range new {
				operations = append(operations, &apigateway.PatchOperation{
					Op:   aws.String("add"),
					Path: aws.String(fmt.Sprintf("/%s/%s", prefix, escapeJsonPointer(v.(string)))),
				})
			}
		}
	}

	if d.HasChange("endpoint_configuration.0.types") {
		// The REST API must have an endpoint type.
		// If attempting to remove the configuration, do nothing.
		if v, ok := d.GetOk("endpoint_configuration"); ok && len(v.([]interface{})) > 0 {
			m := v.([]interface{})[0].(map[string]interface{})

			operations = append(operations, &apigateway.PatchOperation{
				Op:    aws.String("replace"),
				Path:  aws.String("/endpointConfiguration/types/0"),
				Value: aws.String(m["types"].([]interface{})[0].(string)),
			})
		}
	}

	return operations
}

func resourceAwsApiGatewayRestApiUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Updating API Gateway %s", d.Id())

	if d.HasChange("body") {
		if body, ok := d.GetOk("body"); ok {
			log.Printf("[DEBUG] Updating API Gateway from OpenAPI spec: %s", d.Id())
			_, err := conn.PutRestApi(&apigateway.PutRestApiInput{
				RestApiId: aws.String(d.Id()),
				Mode:      aws.String(apigateway.PutModeOverwrite),
				Body:      []byte(body.(string)),
			})
			if err != nil {
				return errwrap.Wrapf("Error updating API Gateway specification: {{err}}", err)
			}
		}
	}

	_, err := conn.UpdateRestApi(&apigateway.UpdateRestApiInput{
		RestApiId:       aws.String(d.Id()),
		PatchOperations: resourceAwsApiGatewayRestApiUpdateOperations(d),
	})

	if err != nil {
		return err
	}
	log.Printf("[DEBUG] Updated API Gateway %s", d.Id())

	return resourceAwsApiGatewayRestApiRead(d, meta)
}

func resourceAwsApiGatewayRestApiDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Deleting API Gateway: %s", d.Id())

	return resource.Retry(10*time.Minute, func() *resource.RetryError {
		_, err := conn.DeleteRestApi(&apigateway.DeleteRestApiInput{
			RestApiId: aws.String(d.Id()),
		})
		if err == nil {
			return nil
		}

		if apigatewayErr, ok := err.(awserr.Error); ok && apigatewayErr.Code() == "NotFoundException" {
			return nil
		}

		return resource.NonRetryableError(err)
	})
}

func expandApiGatewayEndpointConfiguration(l []interface{}) *apigateway.EndpointConfiguration {
	if len(l) == 0 {
		return nil
	}

	m := l[0].(map[string]interface{})

	endpointConfiguration := &apigateway.EndpointConfiguration{
		Types: expandStringList(m["types"].([]interface{})),
	}

	return endpointConfiguration
}

func flattenApiGatewayEndpointConfiguration(endpointConfiguration *apigateway.EndpointConfiguration) []interface{} {
	if endpointConfiguration == nil {
		return []interface{}{}
	}

	m := map[string]interface{}{
		"types": flattenStringList(endpointConfiguration.Types),
	}

	return []interface{}{m}
}
<commit_msg>fix patch path for api key source<commit_after>package aws

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

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/structure"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsApiGatewayRestApi() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsApiGatewayRestApiCreate,
		Read:   resourceAwsApiGatewayRestApiRead,
		Update: resourceAwsApiGatewayRestApiUpdate,
		Delete: resourceAwsApiGatewayRestApiDelete,

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

			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"api_key_source": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "HEADER",
			},

			"policy": {
				Type:             schema.TypeString,
				Optional:         true,
				ValidateFunc:     validateJsonString,
				DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
			},

			"binary_media_types": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"body": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"minimum_compression_size": {
				Type:         schema.TypeInt,
				Optional:     true,
				Default:      -1,
				ValidateFunc: validateIntegerInRange(-1, 10485760),
			},

			"root_resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"execution_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"endpoint_configuration": {
				Type:     schema.TypeList,
				Optional: true,
				Computed: true,
				MinItems: 1,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"types": {
							Type:     schema.TypeList,
							Required: true,
							MinItems: 1,
							MaxItems: 1,
							Elem: &schema.Schema{
								Type: schema.TypeString,
								ValidateFunc: validation.StringInSlice([]string{
									apigateway.EndpointTypeEdge,
									apigateway.EndpointTypeRegional,
								}, false),
							},
						},
					},
				},
			},
		},
	}
}

func resourceAwsApiGatewayRestApiCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Creating API Gateway")

	var description *string
	if d.Get("description").(string) != "" {
		description = aws.String(d.Get("description").(string))
	}

	params := &apigateway.CreateRestApiInput{
		Name:        aws.String(d.Get("name").(string)),
		Description: description,
	}

	if v, ok := d.GetOk("endpoint_configuration"); ok {
		params.EndpointConfiguration = expandApiGatewayEndpointConfiguration(v.([]interface{}))
	}

	if v, ok := d.GetOk("api_key_source"); ok && v.(string) != "" {
		params.ApiKeySource = aws.String(v.(string))
	}

	if v, ok := d.GetOk("policy"); ok && v.(string) != "" {
		params.Policy = aws.String(v.(string))
	}

	binaryMediaTypes, binaryMediaTypesOk := d.GetOk("binary_media_types")
	if binaryMediaTypesOk {
		params.BinaryMediaTypes = expandStringList(binaryMediaTypes.([]interface{}))
	}

	minimumCompressionSize := d.Get("minimum_compression_size").(int)
	if minimumCompressionSize > -1 {
		params.MinimumCompressionSize = aws.Int64(int64(minimumCompressionSize))
	}

	gateway, err := conn.CreateRestApi(params)
	if err != nil {
		return fmt.Errorf("Error creating API Gateway: %s", err)
	}

	d.SetId(*gateway.Id)

	if body, ok := d.GetOk("body"); ok {
		log.Printf("[DEBUG] Initializing API Gateway from OpenAPI spec %s", d.Id())
		_, err := conn.PutRestApi(&apigateway.PutRestApiInput{
			RestApiId: gateway.Id,
			Mode:      aws.String(apigateway.PutModeOverwrite),
			Body:      []byte(body.(string)),
		})
		if err != nil {
			return errwrap.Wrapf("Error creating API Gateway specification: {{err}}", err)
		}
	}

	if err = resourceAwsApiGatewayRestApiRefreshResources(d, meta); err != nil {
		return err
	}

	return resourceAwsApiGatewayRestApiRead(d, meta)
}

func resourceAwsApiGatewayRestApiRefreshResources(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway

	resp, err := conn.GetResources(&apigateway.GetResourcesInput{
		RestApiId: aws.String(d.Id()),
	})
	if err != nil {
		return err
	}

	for _, item := range resp.Items {
		if *item.Path == "/" {
			d.Set("root_resource_id", item.Id)
			break
		}
	}

	return nil
}

func resourceAwsApiGatewayRestApiRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Reading API Gateway %s", d.Id())

	api, err := conn.GetRestApi(&apigateway.GetRestApiInput{
		RestApiId: aws.String(d.Id()),
	})
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" {
			log.Printf("[WARN] API Gateway (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return err
	}

	d.Set("name", api.Name)
	d.Set("description", api.Description)
	d.Set("api_key_source", api.ApiKeySource)

	// The API returns policy as an escaped JSON string
	// {\\\"Version\\\":\\\"2012-10-17\\\",...}
	// The string must be normalized before unquoting as it may contain escaped
	// forward slashes in CIDR blocks, which will break strconv.Unquote

	// I'm not sure why it needs to be wrapped with double quotes first, but it does
	normalized_policy, err := structure.NormalizeJsonString(`"` + aws.StringValue(api.Policy) + `"`)
	if err != nil {
		fmt.Printf("error normalizing policy JSON: %s\n", err)
	}
	policy, err := strconv.Unquote(normalized_policy)
	if err != nil {
		return fmt.Errorf("error unescaping policy: %s", err)
	}
	d.Set("policy", policy)

	d.Set("binary_media_types", api.BinaryMediaTypes)

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Service:   "execute-api",
		Region:    meta.(*AWSClient).region,
		AccountID: meta.(*AWSClient).accountid,
		Resource:  d.Id(),
	}.String()
	d.Set("execution_arn", arn)

	if api.MinimumCompressionSize == nil {
		d.Set("minimum_compression_size", -1)
	} else {
		d.Set("minimum_compression_size", api.MinimumCompressionSize)
	}
	if err := d.Set("created_date", api.CreatedDate.Format(time.RFC3339)); err != nil {
		log.Printf("[DEBUG] Error setting created_date: %s", err)
	}

	if err := d.Set("endpoint_configuration", flattenApiGatewayEndpointConfiguration(api.EndpointConfiguration)); err != nil {
		return fmt.Errorf("error setting endpoint_configuration: %s", err)
	}

	return nil
}

func resourceAwsApiGatewayRestApiUpdateOperations(d *schema.ResourceData) []*apigateway.PatchOperation {
	operations := make([]*apigateway.PatchOperation, 0)

	if d.HasChange("name") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/name"),
			Value: aws.String(d.Get("name").(string)),
		})
	}

	if d.HasChange("description") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/description"),
			Value: aws.String(d.Get("description").(string)),
		})
	}

	if d.HasChange("api_key_source") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/apiKeySource"),
			Value: aws.String(d.Get("api_key_source").(string)),
		})
	}

	if d.HasChange("policy") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/policy"),
			Value: aws.String(d.Get("policy").(string)),
		})
	}

	if d.HasChange("minimum_compression_size") {
		minimumCompressionSize := d.Get("minimum_compression_size").(int)
		var value string
		if minimumCompressionSize > -1 {
			value = strconv.Itoa(minimumCompressionSize)
		}
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String("replace"),
			Path:  aws.String("/minimumCompressionSize"),
			Value: aws.String(value),
		})
	}

	if d.HasChange("binary_media_types") {
		o, n := d.GetChange("binary_media_types")
		prefix := "binaryMediaTypes"

		old := o.([]interface{})
		new := n.([]interface{})

		// Remove every binary media types. Simpler to remove and add new ones,
		// since there are no replacings.
		for _, v := range old {
			operations = append(operations, &apigateway.PatchOperation{
				Op:   aws.String("remove"),
				Path: aws.String(fmt.Sprintf("/%s/%s", prefix, escapeJsonPointer(v.(string)))),
			})
		}

		// Handle additions
		if len(new) > 0 {
			for _, v := range new {
				operations = append(operations, &apigateway.PatchOperation{
					Op:   aws.String("add"),
					Path: aws.String(fmt.Sprintf("/%s/%s", prefix, escapeJsonPointer(v.(string)))),
				})
			}
		}
	}

	if d.HasChange("endpoint_configuration.0.types") {
		// The REST API must have an endpoint type.
		// If attempting to remove the configuration, do nothing.
		if v, ok := d.GetOk("endpoint_configuration"); ok && len(v.([]interface{})) > 0 {
			m := v.([]interface{})[0].(map[string]interface{})

			operations = append(operations, &apigateway.PatchOperation{
				Op:    aws.String("replace"),
				Path:  aws.String("/endpointConfiguration/types/0"),
				Value: aws.String(m["types"].([]interface{})[0].(string)),
			})
		}
	}

	return operations
}

func resourceAwsApiGatewayRestApiUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Updating API Gateway %s", d.Id())

	if d.HasChange("body") {
		if body, ok := d.GetOk("body"); ok {
			log.Printf("[DEBUG] Updating API Gateway from OpenAPI spec: %s", d.Id())
			_, err := conn.PutRestApi(&apigateway.PutRestApiInput{
				RestApiId: aws.String(d.Id()),
				Mode:      aws.String(apigateway.PutModeOverwrite),
				Body:      []byte(body.(string)),
			})
			if err != nil {
				return errwrap.Wrapf("Error updating API Gateway specification: {{err}}", err)
			}
		}
	}

	_, err := conn.UpdateRestApi(&apigateway.UpdateRestApiInput{
		RestApiId:       aws.String(d.Id()),
		PatchOperations: resourceAwsApiGatewayRestApiUpdateOperations(d),
	})

	if err != nil {
		return err
	}
	log.Printf("[DEBUG] Updated API Gateway %s", d.Id())

	return resourceAwsApiGatewayRestApiRead(d, meta)
}

func resourceAwsApiGatewayRestApiDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigateway
	log.Printf("[DEBUG] Deleting API Gateway: %s", d.Id())

	return resource.Retry(10*time.Minute, func() *resource.RetryError {
		_, err := conn.DeleteRestApi(&apigateway.DeleteRestApiInput{
			RestApiId: aws.String(d.Id()),
		})
		if err == nil {
			return nil
		}

		if apigatewayErr, ok := err.(awserr.Error); ok && apigatewayErr.Code() == "NotFoundException" {
			return nil
		}

		return resource.NonRetryableError(err)
	})
}

func expandApiGatewayEndpointConfiguration(l []interface{}) *apigateway.EndpointConfiguration {
	if len(l) == 0 {
		return nil
	}

	m := l[0].(map[string]interface{})

	endpointConfiguration := &apigateway.EndpointConfiguration{
		Types: expandStringList(m["types"].([]interface{})),
	}

	return endpointConfiguration
}

func flattenApiGatewayEndpointConfiguration(endpointConfiguration *apigateway.EndpointConfiguration) []interface{} {
	if endpointConfiguration == nil {
		return []interface{}{}
	}

	m := map[string]interface{}{
		"types": flattenStringList(endpointConfiguration.Types),
	}

	return []interface{}{m}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 1&1 Internet AG, http://1und1.de . All rights reserved. Licensed under the Apache v2 License.
 */

package oneandone_cloudserver_api

import (
	"github.com/docker/machine/log"
	"net/http"
)

type Server struct {
	withId
	withName
	withDescription
	Password string        `json:"first_password"`
	Status   Status        `json:"status"`
	Hardware Hardware      `json:"hardware"`
	Image    ImageInServer `json:"image"`
	Ips      []IpInServer  `json:"ips"`
	withApi
}

type Hardware struct {
	Vcores            int   `json:"vcore"`
	CoresPerProcessor int   `json:"cores_per_processor"`
	Ram               int   `json:"ram"`
	Hdds              []Hdd `json:"hdds"`
}

type Hdd struct {
	withId
	Size   int  `json:"size"`
	IsMain bool `json:"is_main"`
}

type ImageInServer struct {
	withId
	withName
}

type IpInServer struct {
	withId
	withType
	Ip         string               `json:"ip"`
	ReverseDns string               `json:"reverse_dns"`
	Firewall   FirewallInIpInServer `json:"firewall_policy"`
}

type FirewallInIpInServer struct {
	withId
	withName
}

type ServerCreateData struct {
	Name               string   `json:"name"`
	Description        string   `json:"description"`
	Hardware           Hardware `json:"hardware"`
	ApplianceId        string   `json:"appliance_id"`
	Password           string   `json:"password"`
	PowerOn            bool     `json:"power_on"`
	FirewallPolicyId   string   `json:"firewall_policy_id"`
	IpId               string   `json:"ip_id"`
	LoadBalancerId     string   `json:"load_balancer_id"`
	MonitoringPolicyId string   `json:"monitoring_policy_id"`
	PrivateNetworkId   string   `json:"private_network_id"`
}

type ServerAction struct {
	Action string `json:"action"`
	Method string `json:"method"`
}

type FixedInstanceInformation struct {
	withName
	withId
	Hardware Hardware `json:"hardware"`
	withApi
}

type ServerRenameData struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

// GET /servers
func (api *API) GetServers() ([]Server, error) {
	log.Debug("requesting information about servers")
	result := []Server{}
	err := api.Client.Get(createUrl(api, "servers"), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	for index, _ := range result {
		result[index].api = api
	}
	return result, nil
}

// POST /servers
func (api *API) CreateServer(configuration ServerCreateData) (*Server, error) {
	log.Debug("requesting to create a new server")
	result := new(Server)
	err := api.Client.Post(createUrl(api, "servers"), &configuration, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = api
	return result, nil
}

// GET /servers/{id}
func (api *API) GetServer(Id string) (*Server, error) {
	log.Debug("requesting information about server ", Id)
	result := new(Server)
	err := api.Client.Get(createUrl(api, "servers", Id), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	result.api = api
	return result, nil
}

func (api *API) GetFixedInstanceSizes() ([]FixedInstanceInformation, error) {
	log.Debug("requesting information about fixed instance sizes")
	result := []FixedInstanceInformation{}
	err := api.Client.Get(createUrl(api, "servers", "fixed_instance_sizes"), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	for index, _ := range result {
		result[index].api = api
	}
	return result, nil
}

// DELETE /servers/{id}
func (s *Server) Delete() (*Server, error) {
	log.Debugf("Requested to delete server '%v' ", s.Id)
	result := new(Server)
	err := s.api.Client.Delete(createUrl(s.api, "servers", s.Id), &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

// PUT /servers/{id}
func (s *Server) RenameServer(data ServerRenameData) (*Server, error) {
	log.Debugf("Requested to rename server '%v'", s.Id)
	result := new(Server)
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id), data, &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

// GET /servers/{id}/size

// PUT /servers/{id}/size

// GET /servers/{id}/size/hdds

// POST /servers/{id}/size/hdds

// GET /servers/{id}/size/hdds/{id}

// DELETE /servers/{id}/size/hdds/{id}

// PUT /servers/{id}/size/hdds/{id}

// GET /servers/{id}/image

// PUT /servers/{id}/image

// GET /servers/{id}/ips

// POST /servers/{id}/ips

// GET /servers/{id}/ips/{id}

// DELETE /servers/{id}/ips/{id}

// PUT /servers/{id}/ips/{id}

// GET /servers/{id}/status

// PUT /servers/{id}/status/action
func (s *Server) Reboot(hardware bool) (*Server, error) {
	log.Debugf("Requested to reboot Server '%v'.", s.Id)
	result := new(Server)
	request := ServerAction{}
	request.Action = "REBOOT"
	if hardware {
		request.Method = "HARDWARE"
	} else {
		request.Method = "SOFTWARE"
	}
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id, "status", "action"), request, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

func (s *Server) Shutdown(hardware bool) (*Server, error) {
	log.Debugf("Requested to shutdown Server '%v'.", s.Id)
	result := new(Server)
	request := ServerAction{}
	request.Action = "POWER_OFF"
	if hardware {
		request.Method = "HARDWARE"
	} else {
		request.Method = "SOFTWARE"
	}
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id, "status", "action"), request, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

func (s *Server) Start() (*Server, error) {
	log.Debugf("Requested to start Server '%v'.", s.Id)
	result := new(Server)
	request := ServerAction{}
	request.Action = "POWER_ON"
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id, "status", "action"), request, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

// GET /servers/{id}/dvd

// DELETE /servers/{id}/dvd

// PUT /servers/{id}/dvd

// GET /servers/{id}/private_networks

// PUT /servers/{id}/private_networks

// GET /servers/{id}/private_networks/{id}

// DELETE /servers/{id}/private_networks/{id}

// GET /servers/{id}/snapshots

// POST /servers/{id}/snapshots

// GET /servers/{id}/snapshots/{id}

// DELETE /servers/{id}/snapshots/{id}

// PUT /servers/{id}/snapshots/{id}

// POST /servers/{server_id}/clone
func (server *Server) Clone(NewName string) Server {
	return Server{}
}
<commit_msg>Implement GetStatus<commit_after>/*
 * Copyright 2015 1&1 Internet AG, http://1und1.de . All rights reserved. Licensed under the Apache v2 License.
 */

package oneandone_cloudserver_api

import (
	"github.com/docker/machine/log"
	"net/http"
)

type Server struct {
	withId
	withName
	withDescription
	Password string        `json:"first_password"`
	Status   Status        `json:"status"`
	Hardware Hardware      `json:"hardware"`
	Image    ImageInServer `json:"image"`
	Ips      []IpInServer  `json:"ips"`
	withApi
}

type Hardware struct {
	Vcores            int   `json:"vcore"`
	CoresPerProcessor int   `json:"cores_per_processor"`
	Ram               int   `json:"ram"`
	Hdds              []Hdd `json:"hdds"`
}

type Hdd struct {
	withId
	Size   int  `json:"size"`
	IsMain bool `json:"is_main"`
}

type ImageInServer struct {
	withId
	withName
}

type IpInServer struct {
	withId
	withType
	Ip         string               `json:"ip"`
	ReverseDns string               `json:"reverse_dns"`
	Firewall   FirewallInIpInServer `json:"firewall_policy"`
}

type FirewallInIpInServer struct {
	withId
	withName
}

type ServerCreateData struct {
	Name               string   `json:"name"`
	Description        string   `json:"description"`
	Hardware           Hardware `json:"hardware"`
	ApplianceId        string   `json:"appliance_id"`
	Password           string   `json:"password"`
	PowerOn            bool     `json:"power_on"`
	FirewallPolicyId   string   `json:"firewall_policy_id"`
	IpId               string   `json:"ip_id"`
	LoadBalancerId     string   `json:"load_balancer_id"`
	MonitoringPolicyId string   `json:"monitoring_policy_id"`
	PrivateNetworkId   string   `json:"private_network_id"`
}

type ServerAction struct {
	Action string `json:"action"`
	Method string `json:"method"`
}

type FixedInstanceInformation struct {
	withName
	withId
	Hardware Hardware `json:"hardware"`
	withApi
}

type ServerRenameData struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

// GET /servers
func (api *API) GetServers() ([]Server, error) {
	log.Debug("requesting information about servers")
	result := []Server{}
	err := api.Client.Get(createUrl(api, "servers"), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	for index, _ := range result {
		result[index].api = api
	}
	return result, nil
}

// POST /servers
func (api *API) CreateServer(configuration ServerCreateData) (*Server, error) {
	log.Debug("requesting to create a new server")
	result := new(Server)
	err := api.Client.Post(createUrl(api, "servers"), &configuration, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = api
	return result, nil
}

// GET /servers/{id}
func (api *API) GetServer(Id string) (*Server, error) {
	log.Debug("requesting information about server ", Id)
	result := new(Server)
	err := api.Client.Get(createUrl(api, "servers", Id), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	result.api = api
	return result, nil
}

func (api *API) GetFixedInstanceSizes() ([]FixedInstanceInformation, error) {
	log.Debug("requesting information about fixed instance sizes")
	result := []FixedInstanceInformation{}
	err := api.Client.Get(createUrl(api, "servers", "fixed_instance_sizes"), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	for index, _ := range result {
		result[index].api = api
	}
	return result, nil
}

// DELETE /servers/{id}
func (s *Server) Delete() (*Server, error) {
	log.Debugf("Requested to delete server '%v' ", s.Id)
	result := new(Server)
	err := s.api.Client.Delete(createUrl(s.api, "servers", s.Id), &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

// PUT /servers/{id}
func (s *Server) RenameServer(data ServerRenameData) (*Server, error) {
	log.Debugf("Requested to rename server '%v'", s.Id)
	result := new(Server)
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id), data, &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

// GET /servers/{id}/size

// PUT /servers/{id}/size

// GET /servers/{id}/size/hdds

// POST /servers/{id}/size/hdds

// GET /servers/{id}/size/hdds/{id}

// DELETE /servers/{id}/size/hdds/{id}

// PUT /servers/{id}/size/hdds/{id}

// GET /servers/{id}/image

// PUT /servers/{id}/image

// GET /servers/{id}/ips

// POST /servers/{id}/ips

// GET /servers/{id}/ips/{id}

// DELETE /servers/{id}/ips/{id}

// PUT /servers/{id}/ips/{id}

// GET /servers/{id}/status
func (s *Server) GetStatus() (*Status, error){
	log.Debugf("Requesting server status for server: '%s'", s.Id)
	result := new(Status)
	err := s.api.Client.Get(createUrl(s.api, "servers", s.Id, "status"), &result, http.StatusOK)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// PUT /servers/{id}/status/action
func (s *Server) Reboot(hardware bool) (*Server, error) {
	log.Debugf("Requested to reboot Server '%v'.", s.Id)
	result := new(Server)
	request := ServerAction{}
	request.Action = "REBOOT"
	if hardware {
		request.Method = "HARDWARE"
	} else {
		request.Method = "SOFTWARE"
	}
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id, "status", "action"), request, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

func (s *Server) Shutdown(hardware bool) (*Server, error) {
	log.Debugf("Requested to shutdown Server '%v'.", s.Id)
	result := new(Server)
	request := ServerAction{}
	request.Action = "POWER_OFF"
	if hardware {
		request.Method = "HARDWARE"
	} else {
		request.Method = "SOFTWARE"
	}
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id, "status", "action"), request, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

func (s *Server) Start() (*Server, error) {
	log.Debugf("Requested to start Server '%v'.", s.Id)
	result := new(Server)
	request := ServerAction{}
	request.Action = "POWER_ON"
	err := s.api.Client.Put(createUrl(s.api, "servers", s.Id, "status", "action"), request, &result, http.StatusAccepted)
	if err != nil {
		return nil, err
	}
	result.api = s.api
	return result, nil
}

// GET /servers/{id}/dvd

// DELETE /servers/{id}/dvd

// PUT /servers/{id}/dvd

// GET /servers/{id}/private_networks

// PUT /servers/{id}/private_networks

// GET /servers/{id}/private_networks/{id}

// DELETE /servers/{id}/private_networks/{id}

// GET /servers/{id}/snapshots

// POST /servers/{id}/snapshots

// GET /servers/{id}/snapshots/{id}

// DELETE /servers/{id}/snapshots/{id}

// PUT /servers/{id}/snapshots/{id}

// POST /servers/{server_id}/clone
func (server *Server) Clone(NewName string) Server {
	return Server{}
}
<|endoftext|>


<commit_before>// Copyright 2014 Mathias Monnerville. All rights reserved.
// Use of this source code is governed by a GPL
// license that can be found in the LICENSE file.

// Package mango is a library for the MangoPay service v2.
//
// MangoPay is a platform that allows to accept payments and manage e-money
// using wallets. See http://www.mangopay.com.
//
// First, create an account with a unique Id to authenticate to the service:
//  conf, err := mango.RegisterClient("myclientid", "My Company", "contact@company.com", mango.Sandbox)
//  if err != nil {
//      panic(err)
//  }
// Or use existing credentials:
//  conf, err := mango.NewConfig("myclientid", "My Company", "contact@company.com", "passwd", "sandbox")
//
// Then, choose an authentication mode (OAuth2.0 or Basic) to use with the service:
//  service, err := mango.NewMangoPay(conf, mango.OAuth)
package mango

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

// Request execution environment (production or sandbox).
type ExecEnvironment int

const (
	Production ExecEnvironment = iota
	Sandbox
)

// Base URLs to execution environements
var rootURLs = map[ExecEnvironment]string{
	Production: "https://api.mangopay.com/v2/",
	Sandbox:    "https://api.sandbox.mangopay.com/v2/",
}

// The Mangopay service.
type MangoPay struct {
	clientId   string // MangoPay partner ID
	password   string
	env        ExecEnvironment // Live or testing env
	rootURL    *url.URL        // Base API URL for the current execution environment
	verbosity  Level
	authMethod AuthMode
	// To track the current token during its lifetime
	oauth *oAuth2
}

// ProcessIdent identifies the current operation.
type ProcessIdent struct {
	Id           string
	Tag          string
	CreationDate int64
}

// ProcessReply holds commong fields part of MangoPay API replies.
type ProcessReply struct {
	ProcessIdent
	Status        string
	ResultCode    string
	ResultMessage string
	ExecutionDate int64
}

// NewMangoPay creates a suitable environment for accessing
// the web service. Default verbosity level is set to Info, which can be
// changed through the use of Option().
func NewMangoPay(auth *Config, mode AuthMode) (*MangoPay, error) {
	if auth == nil {
		return nil, errors.New("nil config")
	}
	if auth.Env == "sandbox" {
		auth.env = Sandbox
	} else if auth.Env == "production" {
		auth.env = Production
	} else {
		return nil, errors.New("unknown exec environment: " + auth.Env)
	}
	u, err := url.Parse(rootURLs[auth.env])
	if err != nil {
		return nil, err
	}
	return &MangoPay{auth.ClientId, auth.Passphrase, auth.env, u, Info, mode, nil}, nil
}

// Option set various options like verbosity etc.
func (m *MangoPay) Option(opts ...option) {
	for _, opt := range opts {
		opt(m)
	}
}

// request prepares and sends a well formatted HTTP request to the
// mangopay service.
func (s *MangoPay) request(ma mangoAction, data JsonObject) (*http.Response, error) {
	mr, ok := mangoRequests[ma]
	if !ok {
		return nil, errors.New("Action not implemented.")
	}

	// Create the submit url
	path := mr.Path
	if mr.PathValues != nil {
		// Substitute path variables, if any
		for name, _ := range mr.PathValues {
			if _, ok := data[name]; !ok {
				return nil, errors.New(fmt.Sprintf("missing keyword %s", name))
			}
			path = strings.Replace(path, "{{"+name+"}}", fmt.Sprintf("%v", data[name]), -1)
		}
	} else {
		path = mr.Path
	}

	body, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	resp, err := s.rawRequest(mr.Method, "application/json",
		fmt.Sprintf("%s%s%s", s.rootURL, s.clientId, path), body, true)
	return resp, err
}

// rawRequest sends an HTTP request with method method to an arbitrary URI.
func (s *MangoPay) rawRequest(method, contentType string, uri string, body []byte, useAuth bool) (*http.Response, error) {
	if contentType == "" {
		return nil, errors.New("empty request's content type")
	}
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, u.String(), strings.NewReader(string(body)))
	if err != nil {
		return nil, err
	}

	// Set header for basic auth
	if useAuth {
		if s.authMethod == BasicAuth {
			req.Header.Set("Authorization", basicAuthorization(s.clientId, s.password))
		} else {
			o, err := oAuthAuthorization(s)
			if err != nil {
				return nil, err
			}
			req.Header.Set("Authorization", o)
		}
	}
	req.Header.Set("Content-Type", contentType)

	if s.verbosity == Debug {
		fmt.Println(">>>>>>>>>>>>>>>>>>>>>> DEBUG REQUEST")
		fmt.Printf("%s %s\n\n", req.Method, req.URL.String())
		for k, v := range req.Header {
			for _, j := range v {
				fmt.Printf("%s: %v\n", k, j)
			}
		}
		rb := string(body)
		if rb != "null" {
			fmt.Printf("\n%s\n", rb)
		}
		fmt.Println("\nSending request ...")
		fmt.Println("<<<<<<<<<<<<<<<<<<<<<< DEBUG REQUEST")
	}

	// Send request
	resp, err := http.DefaultClient.Do(req)

	// Handle reponse status code
	if resp.StatusCode != http.StatusOK {
		j := JsonObject{}
		err = s.unMarshalJSONResponse(resp, &j)
		if err != nil {
			return nil, err
		}
		if msg, ok := j["Message"]; ok {
			err = errors.New(fmt.Sprintf("%s (%d)", msg.(string), resp.StatusCode))
		} else {
			err = errors.New(fmt.Sprintf("HTTP status %d; body: '%s'", resp.StatusCode, j))
		}
	}
	return resp, err
}

// Unmarshal a JSON HTTP response into an instance.
func (m *MangoPay) unMarshalJSONResponse(resp *http.Response, v interface{}) error {
	if resp == nil {
		return errors.New("can't unmarshal nil response")
	}
	b, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return err
	}
	if m.verbosity == Debug {
		fmt.Println(">>>>>>>>>>>>>>>>>>>>>> DEBUG RESPONSE")
		fmt.Printf("Status code: %d\n\n", resp.StatusCode)
		for k, v := range resp.Header {
			for _, j := range v {
				fmt.Printf("%s: %v\n", k, j)
			}
		}
		fmt.Printf("\n%s\n", string(b))
		fmt.Println("<<<<<<<<<<<<<<<<<<<<<< DEBUG RESPONSE")
	}
	if err := json.Unmarshal(b, v); err != nil {
		return err
	}
	return nil
}

// Generic request for any object.
func (m *MangoPay) anyRequest(o interface{}, action mangoAction, data JsonObject) (interface{}, error) {
	resp, err := m.request(action, data)
	if err != nil {
		return nil, err
	}

	t := reflect.TypeOf(o)
	if t.Kind() == reflect.Ptr {
		v := reflect.ValueOf(o)
		t = reflect.Indirect(v).Type()
	}
	ins := reflect.New(t).Interface()
	if err := m.unMarshalJSONResponse(resp, ins); err != nil {
		return nil, err
	}
	return ins, nil
}

func unixTimeToString(t int64) string {
	if t > 0 {
		return time.Unix(t, 0).String()
	}
	return "Never"
}

// Use reflection to print data structures.
func struct2string(c interface{}) string {
	var b bytes.Buffer
	e := reflect.ValueOf(c).Elem()
	for i := 0; i < e.NumField(); i++ {
		sfield := e.Type().Field(i)
		// Skip unexported fields
		if sfield.PkgPath != "" {
			continue
		}
		name := sfield.Name
		val := e.Field(i).Interface()
		// Handle embedded types
		if sfield.Anonymous {
			b.Write([]byte(struct2string(e.Field(i).Addr().Interface())))
		} else {
			if name == "CreationDate" || name == "ExecutionDate" ||
				name == "Birthday" {
				val = unixTimeToString(val.(int64))
			}
			b.Write([]byte(fmt.Sprintf("%-24s: %v\n", name, val)))
		}
	}
	return b.String()
}

func consumerId(c Consumer) string {
	id := ""
	switch c.(type) {
	case *LegalUser:
		id = c.(*LegalUser).Id
	case *NaturalUser:
		id = c.(*NaturalUser).Id
	}
	return id
}
<commit_msg>Better comments formatting<commit_after>// Copyright 2014 Mathias Monnerville. All rights reserved.
// Use of this source code is governed by a GPL
// license that can be found in the LICENSE file.

// Package mango is a library for the MangoPay service v2.
//
// MangoPay is a platform that allows to accept payments and manage e-money
// using wallets. See http://www.mangopay.com.
//
// First, create an account with a unique Id to authenticate to the service:
//  conf, err := mango.RegisterClient("myclientid", "My Company",
//      "contact@company.com", mango.Sandbox)
//  if err != nil {
//      panic(err)
//  }
// Or use existing credentials:
//  conf, err := mango.NewConfig("myclientid", "My Company",
//      "contact@company.com", "passwd", "sandbox")
//
// Then, choose an authentication mode (OAuth2.0 or Basic) to use with the service:
//  service, err := mango.NewMangoPay(conf, mango.OAuth)
package mango

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

// Request execution environment (production or sandbox).
type ExecEnvironment int

const (
	Production ExecEnvironment = iota
	Sandbox
)

// Base URLs to execution environements
var rootURLs = map[ExecEnvironment]string{
	Production: "https://api.mangopay.com/v2/",
	Sandbox:    "https://api.sandbox.mangopay.com/v2/",
}

// The Mangopay service.
type MangoPay struct {
	clientId   string // MangoPay partner ID
	password   string
	env        ExecEnvironment // Live or testing env
	rootURL    *url.URL        // Base API URL for the current execution environment
	verbosity  Level
	authMethod AuthMode
	// To track the current token during its lifetime
	oauth *oAuth2
}

// ProcessIdent identifies the current operation.
type ProcessIdent struct {
	Id           string
	Tag          string
	CreationDate int64
}

// ProcessReply holds commong fields part of MangoPay API replies.
type ProcessReply struct {
	ProcessIdent
	Status        string
	ResultCode    string
	ResultMessage string
	ExecutionDate int64
}

// NewMangoPay creates a suitable environment for accessing
// the web service. Default verbosity level is set to Info, which can be
// changed through the use of Option().
func NewMangoPay(auth *Config, mode AuthMode) (*MangoPay, error) {
	if auth == nil {
		return nil, errors.New("nil config")
	}
	if auth.Env == "sandbox" {
		auth.env = Sandbox
	} else if auth.Env == "production" {
		auth.env = Production
	} else {
		return nil, errors.New("unknown exec environment: " + auth.Env)
	}
	u, err := url.Parse(rootURLs[auth.env])
	if err != nil {
		return nil, err
	}
	return &MangoPay{auth.ClientId, auth.Passphrase, auth.env, u, Info, mode, nil}, nil
}

// Option set various options like verbosity etc.
func (m *MangoPay) Option(opts ...option) {
	for _, opt := range opts {
		opt(m)
	}
}

// request prepares and sends a well formatted HTTP request to the
// mangopay service.
func (s *MangoPay) request(ma mangoAction, data JsonObject) (*http.Response, error) {
	mr, ok := mangoRequests[ma]
	if !ok {
		return nil, errors.New("Action not implemented.")
	}

	// Create the submit url
	path := mr.Path
	if mr.PathValues != nil {
		// Substitute path variables, if any
		for name, _ := range mr.PathValues {
			if _, ok := data[name]; !ok {
				return nil, errors.New(fmt.Sprintf("missing keyword %s", name))
			}
			path = strings.Replace(path, "{{"+name+"}}", fmt.Sprintf("%v", data[name]), -1)
		}
	} else {
		path = mr.Path
	}

	body, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	resp, err := s.rawRequest(mr.Method, "application/json",
		fmt.Sprintf("%s%s%s", s.rootURL, s.clientId, path), body, true)
	return resp, err
}

// rawRequest sends an HTTP request with method method to an arbitrary URI.
func (s *MangoPay) rawRequest(method, contentType string, uri string, body []byte, useAuth bool) (*http.Response, error) {
	if contentType == "" {
		return nil, errors.New("empty request's content type")
	}
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, u.String(), strings.NewReader(string(body)))
	if err != nil {
		return nil, err
	}

	// Set header for basic auth
	if useAuth {
		if s.authMethod == BasicAuth {
			req.Header.Set("Authorization", basicAuthorization(s.clientId, s.password))
		} else {
			o, err := oAuthAuthorization(s)
			if err != nil {
				return nil, err
			}
			req.Header.Set("Authorization", o)
		}
	}
	req.Header.Set("Content-Type", contentType)

	if s.verbosity == Debug {
		fmt.Println(">>>>>>>>>>>>>>>>>>>>>> DEBUG REQUEST")
		fmt.Printf("%s %s\n\n", req.Method, req.URL.String())
		for k, v := range req.Header {
			for _, j := range v {
				fmt.Printf("%s: %v\n", k, j)
			}
		}
		rb := string(body)
		if rb != "null" {
			fmt.Printf("\n%s\n", rb)
		}
		fmt.Println("\nSending request ...")
		fmt.Println("<<<<<<<<<<<<<<<<<<<<<< DEBUG REQUEST")
	}

	// Send request
	resp, err := http.DefaultClient.Do(req)

	// Handle reponse status code
	if resp.StatusCode != http.StatusOK {
		j := JsonObject{}
		err = s.unMarshalJSONResponse(resp, &j)
		if err != nil {
			return nil, err
		}
		if msg, ok := j["Message"]; ok {
			err = errors.New(fmt.Sprintf("%s (%d)", msg.(string), resp.StatusCode))
		} else {
			err = errors.New(fmt.Sprintf("HTTP status %d; body: '%s'", resp.StatusCode, j))
		}
	}
	return resp, err
}

// Unmarshal a JSON HTTP response into an instance.
func (m *MangoPay) unMarshalJSONResponse(resp *http.Response, v interface{}) error {
	if resp == nil {
		return errors.New("can't unmarshal nil response")
	}
	b, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return err
	}
	if m.verbosity == Debug {
		fmt.Println(">>>>>>>>>>>>>>>>>>>>>> DEBUG RESPONSE")
		fmt.Printf("Status code: %d\n\n", resp.StatusCode)
		for k, v := range resp.Header {
			for _, j := range v {
				fmt.Printf("%s: %v\n", k, j)
			}
		}
		fmt.Printf("\n%s\n", string(b))
		fmt.Println("<<<<<<<<<<<<<<<<<<<<<< DEBUG RESPONSE")
	}
	if err := json.Unmarshal(b, v); err != nil {
		return err
	}
	return nil
}

// Generic request for any object.
func (m *MangoPay) anyRequest(o interface{}, action mangoAction, data JsonObject) (interface{}, error) {
	resp, err := m.request(action, data)
	if err != nil {
		return nil, err
	}

	t := reflect.TypeOf(o)
	if t.Kind() == reflect.Ptr {
		v := reflect.ValueOf(o)
		t = reflect.Indirect(v).Type()
	}
	ins := reflect.New(t).Interface()
	if err := m.unMarshalJSONResponse(resp, ins); err != nil {
		return nil, err
	}
	return ins, nil
}

func unixTimeToString(t int64) string {
	if t > 0 {
		return time.Unix(t, 0).String()
	}
	return "Never"
}

// Use reflection to print data structures.
func struct2string(c interface{}) string {
	var b bytes.Buffer
	e := reflect.ValueOf(c).Elem()
	for i := 0; i < e.NumField(); i++ {
		sfield := e.Type().Field(i)
		// Skip unexported fields
		if sfield.PkgPath != "" {
			continue
		}
		name := sfield.Name
		val := e.Field(i).Interface()
		// Handle embedded types
		if sfield.Anonymous {
			b.Write([]byte(struct2string(e.Field(i).Addr().Interface())))
		} else {
			if name == "CreationDate" || name == "ExecutionDate" ||
				name == "Birthday" {
				val = unixTimeToString(val.(int64))
			}
			b.Write([]byte(fmt.Sprintf("%-24s: %v\n", name, val)))
		}
	}
	return b.String()
}

func consumerId(c Consumer) string {
	id := ""
	switch c.(type) {
	case *LegalUser:
		id = c.(*LegalUser).Id
	case *NaturalUser:
		id = c.(*NaturalUser).Id
	}
	return id
}
<|endoftext|>


<commit_before>package log

import (
	"bytes"
	"testing"
)

const (
	infoLevel  = "Infof"
	debugLevel = "Debugf"
	errorLevel = "Errorf"
)

func TestLogger(t *testing.T) {
	for _, tt := range loggerTests() {
		var got string

		lgr := tt.lgr

		switch tt.level {
		case infoLevel:
			lgr.out.Infof(tt.format, tt.args...)
			got = lgr.bufOut.String()
		case debugLevel:
			lgr.out.Debugf(tt.format, tt.args...)
			got = lgr.bufOut.String()
		case errorLevel:
			lgr.out.Errorf(tt.format, tt.args...)
			got = lgr.bufErr.String()
		default:
			t.Errorf("invalid level %q", tt.level)
		}

		if got != tt.want {
			t.Errorf("%v(%q, %v) = %q; want %q",
				tt.level, tt.format, tt.args, got, tt.want)
		}
	}
}

type testLogger struct {
	out    *Log
	bufOut *bytes.Buffer
	bufErr *bytes.Buffer
}

func testNew(verbose bool) testLogger {
	var (
		bufOut bytes.Buffer
		bufErr bytes.Buffer
	)

	l := testLogger{
		out:    New(&bufOut, &bufErr),
		bufOut: &bufOut,
		bufErr: &bufErr,
	}

	if verbose {
		l.out.Verbose()
	}

	return l
}

type loggerTest struct {
	lgr    testLogger
	level  string
	format string
	args   []interface{}
	want   string
}

func loggerTests() []loggerTest {
	const (
		verboseTrue  = true
		verboseFalse = false
	)

	tests := []loggerTest{
		{
			lgr:    testNew(verboseFalse),
			level:  infoLevel,
			format: "%v %v %v",
			args:   []interface{}{"info", 42, 3.14},
			want:   "info 42 3.14\n",
		},
		{
			lgr:    testNew(verboseTrue),
			level:  debugLevel,
			format: "%v %v %v",
			args:   []interface{}{"debug", 42, 3.14},
			want:   "debug 42 3.14\n",
		},
		{
			lgr:    testNew(verboseFalse),
			level:  debugLevel,
			format: "%v %v %v",
			args:   []interface{}{"debug", 42, 3.14},
			want:   "",
		},
		{
			lgr:    testNew(verboseFalse),
			level:  errorLevel,
			format: "%v %v %v",
			args:   []interface{}{"error", 42, 3.14},
			want:   "error 42 3.14\n",
		},
	}

	return tests
}
<commit_msg>rest/internal/log: add verbose to log test<commit_after>package log

import (
	"bytes"
	"testing"
)

const (
	infoLevel  = "Infof"
	debugLevel = "Debugf"
	errorLevel = "Errorf"
)

func TestLogger(t *testing.T) {
	for _, tt := range loggerTests() {
		var got string

		lgr := tt.lgr

		switch tt.level {
		case infoLevel:
			lgr.out.Infof(tt.format, tt.args...)
			got = lgr.bufOut.String()
		case debugLevel:
			lgr.out.Debugf(tt.format, tt.args...)
			got = lgr.bufOut.String()
		case errorLevel:
			lgr.out.Errorf(tt.format, tt.args...)
			got = lgr.bufErr.String()
		default:
			t.Errorf("invalid level %q", tt.level)
		}

		if got != tt.want {
			t.Errorf("%v(%q, %v) = %q; want %q",
				tt.level, tt.format, tt.args, got, tt.want)
		}
	}
}

type testLogger struct {
	out    *Log
	bufOut *bytes.Buffer
	bufErr *bytes.Buffer
}

func testNew(verbose bool) testLogger {
	var (
		bufOut bytes.Buffer
		bufErr bytes.Buffer
	)

	l := testLogger{
		out:    New(&bufOut, &bufErr),
		bufOut: &bufOut,
		bufErr: &bufErr,
	}

	if verbose {
		l.out.Verbose()
	}

	return l
}

type loggerTest struct {
	verbose bool
	lgr     testLogger
	level   string
	format  string
	args    []interface{}
	want    string
}

func loggerTests() []loggerTest {
	const (
		verboseTrue  = true
		verboseFalse = false
	)

	tests := []loggerTest{
		{
			verbose: false,
			lgr:     testNew(verboseFalse),
			level:   infoLevel,
			format:  "%v %v %v",
			args:    []interface{}{"info", 42, 3.14},
			want:    "info 42 3.14\n",
		},
		{
			verbose: true,
			lgr:     testNew(verboseTrue),
			level:   debugLevel,
			format:  "%v %v %v",
			args:    []interface{}{"debug", 42, 3.14},
			want:    "debug 42 3.14\n",
		},
		{
			verbose: false,
			lgr:     testNew(verboseFalse),
			level:   debugLevel,
			format:  "%v %v %v",
			args:    []interface{}{"debug", 42, 3.14},
			want:    "",
		},
		{
			verbose: false,
			lgr:     testNew(verboseFalse),
			level:   errorLevel,
			format:  "%v %v %v",
			args:    []interface{}{"error", 42, 3.14},
			want:    "error 42 3.14\n",
		},
	}

	return tests
}
<|endoftext|>


<commit_before>package smux

import (
	"encoding/binary"
	"io"
	"io/ioutil"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

const (
	defaultAcceptBacklog = 1024
)

const (
	errBrokenPipe      = "broken pipe"
	errInvalidProtocol = "invalid protocol version"
	errGoAway          = "stream id overflows, should start a new connection"
)

type writeRequest struct {
	frame  Frame
	result chan writeResult
}

type writeResult struct {
	n   int
	err error
}

type buffersWriter interface {
	WriteBuffers(v [][]byte) (n int, err error)
}

// Session defines a multiplexed connection for streams
type Session struct {
	conn io.ReadWriteCloser

	config           *Config
	nextStreamID     uint32 // next stream identifier
	nextStreamIDLock sync.Mutex

	bucket       int32         // token bucket
	bucketNotify chan struct{} // used for waiting for tokens

	streams    map[uint32]*Stream // all streams in this session
	streamLock sync.Mutex         // locks streams

	die       chan struct{} // flag session has died
	dieLock   sync.Mutex
	chAccepts chan *Stream

	dataReady int32 // flag data has arrived

	goAway int32 // flag id exhausted

	deadline atomic.Value

	writes chan writeRequest
}

func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
	s := new(Session)
	s.die = make(chan struct{})
	s.conn = conn
	s.config = config
	s.streams = make(map[uint32]*Stream)
	s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
	s.bucket = int32(config.MaxReceiveBuffer)
	s.bucketNotify = make(chan struct{}, 1)
	s.writes = make(chan writeRequest)

	if client {
		s.nextStreamID = 1
	} else {
		s.nextStreamID = 0
	}
	go s.recvLoop()
	go s.sendLoop()
	go s.keepalive()
	return s
}

// OpenStream is used to create a new stream
func (s *Session) OpenStream() (*Stream, error) {
	if s.IsClosed() {
		return nil, errors.New(errBrokenPipe)
	}

	// generate stream id
	s.nextStreamIDLock.Lock()
	if s.goAway > 0 {
		s.nextStreamIDLock.Unlock()
		return nil, errors.New(errGoAway)
	}

	s.nextStreamID += 2
	sid := s.nextStreamID
	if sid == sid%2 { // stream-id overflows
		s.goAway = 1
		s.nextStreamIDLock.Unlock()
		return nil, errors.New(errGoAway)
	}
	s.nextStreamIDLock.Unlock()

	stream := newStream(sid, s.config.MaxFrameSize, s)

	if _, err := s.writeFrame(newFrame(cmdSYN, sid)); err != nil {
		return nil, errors.Wrap(err, "writeFrame")
	}

	s.streamLock.Lock()
	s.streams[sid] = stream
	s.streamLock.Unlock()
	return stream, nil
}

// AcceptStream is used to block until the next available stream
// is ready to be accepted.
func (s *Session) AcceptStream() (*Stream, error) {
	var deadline <-chan time.Time
	if d, ok := s.deadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}
	select {
	case stream := <-s.chAccepts:
		return stream, nil
	case <-deadline:
		return nil, errTimeout
	case <-s.die:
		return nil, errors.New(errBrokenPipe)
	}
}

// Close is used to close the session and all streams.
func (s *Session) Close() (err error) {
	s.dieLock.Lock()

	select {
	case <-s.die:
		s.dieLock.Unlock()
		return errors.New(errBrokenPipe)
	default:
		close(s.die)
		err = s.conn.Close()
		s.dieLock.Unlock()
		s.streamLock.Lock()
		for k := range s.streams {
			s.streams[k].sessionClose()
		}
		s.streamLock.Unlock()
		return
	}
}

// notifyBucket notifies recvLoop that bucket is available
func (s *Session) notifyBucket() {
	select {
	case s.bucketNotify <- struct{}{}:
	default:
	}
}

// IsClosed does a safe check to see if we have shutdown
func (s *Session) IsClosed() bool {
	select {
	case <-s.die:
		return true
	default:
		return false
	}
}

// NumStreams returns the number of currently open streams
func (s *Session) NumStreams() int {
	if s.IsClosed() {
		return 0
	}
	s.streamLock.Lock()
	defer s.streamLock.Unlock()
	return len(s.streams)
}

// SetDeadline sets a deadline used by Accept* calls.
// A zero time value disables the deadline.
func (s *Session) SetDeadline(t time.Time) error {
	s.deadline.Store(t)
	return nil
}

// notify the session that a stream has closed
func (s *Session) streamClosed(sid uint32) {
	s.streamLock.Lock()
	if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
		if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
			s.notifyBucket()
		}
	}
	delete(s.streams, sid)
	s.streamLock.Unlock()
}

// returnTokens is called by stream to return token after read
func (s *Session) returnTokens(n int) {
	if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
		s.notifyBucket()
	}
}

// recvLoop keeps on reading from underlying connection if tokens are available
func (s *Session) recvLoop() {
	var hdr rawHeader

	for {
		for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
			select {
			case <-s.bucketNotify:
			case <-s.die:
				return
			}
		}

		// read header first
		if _, err := io.ReadFull(s.conn, hdr[:]); err == nil {
			if hdr.Version() != version { // just ignore
				continue
			}
			atomic.StoreInt32(&s.dataReady, 1)
			sid := hdr.StreamID()
			switch hdr.Cmd() {
			case cmdNOP:
			case cmdSYN:
				s.streamLock.Lock()
				if _, ok := s.streams[sid]; !ok {
					stream := newStream(sid, s.config.MaxFrameSize, s)
					s.streams[sid] = stream
					select {
					case s.chAccepts <- stream:
					case <-s.die:
					}
				}
				s.streamLock.Unlock()
			case cmdFIN:
				s.streamLock.Lock()
				if stream, ok := s.streams[sid]; ok {
					stream.markRST()
					stream.notifyReadEvent()
				}
				s.streamLock.Unlock()
			case cmdPSH:
				var written int64
				var err error
				s.streamLock.Lock()
				if stream, ok := s.streams[sid]; ok {
					written, err = stream.receiveBytes(s.conn, int64(hdr.Length()))
					atomic.AddInt32(&s.bucket, -int32(written))
					stream.notifyReadEvent()
				} else { // discard
					written, err = io.CopyN(ioutil.Discard, s.conn, int64(hdr.Length()))
				}
				s.streamLock.Unlock()

				// read data error
				if err != nil {
					s.Close()
					return
				}
			default:
				s.Close()
				return
			}
		} else {
			s.Close()
			return
		}
	}
}

func (s *Session) keepalive() {
	tickerPing := time.NewTicker(s.config.KeepAliveInterval)
	tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
	defer tickerPing.Stop()
	defer tickerTimeout.Stop()
	for {
		select {
		case <-tickerPing.C:
			s.writeFrameInternal(newFrame(cmdNOP, 0), tickerPing.C)
			s.notifyBucket() // force a signal to the recvLoop
		case <-tickerTimeout.C:
			if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
				s.Close()
				return
			}
		case <-s.die:
			return
		}
	}
}

func (s *Session) sendLoop() {
	buf := make([]byte, (1<<16)+headerSize)
	var n int
	var err error
	v := make([][]byte, 2) // vector for writing

	for {
		select {
		case <-s.die:
			return
		case request := <-s.writes:
			buf[0] = request.frame.ver
			buf[1] = request.frame.cmd
			binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
			binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)

			if bw, ok := s.conn.(buffersWriter); ok {
				v[0] = buf[:headerSize]
				v[1] = request.frame.data
				n, err = bw.WriteBuffers(v)
			} else {
				copy(buf[headerSize:], request.frame.data)
				n, err = s.conn.Write(buf[:headerSize+len(request.frame.data)])
			}

			n -= headerSize
			if n < 0 {
				n = 0
			}

			result := writeResult{
				n:   n,
				err: err,
			}

			request.result <- result
			close(request.result)
		}
	}
}

// writeFrame writes the frame to the underlying connection
// and returns the number of bytes written if successful
func (s *Session) writeFrame(f Frame) (n int, err error) {
	return s.writeFrameInternal(f, nil)
}

// internal writeFrame version to support deadline used in keepalive
func (s *Session) writeFrameInternal(f Frame, deadline <-chan time.Time) (int, error) {
	req := writeRequest{
		frame:  f,
		result: make(chan writeResult, 1),
	}
	select {
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	case s.writes <- req:
	case <-deadline:
		return 0, errTimeout
	}

	select {
	case result := <-req.result:
		return result.n, result.err
	case <-deadline:
		return 0, errTimeout
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	}
}
<commit_msg>fix a potential logic race<commit_after>package smux

import (
	"encoding/binary"
	"io"
	"io/ioutil"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pkg/errors"
)

const (
	defaultAcceptBacklog = 1024
)

const (
	errBrokenPipe      = "broken pipe"
	errInvalidProtocol = "invalid protocol version"
	errGoAway          = "stream id overflows, should start a new connection"
)

type writeRequest struct {
	frame  Frame
	result chan writeResult
}

type writeResult struct {
	n   int
	err error
}

type buffersWriter interface {
	WriteBuffers(v [][]byte) (n int, err error)
}

// Session defines a multiplexed connection for streams
type Session struct {
	conn io.ReadWriteCloser

	config           *Config
	nextStreamID     uint32 // next stream identifier
	nextStreamIDLock sync.Mutex

	bucket       int32         // token bucket
	bucketNotify chan struct{} // used for waiting for tokens

	streams    map[uint32]*Stream // all streams in this session
	streamLock sync.Mutex         // locks streams

	die       chan struct{} // flag session has died
	dieLock   sync.Mutex
	chAccepts chan *Stream

	dataReady int32 // flag data has arrived

	goAway int32 // flag id exhausted

	deadline atomic.Value

	writes chan writeRequest
}

func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
	s := new(Session)
	s.die = make(chan struct{})
	s.conn = conn
	s.config = config
	s.streams = make(map[uint32]*Stream)
	s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
	s.bucket = int32(config.MaxReceiveBuffer)
	s.bucketNotify = make(chan struct{}, 1)
	s.writes = make(chan writeRequest)

	if client {
		s.nextStreamID = 1
	} else {
		s.nextStreamID = 0
	}
	go s.recvLoop()
	go s.sendLoop()
	go s.keepalive()
	return s
}

// OpenStream is used to create a new stream
func (s *Session) OpenStream() (*Stream, error) {
	if s.IsClosed() {
		return nil, errors.New(errBrokenPipe)
	}

	// generate stream id
	s.nextStreamIDLock.Lock()
	if s.goAway > 0 {
		s.nextStreamIDLock.Unlock()
		return nil, errors.New(errGoAway)
	}

	s.nextStreamID += 2
	sid := s.nextStreamID
	if sid == sid%2 { // stream-id overflows
		s.goAway = 1
		s.nextStreamIDLock.Unlock()
		return nil, errors.New(errGoAway)
	}
	s.nextStreamIDLock.Unlock()

	stream := newStream(sid, s.config.MaxFrameSize, s)

	if _, err := s.writeFrame(newFrame(cmdSYN, sid)); err != nil {
		return nil, errors.Wrap(err, "writeFrame")
	}

	s.streamLock.Lock()
	defer s.streamLock.Unlock()
	select {
	case <-s.die:
		return nil, errors.New(errBrokenPipe)
	default:
		s.streams[sid] = stream
		return stream, nil
	}
}

// AcceptStream is used to block until the next available stream
// is ready to be accepted.
func (s *Session) AcceptStream() (*Stream, error) {
	var deadline <-chan time.Time
	if d, ok := s.deadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}
	select {
	case stream := <-s.chAccepts:
		return stream, nil
	case <-deadline:
		return nil, errTimeout
	case <-s.die:
		return nil, errors.New(errBrokenPipe)
	}
}

// Close is used to close the session and all streams.
func (s *Session) Close() (err error) {
	s.dieLock.Lock()

	select {
	case <-s.die:
		s.dieLock.Unlock()
		return errors.New(errBrokenPipe)
	default:
		close(s.die)
		err = s.conn.Close()
		s.dieLock.Unlock()
		s.streamLock.Lock()
		for k := range s.streams {
			s.streams[k].sessionClose()
		}
		s.streamLock.Unlock()
		return
	}
}

// notifyBucket notifies recvLoop that bucket is available
func (s *Session) notifyBucket() {
	select {
	case s.bucketNotify <- struct{}{}:
	default:
	}
}

// IsClosed does a safe check to see if we have shutdown
func (s *Session) IsClosed() bool {
	select {
	case <-s.die:
		return true
	default:
		return false
	}
}

// NumStreams returns the number of currently open streams
func (s *Session) NumStreams() int {
	if s.IsClosed() {
		return 0
	}
	s.streamLock.Lock()
	defer s.streamLock.Unlock()
	return len(s.streams)
}

// SetDeadline sets a deadline used by Accept* calls.
// A zero time value disables the deadline.
func (s *Session) SetDeadline(t time.Time) error {
	s.deadline.Store(t)
	return nil
}

// notify the session that a stream has closed
func (s *Session) streamClosed(sid uint32) {
	s.streamLock.Lock()
	if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
		if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
			s.notifyBucket()
		}
	}
	delete(s.streams, sid)
	s.streamLock.Unlock()
}

// returnTokens is called by stream to return token after read
func (s *Session) returnTokens(n int) {
	if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
		s.notifyBucket()
	}
}

// recvLoop keeps on reading from underlying connection if tokens are available
func (s *Session) recvLoop() {
	var hdr rawHeader

	for {
		for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
			select {
			case <-s.bucketNotify:
			case <-s.die:
				return
			}
		}

		// read header first
		if _, err := io.ReadFull(s.conn, hdr[:]); err == nil {
			if hdr.Version() != version { // just ignore
				continue
			}
			atomic.StoreInt32(&s.dataReady, 1)
			sid := hdr.StreamID()
			switch hdr.Cmd() {
			case cmdNOP:
			case cmdSYN:
				s.streamLock.Lock()
				if _, ok := s.streams[sid]; !ok {
					stream := newStream(sid, s.config.MaxFrameSize, s)
					s.streams[sid] = stream
					select {
					case s.chAccepts <- stream:
					case <-s.die:
					}
				}
				s.streamLock.Unlock()
			case cmdFIN:
				s.streamLock.Lock()
				if stream, ok := s.streams[sid]; ok {
					stream.markRST()
					stream.notifyReadEvent()
				}
				s.streamLock.Unlock()
			case cmdPSH:
				var written int64
				var err error
				s.streamLock.Lock()
				if stream, ok := s.streams[sid]; ok {
					written, err = stream.receiveBytes(s.conn, int64(hdr.Length()))
					atomic.AddInt32(&s.bucket, -int32(written))
					stream.notifyReadEvent()
				} else { // discard
					written, err = io.CopyN(ioutil.Discard, s.conn, int64(hdr.Length()))
				}
				s.streamLock.Unlock()

				// read data error
				if err != nil {
					s.Close()
					return
				}
			default:
				s.Close()
				return
			}
		} else {
			s.Close()
			return
		}
	}
}

func (s *Session) keepalive() {
	tickerPing := time.NewTicker(s.config.KeepAliveInterval)
	tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
	defer tickerPing.Stop()
	defer tickerTimeout.Stop()
	for {
		select {
		case <-tickerPing.C:
			s.writeFrameInternal(newFrame(cmdNOP, 0), tickerPing.C)
			s.notifyBucket() // force a signal to the recvLoop
		case <-tickerTimeout.C:
			if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
				s.Close()
				return
			}
		case <-s.die:
			return
		}
	}
}

func (s *Session) sendLoop() {
	buf := make([]byte, (1<<16)+headerSize)
	var n int
	var err error
	v := make([][]byte, 2) // vector for writing

	for {
		select {
		case <-s.die:
			return
		case request := <-s.writes:
			buf[0] = request.frame.ver
			buf[1] = request.frame.cmd
			binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
			binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)

			if bw, ok := s.conn.(buffersWriter); ok {
				v[0] = buf[:headerSize]
				v[1] = request.frame.data
				n, err = bw.WriteBuffers(v)
			} else {
				copy(buf[headerSize:], request.frame.data)
				n, err = s.conn.Write(buf[:headerSize+len(request.frame.data)])
			}

			n -= headerSize
			if n < 0 {
				n = 0
			}

			result := writeResult{
				n:   n,
				err: err,
			}

			request.result <- result
			close(request.result)
		}
	}
}

// writeFrame writes the frame to the underlying connection
// and returns the number of bytes written if successful
func (s *Session) writeFrame(f Frame) (n int, err error) {
	return s.writeFrameInternal(f, nil)
}

// internal writeFrame version to support deadline used in keepalive
func (s *Session) writeFrameInternal(f Frame, deadline <-chan time.Time) (int, error) {
	req := writeRequest{
		frame:  f,
		result: make(chan writeResult, 1),
	}
	select {
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	case s.writes <- req:
	case <-deadline:
		return 0, errTimeout
	}

	select {
	case result := <-req.result:
		return result.n, result.err
	case <-deadline:
		return 0, errTimeout
	case <-s.die:
		return 0, errors.New(errBrokenPipe)
	}
}
<|endoftext|>


<commit_before>package webdriver

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
)

func (wds *Session) Close() (*Session, error) {
	req, err := wds.wd.NewRequest("DELETE", fmt.Sprintf("/wd/hub/session/%s", wds.SessionId), nil)
	if err != nil {
		return nil, err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return nil, err
	}

	return wds, nil

}

func (wds *Session) Source() (io.Reader, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/source", wds.SessionId), nil)
	if err != nil {
		return nil, err
	}

	source := &bytes.Buffer{}
	if err := wds.wd.Do(req, source); err != nil {
		return nil, err
	}

	return source, nil
}

func (wds *Session) Screenshot() (io.Reader, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/screenshot", wds.SessionId), nil)
	if err != nil {
		return nil, err
	}

	image := &bytes.Buffer{}

	if err := wds.wd.Do(req, image); err != nil {
		return nil, err
	}

	r := base64.NewDecoder(base64.StdEncoding, image)
	return r, nil
}

func (wds *Session) SetWindowSize(width, height int) (*Session, error) {
	body := struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	}{
		Width:  width,
		Height: height,
	}

	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/window/current/size", wds.SessionId), body)
	if err != nil {
		return nil, err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return nil, err
	}

	return wds, nil

}

func (wds *Session) Back() error {
	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/back", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, nil); err != nil {
		return err
	}

	return nil
}

func (wds *Session) Forward() error {
	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/forward", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil
}

func (wds *Session) Refresh() error {
	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/refresh", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil
}

func (wds *Session) Title() (string, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/title", wds.SessionId), nil)
	if err != nil {
		return "", err
	}

	u := ""
	if err := wds.wd.Do(req, &u); err != nil {
		return "", err
	}

	return u, nil
}

func (wds *Session) Url() (string, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/url", wds.SessionId), nil)
	if err != nil {
		return "", err
	}

	u := ""
	if err := wds.wd.Do(req, &u); err != nil {
		return "", err
	}

	return u, nil
}

func (wds *Session) SetUrl(url string) error {
	body := struct {
		Url string `json:"url"`
	}{
		Url: url,
	}

	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/url", wds.SessionId), body)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil
}
<commit_msg>removed session returns<commit_after>package webdriver

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
)

func (wds *Session) Close() error {
	req, err := wds.wd.NewRequest("DELETE", fmt.Sprintf("/wd/hub/session/%s", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil

}

func (wds *Session) Source() (io.Reader, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/source", wds.SessionId), nil)
	if err != nil {
		return nil, err
	}

	source := &bytes.Buffer{}
	if err := wds.wd.Do(req, source); err != nil {
		return nil, err
	}

	return source, nil
}

func (wds *Session) Screenshot() (io.Reader, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/screenshot", wds.SessionId), nil)
	if err != nil {
		return nil, err
	}

	image := &bytes.Buffer{}

	if err := wds.wd.Do(req, image); err != nil {
		return nil, err
	}

	r := base64.NewDecoder(base64.StdEncoding, image)
	return r, nil
}

func (wds *Session) SetWindowSize(width, height int) error {
	body := struct {
		Width  int `json:"width"`
		Height int `json:"height"`
	}{
		Width:  width,
		Height: height,
	}

	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/window/current/size", wds.SessionId), body)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil

}

func (wds *Session) Back() error {
	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/back", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, nil); err != nil {
		return err
	}

	return nil
}

func (wds *Session) Forward() error {
	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/forward", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil
}

func (wds *Session) Refresh() error {
	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/refresh", wds.SessionId), nil)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil
}

func (wds *Session) Title() (string, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/title", wds.SessionId), nil)
	if err != nil {
		return "", err
	}

	u := ""
	if err := wds.wd.Do(req, &u); err != nil {
		return "", err
	}

	return u, nil
}

func (wds *Session) Url() (string, error) {
	req, err := wds.wd.NewRequest("GET", fmt.Sprintf("/wd/hub/session/%s/url", wds.SessionId), nil)
	if err != nil {
		return "", err
	}

	u := ""
	if err := wds.wd.Do(req, &u); err != nil {
		return "", err
	}

	return u, nil
}

func (wds *Session) SetUrl(url string) error {
	body := struct {
		Url string `json:"url"`
	}{
		Url: url,
	}

	req, err := wds.wd.NewRequest("POST", fmt.Sprintf("/wd/hub/session/%s/url", wds.SessionId), body)
	if err != nil {
		return err
	}

	if err := wds.wd.Do(req, &wds); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package gorethink

import (
	"sync"
	"sync/atomic"
	"time"

	"gopkg.in/fatih/pool.v1"

	p "github.com/dancannon/gorethink/ql2"
)

type Query struct {
	Type       p.Query_QueryType
	Token      int64
	Term       *Term
	GlobalOpts map[string]interface{}
}

func (q *Query) build() []interface{} {
	res := []interface{}{q.Type}
	if q.Term != nil {
		res = append(res, q.Term.build())

		if len(q.GlobalOpts) > 0 {
			res = append(res, q.GlobalOpts)
		}
	}

	return res
}

type Session struct {
	token      int64
	address    string
	database   string
	timeout    time.Duration
	authkey    string
	timeFormat string

	// Pool configuration options
	initialCap  int
	maxCap      int
	idleTimeout time.Duration

	// Response cache, used for batched responses
	sync.Mutex
	cache map[int64]*Cursor

	closed bool

	pool pool.Pool
}

func newSession(args map[string]interface{}) *Session {
	s := &Session{
		cache: map[int64]*Cursor{},
	}

	if token, ok := args["token"]; ok {
		s.token = token.(int64)
	}
	if address, ok := args["address"]; ok {
		s.address = address.(string)
	}
	if database, ok := args["database"]; ok {
		s.database = database.(string)
	}
	if timeout, ok := args["timeout"]; ok {
		s.timeout = timeout.(time.Duration)
	}
	if authkey, ok := args["authkey"]; ok {
		s.authkey = authkey.(string)
	}

	// Pool configuration options
	if initialCap, ok := args["initialCap"]; ok {
		s.initialCap = initialCap.(int)
	} else {
		s.initialCap = 5
	}
	if maxCap, ok := args["maxCap"]; ok {
		s.maxCap = maxCap.(int)
	} else {
		s.maxCap = 30
	}
	if idleTimeout, ok := args["idleTimeout"]; ok {
		s.idleTimeout = idleTimeout.(time.Duration)
	} else {
		s.idleTimeout = 10 * time.Second
	}

	return s
}

type ConnectOpts struct {
	Token       int64         `gorethink:"token,omitempty"`
	Address     string        `gorethink:"address,omitempty"`
	Database    string        `gorethink:"database,omitempty"`
	Timeout     time.Duration `gorethink:"timeout,omitempty"`
	AuthKey     string        `gorethink:"authkey,omitempty"`
	MaxIdle     int           `gorethink:"max_idle,omitempty"`
	MaxActive   int           `gorethink:"max_active,omitempty"`
	IdleTimeout time.Duration `gorethink:"idle_timeout,omitempty"`
}

func (o *ConnectOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Connect creates a new database session.
//
// Supported arguments include token, address, database, timeout, authkey,
// and timeFormat. Pool options include maxIdle, maxActive and idleTimeout.
//
// By default maxIdle and maxActive are set to 1: passing values greater
// than the default (e.g. maxIdle: "10", maxActive: "20") will provide a
// pool of re-usable connections.
//
// Basic connection example:
//
//	var session *r.Session
// 	session, err := r.Connect(r.ConnectOpts{
// 		Address:  "localhost:28015",
// 		Database: "test",
// 		AuthKey:  "14daak1cad13dj",
// 	})
func Connect(args ConnectOpts) (*Session, error) {
	s := newSession(args.toMap())
	err := s.Reconnect()

	return s, err
}

type CloseOpts struct {
	NoReplyWait bool `gorethink:"noreplyWait,omitempty"`
}

func (o *CloseOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Reconnect closes and re-opens a session.
func (s *Session) Reconnect(optArgs ...CloseOpts) error {
	if err := s.Close(optArgs...); err != nil {
		return err
	}

	s.closed = false
	if s.pool == nil {
		cp, err := pool.NewChannelPool(s.initialCap, s.maxCap, Dial(s))
		s.pool = cp
		if err != nil {
			return err
		}

		s.pool = cp
	}

	// Check the connection
	_, err := s.getConn()

	return err
}

// Close closes the session
func (s *Session) Close(optArgs ...CloseOpts) error {
	if s.closed {
		return nil
	}

	if len(optArgs) >= 1 {
		if optArgs[0].NoReplyWait {
			s.NoReplyWait()
		}
	}

	if s.pool != nil {
		s.pool.Close()
	}
	s.closed = true

	return nil
}

// noreplyWait ensures that previous queries with the noreply flag have been
// processed by the server. Note that this guarantee only applies to queries
// run on the given connection
func (s *Session) NoReplyWait() {
	s.noreplyWaitQuery()
}

// Use changes the default database used
func (s *Session) Use(database string) {
	s.database = database
}

// SetTimeout causes any future queries that are run on this session to timeout
// after the given duration, returning a timeout error.  Set to zero to disable.
func (s *Session) SetTimeout(timeout time.Duration) {
	s.timeout = timeout
}

// getToken generates the next query token, used to number requests and match
// responses with requests.
func (s *Session) nextToken() int64 {
	return atomic.AddInt64(&s.token, 1)
}

// startQuery creates a query from the term given and sends it to the server.
// The result from the server is returned as a cursor
func (s *Session) startQuery(t Term, opts map[string]interface{}) (*Cursor, error) {
	token := s.nextToken()

	// Build global options
	globalOpts := map[string]interface{}{}
	for k, v := range opts {
		globalOpts[k] = Expr(v).build()
	}

	// If no DB option was set default to the value set in the connection
	if _, ok := opts["db"]; !ok {
		globalOpts["db"] = Db(s.database).build()
	}

	// Construct query
	q := Query{
		Type:       p.Query_START,
		Token:      token,
		Term:       &t,
		GlobalOpts: globalOpts,
	}

	// Get a connection from the pool, do not close yet as it
	// might be needed later if a partial response is returned
	conn, err := s.getConn()
	if err != nil {
		return nil, err
	}

	return conn.SendQuery(s, q, opts, false)
}

func (s *Session) handleBatchResponse(cursor *Cursor, response *Response) {
	cursor.extend(response)

	s.Lock()
	cursor.outstandingRequests -= 1

	if response.Type != p.Response_SUCCESS_PARTIAL &&
		response.Type != p.Response_SUCCESS_FEED &&
		cursor.outstandingRequests == 0 {
		delete(s.cache, response.Token)
	}
	s.Unlock()
}

// continueQuery continues a previously run query.
// This is needed if a response is batched.
func (s *Session) continueQuery(cursor *Cursor) error {
	err := s.asyncContinueQuery(cursor)
	if err != nil {
		return err
	}

	response, err := cursor.conn.ReadResponse(s, cursor.query.Token)
	if err != nil {
		return err
	}

	s.handleBatchResponse(cursor, response)

	return nil
}

// asyncContinueQuery asynchronously continues a previously run query.
// This is needed if a response is batched.
func (s *Session) asyncContinueQuery(cursor *Cursor) error {
	s.Lock()
	if cursor.outstandingRequests != 0 {

		s.Unlock()
		return nil
	}
	cursor.outstandingRequests = 1
	s.Unlock()

	q := Query{
		Type:  p.Query_CONTINUE,
		Token: cursor.query.Token,
	}

	_, err := cursor.conn.SendQuery(s, q, cursor.opts, true)
	if err != nil {
		return err
	}

	return nil
}

// stopQuery sends closes a query by sending Query_STOP to the server.
func (s *Session) stopQuery(cursor *Cursor) error {
	cursor.mu.Lock()
	cursor.outstandingRequests += 1
	cursor.mu.Unlock()

	q := Query{
		Type:  p.Query_STOP,
		Token: cursor.query.Token,
		Term:  &cursor.term,
	}

	_, err := cursor.conn.SendQuery(s, q, cursor.opts, false)
	if err != nil {
		return err
	}

	response, err := cursor.conn.ReadResponse(s, cursor.query.Token)
	if err != nil {
		return err
	}

	s.handleBatchResponse(cursor, response)

	return nil
}

// noreplyWaitQuery sends the NOREPLY_WAIT query to the server.
func (s *Session) noreplyWaitQuery() error {
	q := Query{
		Type:  p.Query_NOREPLY_WAIT,
		Token: s.nextToken(),
	}

	conn, err := s.getConn()
	if err != nil {
		return err
	}
	defer conn.CloseNoWait()

	_, err = conn.SendQuery(s, q, map[string]interface{}{}, false)

	err = conn.Close()
	if err != nil {
		return err
	}

	return nil
}
func (s *Session) getConn() (Connection, error) {
	c, err := s.pool.Get()
	if err != nil {
		return Connection{}, err
	}

	return Connection{Conn: c, s: s}, nil
}

func (s *Session) checkCache(token int64) (*Cursor, bool) {
	s.Lock()
	defer s.Unlock()

	cursor, ok := s.cache[token]
	return cursor, ok
}

func (s *Session) setCache(token int64, cursor *Cursor) {
	s.Lock()
	defer s.Unlock()

	s.cache[token] = cursor
}
<commit_msg>Check if pool is closed when getting a connection<commit_after>package gorethink

import (
	"sync"
	"sync/atomic"
	"time"

	"gopkg.in/fatih/pool.v1"

	p "github.com/dancannon/gorethink/ql2"
)

type Query struct {
	Type       p.Query_QueryType
	Token      int64
	Term       *Term
	GlobalOpts map[string]interface{}
}

func (q *Query) build() []interface{} {
	res := []interface{}{q.Type}
	if q.Term != nil {
		res = append(res, q.Term.build())

		if len(q.GlobalOpts) > 0 {
			res = append(res, q.GlobalOpts)
		}
	}

	return res
}

type Session struct {
	token      int64
	address    string
	database   string
	timeout    time.Duration
	authkey    string
	timeFormat string

	// Pool configuration options
	initialCap  int
	maxCap      int
	idleTimeout time.Duration

	// Response cache, used for batched responses
	sync.Mutex
	cache map[int64]*Cursor

	closed bool

	pool pool.Pool
}

func newSession(args map[string]interface{}) *Session {
	s := &Session{
		cache: map[int64]*Cursor{},
	}

	if token, ok := args["token"]; ok {
		s.token = token.(int64)
	}
	if address, ok := args["address"]; ok {
		s.address = address.(string)
	}
	if database, ok := args["database"]; ok {
		s.database = database.(string)
	}
	if timeout, ok := args["timeout"]; ok {
		s.timeout = timeout.(time.Duration)
	}
	if authkey, ok := args["authkey"]; ok {
		s.authkey = authkey.(string)
	}

	// Pool configuration options
	if initialCap, ok := args["initialCap"]; ok {
		s.initialCap = initialCap.(int)
	} else {
		s.initialCap = 5
	}
	if maxCap, ok := args["maxCap"]; ok {
		s.maxCap = maxCap.(int)
	} else {
		s.maxCap = 30
	}
	if idleTimeout, ok := args["idleTimeout"]; ok {
		s.idleTimeout = idleTimeout.(time.Duration)
	} else {
		s.idleTimeout = 10 * time.Second
	}

	return s
}

type ConnectOpts struct {
	Token       int64         `gorethink:"token,omitempty"`
	Address     string        `gorethink:"address,omitempty"`
	Database    string        `gorethink:"database,omitempty"`
	Timeout     time.Duration `gorethink:"timeout,omitempty"`
	AuthKey     string        `gorethink:"authkey,omitempty"`
	MaxIdle     int           `gorethink:"max_idle,omitempty"`
	MaxActive   int           `gorethink:"max_active,omitempty"`
	IdleTimeout time.Duration `gorethink:"idle_timeout,omitempty"`
}

func (o *ConnectOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Connect creates a new database session.
//
// Supported arguments include token, address, database, timeout, authkey,
// and timeFormat. Pool options include maxIdle, maxActive and idleTimeout.
//
// By default maxIdle and maxActive are set to 1: passing values greater
// than the default (e.g. maxIdle: "10", maxActive: "20") will provide a
// pool of re-usable connections.
//
// Basic connection example:
//
//	var session *r.Session
// 	session, err := r.Connect(r.ConnectOpts{
// 		Address:  "localhost:28015",
// 		Database: "test",
// 		AuthKey:  "14daak1cad13dj",
// 	})
func Connect(args ConnectOpts) (*Session, error) {
	s := newSession(args.toMap())
	err := s.Reconnect()

	return s, err
}

type CloseOpts struct {
	NoReplyWait bool `gorethink:"noreplyWait,omitempty"`
}

func (o *CloseOpts) toMap() map[string]interface{} {
	return optArgsToMap(o)
}

// Reconnect closes and re-opens a session.
func (s *Session) Reconnect(optArgs ...CloseOpts) error {
	if err := s.Close(optArgs...); err != nil {
		return err
	}

	s.closed = false
	if s.pool == nil {
		cp, err := pool.NewChannelPool(s.initialCap, s.maxCap, Dial(s))
		s.pool = cp
		if err != nil {
			return err
		}

		s.pool = cp
	}

	// Check the connection
	_, err := s.getConn()

	return err
}

// Close closes the session
func (s *Session) Close(optArgs ...CloseOpts) error {
	if s.closed {
		return nil
	}

	if len(optArgs) >= 1 {
		if optArgs[0].NoReplyWait {
			s.NoReplyWait()
		}
	}

	if s.pool != nil {
		s.pool.Close()
	}
	s.closed = true

	return nil
}

// noreplyWait ensures that previous queries with the noreply flag have been
// processed by the server. Note that this guarantee only applies to queries
// run on the given connection
func (s *Session) NoReplyWait() {
	s.noreplyWaitQuery()
}

// Use changes the default database used
func (s *Session) Use(database string) {
	s.database = database
}

// SetTimeout causes any future queries that are run on this session to timeout
// after the given duration, returning a timeout error.  Set to zero to disable.
func (s *Session) SetTimeout(timeout time.Duration) {
	s.timeout = timeout
}

// getToken generates the next query token, used to number requests and match
// responses with requests.
func (s *Session) nextToken() int64 {
	return atomic.AddInt64(&s.token, 1)
}

// startQuery creates a query from the term given and sends it to the server.
// The result from the server is returned as a cursor
func (s *Session) startQuery(t Term, opts map[string]interface{}) (*Cursor, error) {
	token := s.nextToken()

	// Build global options
	globalOpts := map[string]interface{}{}
	for k, v := range opts {
		globalOpts[k] = Expr(v).build()
	}

	// If no DB option was set default to the value set in the connection
	if _, ok := opts["db"]; !ok {
		globalOpts["db"] = Db(s.database).build()
	}

	// Construct query
	q := Query{
		Type:       p.Query_START,
		Token:      token,
		Term:       &t,
		GlobalOpts: globalOpts,
	}

	// Get a connection from the pool, do not close yet as it
	// might be needed later if a partial response is returned
	conn, err := s.getConn()
	if err != nil {
		return nil, err
	}

	return conn.SendQuery(s, q, opts, false)
}

func (s *Session) handleBatchResponse(cursor *Cursor, response *Response) {
	cursor.extend(response)

	s.Lock()
	cursor.outstandingRequests -= 1

	if response.Type != p.Response_SUCCESS_PARTIAL &&
		response.Type != p.Response_SUCCESS_FEED &&
		cursor.outstandingRequests == 0 {
		delete(s.cache, response.Token)
	}
	s.Unlock()
}

// continueQuery continues a previously run query.
// This is needed if a response is batched.
func (s *Session) continueQuery(cursor *Cursor) error {
	err := s.asyncContinueQuery(cursor)
	if err != nil {
		return err
	}

	response, err := cursor.conn.ReadResponse(s, cursor.query.Token)
	if err != nil {
		return err
	}

	s.handleBatchResponse(cursor, response)

	return nil
}

// asyncContinueQuery asynchronously continues a previously run query.
// This is needed if a response is batched.
func (s *Session) asyncContinueQuery(cursor *Cursor) error {
	s.Lock()
	if cursor.outstandingRequests != 0 {

		s.Unlock()
		return nil
	}
	cursor.outstandingRequests = 1
	s.Unlock()

	q := Query{
		Type:  p.Query_CONTINUE,
		Token: cursor.query.Token,
	}

	_, err := cursor.conn.SendQuery(s, q, cursor.opts, true)
	if err != nil {
		return err
	}

	return nil
}

// stopQuery sends closes a query by sending Query_STOP to the server.
func (s *Session) stopQuery(cursor *Cursor) error {
	cursor.mu.Lock()
	cursor.outstandingRequests += 1
	cursor.mu.Unlock()

	q := Query{
		Type:  p.Query_STOP,
		Token: cursor.query.Token,
		Term:  &cursor.term,
	}

	_, err := cursor.conn.SendQuery(s, q, cursor.opts, false)
	if err != nil {
		return err
	}

	response, err := cursor.conn.ReadResponse(s, cursor.query.Token)
	if err != nil {
		return err
	}

	s.handleBatchResponse(cursor, response)

	return nil
}

// noreplyWaitQuery sends the NOREPLY_WAIT query to the server.
func (s *Session) noreplyWaitQuery() error {
	q := Query{
		Type:  p.Query_NOREPLY_WAIT,
		Token: s.nextToken(),
	}

	conn, err := s.getConn()
	if err != nil {
		return err
	}
	defer conn.CloseNoWait()

	_, err = conn.SendQuery(s, q, map[string]interface{}{}, false)

	err = conn.Close()
	if err != nil {
		return err
	}

	return nil
}
func (s *Session) getConn() (*Connection, error) {
	if s.pool == nil {
		return nil, pool.ErrClosed
	}

	c, err := s.pool.Get()
	if err != nil {
		return nil, err
	}

	return &Connection{Conn: c, s: s}, nil
}

func (s *Session) checkCache(token int64) (*Cursor, bool) {
	s.Lock()
	defer s.Unlock()

	cursor, ok := s.cache[token]
	return cursor, ok
}

func (s *Session) setCache(token int64, cursor *Cursor) {
	s.Lock()
	defer s.Unlock()

	s.cache[token] = cursor
}
<|endoftext|>


<commit_before>package helm

import (
	"bytes"
	"encoding/json"
	"html/template"
	"path/filepath"

	"gopkg.in/yaml.v3"

	chart "helm.sh/helm/v3/pkg/chart"
	loader "helm.sh/helm/v3/pkg/chart/loader"
	"k8s.io/apimachinery/pkg/runtime"
)

func ConvertToChart(name string, objects map[string]runtime.Object) (*chart.Chart, error) {

	files := []*loader.BufferedFile{
		&loader.BufferedFile{
			Name: "README.md",
			Data: []byte("This chart was created by converting a Compose file"),
		}}

	chart := `name: {{.Name}}
description: A generated Helm Chart for {{.Name}} from Skippbox Kompose
version: 0.0.1
apiVersion: v1
keywords:
  - {{.Name}}
sources:
home:
`

	t, err := template.New("ChartTmpl").Parse(chart)
	if err != nil {
		return nil, err
	}
	type ChartDetails struct {
		Name string
	}
	var chartData bytes.Buffer
	_ = t.Execute(&chartData, ChartDetails{Name: name})

	files = append(files, &loader.BufferedFile{
		Name: "Chart.yaml",
		Data: chartData.Bytes(),
	})

	for name, o := range objects {
		j, err := json.Marshal(o)
		if err != nil {
			return nil, err
		}
		buf, err := jsonToYaml(j, 2)
		if err != nil {
			return nil, err
		}
		files = append(files, &loader.BufferedFile{
			Name: filepath.Join("templates", name),
			Data: buf,
		})

	}
	return loader.LoadFiles(files)
}

// Convert JSON to YAML.
func jsonToYaml(j []byte, spaces int) ([]byte, error) {
	// Convert the JSON to an object.
	var jsonObj interface{}
	// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the
	// Go JSON library doesn't try to pick the right number type (int, float,
	// etc.) when unmarshling to interface{}, it just picks float64
	// universally. go-yaml does go through the effort of picking the right
	// number type, so we can preserve number type throughout this process.
	err := yaml.Unmarshal(j, &jsonObj)
	if err != nil {
		return nil, err
	}

	var b bytes.Buffer
	encoder := yaml.NewEncoder(&b)
	encoder.SetIndent(spaces)
	if err := encoder.Encode(jsonObj); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}
<commit_msg>cleanup and small fixes<commit_after>package helm

import (
	"bytes"
	"encoding/json"
	"html/template"
	"path/filepath"

	"gopkg.in/yaml.v3"

	chart "helm.sh/helm/v3/pkg/chart"
	loader "helm.sh/helm/v3/pkg/chart/loader"
	"k8s.io/apimachinery/pkg/runtime"
)

func ConvertToChart(name string, objects map[string]runtime.Object) (*chart.Chart, error) {

	files := []*loader.BufferedFile{
		&loader.BufferedFile{
			Name: "README.md",
			Data: []byte("This chart was created by converting a Compose file"),
		}}

	chart := `name: {{.Name}}
description: A generated Helm Chart for {{.Name}} from Skippbox Kompose
version: 0.0.1
apiVersion: v1
keywords:
  - {{.Name}}
sources:
home:
`

	t, err := template.New("ChartTmpl").Parse(chart)
	if err != nil {
		return nil, err
	}
	type ChartDetails struct {
		Name string
	}
	var chartData bytes.Buffer
	err = t.Execute(&chartData, ChartDetails{Name: name})
	if err != nil {
		return nil, err
	}
	files = append(files, &loader.BufferedFile{
		Name: "Chart.yaml",
		Data: chartData.Bytes(),
	})

	for name, o := range objects {
		j, err := json.Marshal(o)
		if err != nil {
			return nil, err
		}
		buf, err := jsonToYaml(j, 2)
		if err != nil {
			return nil, err
		}
		files = append(files, &loader.BufferedFile{
			Name: filepath.Join("templates", name),
			Data: buf,
		})

	}
	return loader.LoadFiles(files)
}

// Convert JSON to YAML.
func jsonToYaml(j []byte, spaces int) ([]byte, error) {
	// Convert the JSON to an object.
	var jsonObj interface{}
	// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the
	// Go JSON library doesn't try to pick the right number type (int, float,
	// etc.) when unmarshling to interface{}, it just picks float64
	// universally. go-yaml does go through the effort of picking the right
	// number type, so we can preserve number type throughout this process.
	err := yaml.Unmarshal(j, &jsonObj)
	if err != nil {
		return nil, err
	}

	var b bytes.Buffer
	encoder := yaml.NewEncoder(&b)
	encoder.SetIndent(spaces)
	if err := encoder.Encode(jsonObj); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}
<|endoftext|>