<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/codecommit"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsCodeCommitRepository() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCodeCommitRepositoryCreate,
		Update: resourceAwsCodeCommitRepositoryUpdate,
		Read:   resourceAwsCodeCommitRepositoryRead,
		Delete: resourceAwsCodeCommitRepositoryDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"repository_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if len(value) > 100 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 100 characters", k))
					}
					return
				},
			},

			"description": {
				Type:     schema.TypeString,
				Optional: true,
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if len(value) > 1000 {
						errors = append(errors, fmt.Errorf(
							"%q cannot be longer than 1000 characters", k))
					}
					return
				},
			},

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

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

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

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

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

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

	input := &codecommit.CreateRepositoryInput{
		RepositoryName:        aws.String(d.Get("repository_name").(string)),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	out, err := conn.CreateRepository(input)
	if err != nil {
		return fmt.Errorf("Error creating CodeCommit Repository: %s", err)
	}

	d.SetId(d.Get("repository_name").(string))
	d.Set("repository_id", out.RepositoryMetadata.RepositoryId)
	d.Set("arn", out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", out.RepositoryMetadata.CloneUrlSsh)

	return resourceAwsCodeCommitRepositoryUpdate(d, meta)
}

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

	if _, ok := d.GetOk("default_branch"); ok {
		if d.HasChange("default_branch") {
			if err := resourceAwsCodeCommitUpdateDefaultBranch(conn, d); err != nil {
				return err
			}
		}
	}

	if d.HasChange("description") {
		if err := resourceAwsCodeCommitUpdateDescription(conn, d); err != nil {
			return err
		}
	}

	return resourceAwsCodeCommitRepositoryRead(d, meta)
}

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

	input := &codecommit.GetRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	}

	out, err := conn.GetRepository(input)
	if err != nil {
		return fmt.Errorf("Error reading CodeCommit Repository: %s", err.Error())
	}

	d.Set("repository_id", out.RepositoryMetadata.RepositoryId)
	d.Set("arn", out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", out.RepositoryMetadata.CloneUrlSsh)
	d.Set("description", out.RepositoryMetadata.RepositoryDescription)
	d.Set("repository_name", out.RepositoryMetadata.RepositoryName)

	if _, ok := d.GetOk("default_branch"); ok {
		if out.RepositoryMetadata.DefaultBranch != nil {
			d.Set("default_branch", out.RepositoryMetadata.DefaultBranch)
		}
	}

	return nil
}

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

	log.Printf("[DEBUG] CodeCommit Delete Repository: %s", d.Id())
	_, err := conn.DeleteRepository(&codecommit.DeleteRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	})
	if err != nil {
		return fmt.Errorf("Error deleting CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDescription(conn *codecommit.CodeCommit, d *schema.ResourceData) error {
	branchInput := &codecommit.UpdateRepositoryDescriptionInput{
		RepositoryName:        aws.String(d.Id()),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	_, err := conn.UpdateRepositoryDescription(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Repository Description for CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDefaultBranch(conn *codecommit.CodeCommit, d *schema.ResourceData) error {
	input := &codecommit.ListBranchesInput{
		RepositoryName: aws.String(d.Id()),
	}

	out, err := conn.ListBranches(input)
	if err != nil {
		return fmt.Errorf("Error reading CodeCommit Repository branches: %s", err.Error())
	}

	if len(out.Branches) == 0 {
		log.Printf("[WARN] Not setting Default Branch CodeCommit Repository that has no branches: %s", d.Id())
		return nil
	}

	branchInput := &codecommit.UpdateDefaultBranchInput{
		RepositoryName:    aws.String(d.Id()),
		DefaultBranchName: aws.String(d.Get("default_branch").(string)),
	}

	_, err = conn.UpdateDefaultBranch(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Default Branch for CodeCommit Repository: %s", err.Error())
	}

	return nil
}
<commit_msg>ValidateFunc: repository_name and description for resource_aws_codecommit_repository<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/codecommit"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsCodeCommitRepository() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCodeCommitRepositoryCreate,
		Update: resourceAwsCodeCommitRepositoryUpdate,
		Read:   resourceAwsCodeCommitRepositoryRead,
		Delete: resourceAwsCodeCommitRepositoryDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"repository_name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateMaxLength(100),
			},

			"description": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validateMaxLength(1000),
			},

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

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

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

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

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

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

	input := &codecommit.CreateRepositoryInput{
		RepositoryName:        aws.String(d.Get("repository_name").(string)),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	out, err := conn.CreateRepository(input)
	if err != nil {
		return fmt.Errorf("Error creating CodeCommit Repository: %s", err)
	}

	d.SetId(d.Get("repository_name").(string))
	d.Set("repository_id", out.RepositoryMetadata.RepositoryId)
	d.Set("arn", out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", out.RepositoryMetadata.CloneUrlSsh)

	return resourceAwsCodeCommitRepositoryUpdate(d, meta)
}

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

	if _, ok := d.GetOk("default_branch"); ok {
		if d.HasChange("default_branch") {
			if err := resourceAwsCodeCommitUpdateDefaultBranch(conn, d); err != nil {
				return err
			}
		}
	}

	if d.HasChange("description") {
		if err := resourceAwsCodeCommitUpdateDescription(conn, d); err != nil {
			return err
		}
	}

	return resourceAwsCodeCommitRepositoryRead(d, meta)
}

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

	input := &codecommit.GetRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	}

	out, err := conn.GetRepository(input)
	if err != nil {
		return fmt.Errorf("Error reading CodeCommit Repository: %s", err.Error())
	}

	d.Set("repository_id", out.RepositoryMetadata.RepositoryId)
	d.Set("arn", out.RepositoryMetadata.Arn)
	d.Set("clone_url_http", out.RepositoryMetadata.CloneUrlHttp)
	d.Set("clone_url_ssh", out.RepositoryMetadata.CloneUrlSsh)
	d.Set("description", out.RepositoryMetadata.RepositoryDescription)
	d.Set("repository_name", out.RepositoryMetadata.RepositoryName)

	if _, ok := d.GetOk("default_branch"); ok {
		if out.RepositoryMetadata.DefaultBranch != nil {
			d.Set("default_branch", out.RepositoryMetadata.DefaultBranch)
		}
	}

	return nil
}

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

	log.Printf("[DEBUG] CodeCommit Delete Repository: %s", d.Id())
	_, err := conn.DeleteRepository(&codecommit.DeleteRepositoryInput{
		RepositoryName: aws.String(d.Id()),
	})
	if err != nil {
		return fmt.Errorf("Error deleting CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDescription(conn *codecommit.CodeCommit, d *schema.ResourceData) error {
	branchInput := &codecommit.UpdateRepositoryDescriptionInput{
		RepositoryName:        aws.String(d.Id()),
		RepositoryDescription: aws.String(d.Get("description").(string)),
	}

	_, err := conn.UpdateRepositoryDescription(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Repository Description for CodeCommit Repository: %s", err.Error())
	}

	return nil
}

func resourceAwsCodeCommitUpdateDefaultBranch(conn *codecommit.CodeCommit, d *schema.ResourceData) error {
	input := &codecommit.ListBranchesInput{
		RepositoryName: aws.String(d.Id()),
	}

	out, err := conn.ListBranches(input)
	if err != nil {
		return fmt.Errorf("Error reading CodeCommit Repository branches: %s", err.Error())
	}

	if len(out.Branches) == 0 {
		log.Printf("[WARN] Not setting Default Branch CodeCommit Repository that has no branches: %s", d.Id())
		return nil
	}

	branchInput := &codecommit.UpdateDefaultBranchInput{
		RepositoryName:    aws.String(d.Id()),
		DefaultBranchName: aws.String(d.Get("default_branch").(string)),
	}

	_, err = conn.UpdateDefaultBranch(branchInput)
	if err != nil {
		return fmt.Errorf("Error Updating Default Branch for CodeCommit Repository: %s", err.Error())
	}

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/secretsmanager"
	"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 resourceAwsSecretsManagerSecret() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSecretsManagerSecretCreate,
		Read:   resourceAwsSecretsManagerSecretRead,
		Update: resourceAwsSecretsManagerSecretUpdate,
		Delete: resourceAwsSecretsManagerSecretDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"kms_key_id": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policy": {
				Type:             schema.TypeString,
				Optional:         true,
				ValidateFunc:     validateJsonString,
				DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
			},
			"recovery_window_in_days": {
				Type:         schema.TypeInt,
				Optional:     true,
				Default:      30,
				ValidateFunc: validation.IntBetween(7, 30),
			},
			"rotation_enabled": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"rotation_lambda_arn": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"rotation_rules": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"automatically_after_days": {
							Type:     schema.TypeInt,
							Required: true,
						},
					},
				},
			},
			"tags": tagsSchema(),
		},
	}
}

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

	input := &secretsmanager.CreateSecretInput{
		Description: aws.String(d.Get("description").(string)),
		Name:        aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("kms_key_id"); ok && v.(string) != "" {
		input.KmsKeyId = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Creating Secrets Manager Secret: %s", input)
	output, err := conn.CreateSecret(input)
	if err != nil {
		return fmt.Errorf("error creating Secrets Manager Secret: %s", err)
	}

	d.SetId(aws.StringValue(output.ARN))

	if v, ok := d.GetOk("policy"); ok && v.(string) != "" {
		input := &secretsmanager.PutResourcePolicyInput{
			ResourcePolicy: aws.String(v.(string)),
			SecretId:       aws.String(d.Id()),
		}

		log.Printf("[DEBUG] Setting Secrets Manager Secret resource policy; %s", input)
		_, err := conn.PutResourcePolicy(input)
		if err != nil {
			return fmt.Errorf("error setting Secrets Manager Secret %q policy: %s", d.Id(), err)
		}
	}

	if v, ok := d.GetOk("rotation_lambda_arn"); ok && v.(string) != "" {
		input := &secretsmanager.RotateSecretInput{
			RotationLambdaARN: aws.String(v.(string)),
			RotationRules:     expandSecretsManagerRotationRules(d.Get("rotation_rules").([]interface{})),
			SecretId:          aws.String(d.Id()),
		}

		log.Printf("[DEBUG] Enabling Secrets Manager Secret rotation: %s", input)
		err := resource.Retry(1*time.Minute, func() *resource.RetryError {
			_, err := conn.RotateSecret(input)
			if err != nil {
				// AccessDeniedException: Secrets Manager cannot invoke the specified Lambda function.
				if isAWSErr(err, "AccessDeniedException", "") {
					return resource.RetryableError(err)
				}
				return resource.NonRetryableError(err)
			}
			return nil
		})
		if err != nil {
			return fmt.Errorf("error enabling Secrets Manager Secret %q rotation: %s", d.Id(), err)
		}
	}

	if v, ok := d.GetOk("tags"); ok {
		input := &secretsmanager.TagResourceInput{
			SecretId: aws.String(d.Id()),
			Tags:     tagsFromMapSecretsManager(v.(map[string]interface{})),
		}

		log.Printf("[DEBUG] Tagging Secrets Manager Secret: %s", input)
		_, err := conn.TagResource(input)
		if err != nil {
			return fmt.Errorf("error tagging Secrets Manager Secret %q: %s", d.Id(), input)
		}
	}

	return resourceAwsSecretsManagerSecretRead(d, meta)
}

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

	input := &secretsmanager.DescribeSecretInput{
		SecretId: aws.String(d.Id()),
	}

	log.Printf("[DEBUG] Reading Secrets Manager Secret: %s", input)
	output, err := conn.DescribeSecret(input)
	if err != nil {
		if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
			log.Printf("[WARN] Secrets Manager Secret %q not found - removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("error reading Secrets Manager Secret: %s", err)
	}

	d.Set("arn", output.ARN)
	d.Set("description", output.Description)
	d.Set("kms_key_id", output.KmsKeyId)
	d.Set("name", output.Name)

	pIn := &secretsmanager.GetResourcePolicyInput{
		SecretId: aws.String(d.Id()),
	}
	log.Printf("[DEBUG] Reading Secrets Manager Secret policy: %s", pIn)
	pOut, err := conn.GetResourcePolicy(pIn)
	if err != nil {
		return fmt.Errorf("error reading Secrets Manager Secret policy: %s", err)
	}

	if pOut.ResourcePolicy != nil {
		policy, err := structure.NormalizeJsonString(*pOut.ResourcePolicy)
		if err != nil {
			return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err)
		}
		d.Set("policy", policy)
	}

	d.Set("rotation_enabled", output.RotationEnabled)

	if aws.BoolValue(output.RotationEnabled) {
		d.Set("rotation_lambda_arn", output.RotationLambdaARN)
		if err := d.Set("rotation_rules", flattenSecretsManagerRotationRules(output.RotationRules)); err != nil {
			return fmt.Errorf("error setting rotation_rules: %s", err)
		}
	} else {
		d.Set("rotation_lambda_arn", "")
		d.Set("rotation_rules", []interface{}{})
	}

	if err := d.Set("tags", tagsToMapSecretsManager(output.Tags)); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}

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

	if d.HasChange("description") || d.HasChange("kms_key_id") {
		input := &secretsmanager.UpdateSecretInput{
			Description: aws.String(d.Get("description").(string)),
			SecretId:    aws.String(d.Id()),
		}

		if v, ok := d.GetOk("kms_key_id"); ok && v.(string) != "" {
			input.KmsKeyId = aws.String(v.(string))
		}

		log.Printf("[DEBUG] Updating Secrets Manager Secret: %s", input)
		_, err := conn.UpdateSecret(input)
		if err != nil {
			return fmt.Errorf("error updating Secrets Manager Secret: %s", err)
		}
	}

	if d.HasChange("policy") {
		if v, ok := d.GetOk("policy"); ok && v.(string) != "" {
			policy, err := structure.NormalizeJsonString(v.(string))
			if err != nil {
				return errwrap.Wrapf("policy contains an invalid JSON: {{err}}", err)
			}
			input := &secretsmanager.PutResourcePolicyInput{
				ResourcePolicy: aws.String(policy),
				SecretId:       aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Setting Secrets Manager Secret resource policy; %s", input)
			_, err = conn.PutResourcePolicy(input)
			if err != nil {
				return fmt.Errorf("error setting Secrets Manager Secret %q policy: %s", d.Id(), err)
			}
		} else {
			input := &secretsmanager.DeleteResourcePolicyInput{
				SecretId: aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Removing Secrets Manager Secret policy: %s", input)
			_, err := conn.DeleteResourcePolicy(input)
			if err != nil {
				return fmt.Errorf("error removing Secrets Manager Secret %q policy: %s", d.Id(), err)
			}
		}
	}

	if d.HasChange("rotation_lambda_arn") || d.HasChange("rotation_rules") {
		if v, ok := d.GetOk("rotation_lambda_arn"); ok && v.(string) != "" {
			input := &secretsmanager.RotateSecretInput{
				RotationLambdaARN: aws.String(v.(string)),
				RotationRules:     expandSecretsManagerRotationRules(d.Get("rotation_rules").([]interface{})),
				SecretId:          aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Enabling Secrets Manager Secret rotation: %s", input)
			err := resource.Retry(1*time.Minute, func() *resource.RetryError {
				_, err := conn.RotateSecret(input)
				if err != nil {
					// AccessDeniedException: Secrets Manager cannot invoke the specified Lambda function.
					if isAWSErr(err, "AccessDeniedException", "") {
						return resource.RetryableError(err)
					}
					return resource.NonRetryableError(err)
				}
				return nil
			})
			if err != nil {
				return fmt.Errorf("error updating Secrets Manager Secret %q rotation: %s", d.Id(), err)
			}
		} else {
			input := &secretsmanager.CancelRotateSecretInput{
				SecretId: aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Cancelling Secrets Manager Secret rotation: %s", input)
			_, err := conn.CancelRotateSecret(input)
			if err != nil {
				return fmt.Errorf("error cancelling Secret Manager Secret %q rotation: %s", d.Id(), err)
			}
		}
	}

	if d.HasChange("tags") {
		oraw, nraw := d.GetChange("tags")
		o := oraw.(map[string]interface{})
		n := nraw.(map[string]interface{})
		create, remove := diffTagsSecretsManager(tagsFromMapSecretsManager(o), tagsFromMapSecretsManager(n))

		if len(remove) > 0 {
			log.Printf("[DEBUG] Removing Secrets Manager Secret %q tags: %#v", d.Id(), remove)
			k := make([]*string, len(remove), len(remove))
			for i, t := range remove {
				k[i] = t.Key
			}

			_, err := conn.UntagResource(&secretsmanager.UntagResourceInput{
				SecretId: aws.String(d.Id()),
				TagKeys:  k,
			})
			if err != nil {
				return fmt.Errorf("error updating Secrets Manager Secrets %q tags: %s", d.Id(), err)
			}
		}
		if len(create) > 0 {
			log.Printf("[DEBUG] Creating Secrets Manager Secret %q tags: %#v", d.Id(), create)
			_, err := conn.TagResource(&secretsmanager.TagResourceInput{
				SecretId: aws.String(d.Id()),
				Tags:     create,
			})
			if err != nil {
				return fmt.Errorf("error updating Secrets Manager Secrets %q tags: %s", d.Id(), err)
			}
		}
	}

	return resourceAwsSecretsManagerSecretRead(d, meta)
}

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

	input := &secretsmanager.DeleteSecretInput{
		RecoveryWindowInDays: aws.Int64(int64(d.Get("recovery_window_in_days").(int))),
		SecretId:             aws.String(d.Id()),
	}

	log.Printf("[DEBUG] Deleting Secrets Manager Secret: %s", input)
	_, err := conn.DeleteSecret(input)
	if err != nil {
		if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
			return nil
		}
		return fmt.Errorf("error deleting Secrets Manager Secret: %s", err)
	}

	return nil
}

func expandSecretsManagerRotationRules(l []interface{}) *secretsmanager.RotationRulesType {
	if len(l) == 0 {
		return nil
	}

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

	rules := &secretsmanager.RotationRulesType{
		AutomaticallyAfterDays: aws.Int64(int64(m["automatically_after_days"].(int))),
	}

	return rules
}

func flattenSecretsManagerRotationRules(rules *secretsmanager.RotationRulesType) []interface{} {
	if rules == nil {
		return []interface{}{}
	}

	m := map[string]interface{}{
		"automatically_after_days": int(aws.Int64Value(rules.AutomaticallyAfterDays)),
	}

	return []interface{}{m}
}
<commit_msg>resource/aws_secretsmanager_secret: Use fmt.Errorf() instead of errwrap.Wrapf() and use aws.StringValue()<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/secretsmanager"
	"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 resourceAwsSecretsManagerSecret() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSecretsManagerSecretCreate,
		Read:   resourceAwsSecretsManagerSecretRead,
		Update: resourceAwsSecretsManagerSecretUpdate,
		Delete: resourceAwsSecretsManagerSecretDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"kms_key_id": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policy": {
				Type:             schema.TypeString,
				Optional:         true,
				ValidateFunc:     validateJsonString,
				DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
			},
			"recovery_window_in_days": {
				Type:         schema.TypeInt,
				Optional:     true,
				Default:      30,
				ValidateFunc: validation.IntBetween(7, 30),
			},
			"rotation_enabled": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"rotation_lambda_arn": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"rotation_rules": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"automatically_after_days": {
							Type:     schema.TypeInt,
							Required: true,
						},
					},
				},
			},
			"tags": tagsSchema(),
		},
	}
}

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

	input := &secretsmanager.CreateSecretInput{
		Description: aws.String(d.Get("description").(string)),
		Name:        aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("kms_key_id"); ok && v.(string) != "" {
		input.KmsKeyId = aws.String(v.(string))
	}

	log.Printf("[DEBUG] Creating Secrets Manager Secret: %s", input)
	output, err := conn.CreateSecret(input)
	if err != nil {
		return fmt.Errorf("error creating Secrets Manager Secret: %s", err)
	}

	d.SetId(aws.StringValue(output.ARN))

	if v, ok := d.GetOk("policy"); ok && v.(string) != "" {
		input := &secretsmanager.PutResourcePolicyInput{
			ResourcePolicy: aws.String(v.(string)),
			SecretId:       aws.String(d.Id()),
		}

		log.Printf("[DEBUG] Setting Secrets Manager Secret resource policy; %s", input)
		_, err := conn.PutResourcePolicy(input)
		if err != nil {
			return fmt.Errorf("error setting Secrets Manager Secret %q policy: %s", d.Id(), err)
		}
	}

	if v, ok := d.GetOk("rotation_lambda_arn"); ok && v.(string) != "" {
		input := &secretsmanager.RotateSecretInput{
			RotationLambdaARN: aws.String(v.(string)),
			RotationRules:     expandSecretsManagerRotationRules(d.Get("rotation_rules").([]interface{})),
			SecretId:          aws.String(d.Id()),
		}

		log.Printf("[DEBUG] Enabling Secrets Manager Secret rotation: %s", input)
		err := resource.Retry(1*time.Minute, func() *resource.RetryError {
			_, err := conn.RotateSecret(input)
			if err != nil {
				// AccessDeniedException: Secrets Manager cannot invoke the specified Lambda function.
				if isAWSErr(err, "AccessDeniedException", "") {
					return resource.RetryableError(err)
				}
				return resource.NonRetryableError(err)
			}
			return nil
		})
		if err != nil {
			return fmt.Errorf("error enabling Secrets Manager Secret %q rotation: %s", d.Id(), err)
		}
	}

	if v, ok := d.GetOk("tags"); ok {
		input := &secretsmanager.TagResourceInput{
			SecretId: aws.String(d.Id()),
			Tags:     tagsFromMapSecretsManager(v.(map[string]interface{})),
		}

		log.Printf("[DEBUG] Tagging Secrets Manager Secret: %s", input)
		_, err := conn.TagResource(input)
		if err != nil {
			return fmt.Errorf("error tagging Secrets Manager Secret %q: %s", d.Id(), input)
		}
	}

	return resourceAwsSecretsManagerSecretRead(d, meta)
}

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

	input := &secretsmanager.DescribeSecretInput{
		SecretId: aws.String(d.Id()),
	}

	log.Printf("[DEBUG] Reading Secrets Manager Secret: %s", input)
	output, err := conn.DescribeSecret(input)
	if err != nil {
		if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
			log.Printf("[WARN] Secrets Manager Secret %q not found - removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("error reading Secrets Manager Secret: %s", err)
	}

	d.Set("arn", output.ARN)
	d.Set("description", output.Description)
	d.Set("kms_key_id", output.KmsKeyId)
	d.Set("name", output.Name)

	pIn := &secretsmanager.GetResourcePolicyInput{
		SecretId: aws.String(d.Id()),
	}
	log.Printf("[DEBUG] Reading Secrets Manager Secret policy: %s", pIn)
	pOut, err := conn.GetResourcePolicy(pIn)
	if err != nil {
		return fmt.Errorf("error reading Secrets Manager Secret policy: %s", err)
	}

	if pOut.ResourcePolicy != nil {
		policy, err := structure.NormalizeJsonString(aws.StringValue(pOut.ResourcePolicy))
		if err != nil {
			return fmt.Errorf("policy contains an invalid JSON: %s", err)
		}
		d.Set("policy", policy)
	}

	d.Set("rotation_enabled", output.RotationEnabled)

	if aws.BoolValue(output.RotationEnabled) {
		d.Set("rotation_lambda_arn", output.RotationLambdaARN)
		if err := d.Set("rotation_rules", flattenSecretsManagerRotationRules(output.RotationRules)); err != nil {
			return fmt.Errorf("error setting rotation_rules: %s", err)
		}
	} else {
		d.Set("rotation_lambda_arn", "")
		d.Set("rotation_rules", []interface{}{})
	}

	if err := d.Set("tags", tagsToMapSecretsManager(output.Tags)); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}

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

	if d.HasChange("description") || d.HasChange("kms_key_id") {
		input := &secretsmanager.UpdateSecretInput{
			Description: aws.String(d.Get("description").(string)),
			SecretId:    aws.String(d.Id()),
		}

		if v, ok := d.GetOk("kms_key_id"); ok && v.(string) != "" {
			input.KmsKeyId = aws.String(v.(string))
		}

		log.Printf("[DEBUG] Updating Secrets Manager Secret: %s", input)
		_, err := conn.UpdateSecret(input)
		if err != nil {
			return fmt.Errorf("error updating Secrets Manager Secret: %s", err)
		}
	}

	if d.HasChange("policy") {
		if v, ok := d.GetOk("policy"); ok && v.(string) != "" {
			policy, err := structure.NormalizeJsonString(v.(string))
			if err != nil {
				return fmt.Errorf("policy contains an invalid JSON: %s", err)
			}
			input := &secretsmanager.PutResourcePolicyInput{
				ResourcePolicy: aws.String(policy),
				SecretId:       aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Setting Secrets Manager Secret resource policy; %s", input)
			_, err = conn.PutResourcePolicy(input)
			if err != nil {
				return fmt.Errorf("error setting Secrets Manager Secret %q policy: %s", d.Id(), err)
			}
		} else {
			input := &secretsmanager.DeleteResourcePolicyInput{
				SecretId: aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Removing Secrets Manager Secret policy: %s", input)
			_, err := conn.DeleteResourcePolicy(input)
			if err != nil {
				return fmt.Errorf("error removing Secrets Manager Secret %q policy: %s", d.Id(), err)
			}
		}
	}

	if d.HasChange("rotation_lambda_arn") || d.HasChange("rotation_rules") {
		if v, ok := d.GetOk("rotation_lambda_arn"); ok && v.(string) != "" {
			input := &secretsmanager.RotateSecretInput{
				RotationLambdaARN: aws.String(v.(string)),
				RotationRules:     expandSecretsManagerRotationRules(d.Get("rotation_rules").([]interface{})),
				SecretId:          aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Enabling Secrets Manager Secret rotation: %s", input)
			err := resource.Retry(1*time.Minute, func() *resource.RetryError {
				_, err := conn.RotateSecret(input)
				if err != nil {
					// AccessDeniedException: Secrets Manager cannot invoke the specified Lambda function.
					if isAWSErr(err, "AccessDeniedException", "") {
						return resource.RetryableError(err)
					}
					return resource.NonRetryableError(err)
				}
				return nil
			})
			if err != nil {
				return fmt.Errorf("error updating Secrets Manager Secret %q rotation: %s", d.Id(), err)
			}
		} else {
			input := &secretsmanager.CancelRotateSecretInput{
				SecretId: aws.String(d.Id()),
			}

			log.Printf("[DEBUG] Cancelling Secrets Manager Secret rotation: %s", input)
			_, err := conn.CancelRotateSecret(input)
			if err != nil {
				return fmt.Errorf("error cancelling Secret Manager Secret %q rotation: %s", d.Id(), err)
			}
		}
	}

	if d.HasChange("tags") {
		oraw, nraw := d.GetChange("tags")
		o := oraw.(map[string]interface{})
		n := nraw.(map[string]interface{})
		create, remove := diffTagsSecretsManager(tagsFromMapSecretsManager(o), tagsFromMapSecretsManager(n))

		if len(remove) > 0 {
			log.Printf("[DEBUG] Removing Secrets Manager Secret %q tags: %#v", d.Id(), remove)
			k := make([]*string, len(remove), len(remove))
			for i, t := range remove {
				k[i] = t.Key
			}

			_, err := conn.UntagResource(&secretsmanager.UntagResourceInput{
				SecretId: aws.String(d.Id()),
				TagKeys:  k,
			})
			if err != nil {
				return fmt.Errorf("error updating Secrets Manager Secrets %q tags: %s", d.Id(), err)
			}
		}
		if len(create) > 0 {
			log.Printf("[DEBUG] Creating Secrets Manager Secret %q tags: %#v", d.Id(), create)
			_, err := conn.TagResource(&secretsmanager.TagResourceInput{
				SecretId: aws.String(d.Id()),
				Tags:     create,
			})
			if err != nil {
				return fmt.Errorf("error updating Secrets Manager Secrets %q tags: %s", d.Id(), err)
			}
		}
	}

	return resourceAwsSecretsManagerSecretRead(d, meta)
}

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

	input := &secretsmanager.DeleteSecretInput{
		RecoveryWindowInDays: aws.Int64(int64(d.Get("recovery_window_in_days").(int))),
		SecretId:             aws.String(d.Id()),
	}

	log.Printf("[DEBUG] Deleting Secrets Manager Secret: %s", input)
	_, err := conn.DeleteSecret(input)
	if err != nil {
		if isAWSErr(err, secretsmanager.ErrCodeResourceNotFoundException, "") {
			return nil
		}
		return fmt.Errorf("error deleting Secrets Manager Secret: %s", err)
	}

	return nil
}

func expandSecretsManagerRotationRules(l []interface{}) *secretsmanager.RotationRulesType {
	if len(l) == 0 {
		return nil
	}

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

	rules := &secretsmanager.RotationRulesType{
		AutomaticallyAfterDays: aws.Int64(int64(m["automatically_after_days"].(int))),
	}

	return rules
}

func flattenSecretsManagerRotationRules(rules *secretsmanager.RotationRulesType) []interface{} {
	if rules == nil {
		return []interface{}{}
	}

	m := map[string]interface{}{
		"automatically_after_days": int(aws.Int64Value(rules.AutomaticallyAfterDays)),
	}

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


<commit_before>/*
Copyright 2017 Gravitational, Inc.

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

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

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

package utils

import (
	"context"
	"io"
	"net"
	"sync"
	"time"

	"github.com/gravitational/trace"
	log "github.com/sirupsen/logrus"
)

// NewLoadBalancer returns new load balancer listening on frotend
// and redirecting requests to backends using round robin algo
func NewLoadBalancer(ctx context.Context, frontend NetAddr, backends ...NetAddr) (*LoadBalancer, error) {
	if ctx == nil {
		return nil, trace.BadParameter("missing parameter context")
	}
	return &LoadBalancer{
		frontend:     frontend,
		ctx:          ctx,
		backends:     backends,
		currentIndex: -1,
		Entry: log.WithFields(log.Fields{
			trace.Component: "loadbalancer",
			trace.ComponentFields: log.Fields{
				"listen": frontend.String(),
			},
		}),
		connections: make(map[NetAddr]map[int64]net.Conn),
	}, nil
}

// LoadBalancer implements naive round robin TCP load
// balancer used in tests.
type LoadBalancer struct {
	sync.RWMutex
	connID int64
	*log.Entry
	frontend       NetAddr
	backends       []NetAddr
	ctx            context.Context
	currentIndex   int
	listener       net.Listener
	listenerClosed bool
	connections    map[NetAddr]map[int64]net.Conn
}

// trackeConnection adds connection to the connection tracker
func (l *LoadBalancer) trackConnection(backend NetAddr, conn net.Conn) int64 {
	l.Lock()
	defer l.Unlock()
	l.connID += 1
	tracker, ok := l.connections[backend]
	if !ok {
		tracker = make(map[int64]net.Conn)
		l.connections[backend] = tracker
	}
	tracker[l.connID] = conn
	return l.connID
}

// untrackConnection removes connection from connection tracker
func (l *LoadBalancer) untrackConnection(backend NetAddr, id int64) {
	l.Lock()
	defer l.Unlock()
	tracker, ok := l.connections[backend]
	if !ok {
		return
	}
	delete(tracker, id)
}

// dropConnections drops connections associated with backend
func (l *LoadBalancer) dropConnections(backend NetAddr) {
	tracker := l.connections[backend]
	for _, conn := range tracker {
		conn.Close()
	}
	delete(l.connections, backend)
}

// AddBackend adds backend
func (l *LoadBalancer) AddBackend(b NetAddr) {
	l.Lock()
	defer l.Unlock()
	l.backends = append(l.backends, b)
	l.Debugf("backends %v", l.backends)
}

// RemoveBackend removes backend
func (l *LoadBalancer) RemoveBackend(b NetAddr) {
	l.Lock()
	defer l.Unlock()
	l.currentIndex = -1
	for i := range l.backends {
		if l.backends[i].Equals(b) {
			l.backends = append(l.backends[:i], l.backends[i+1:]...)
			l.dropConnections(b)
			return
		}
	}
}

func (l *LoadBalancer) nextBackend() (*NetAddr, error) {
	l.Lock()
	defer l.Unlock()
	if len(l.backends) == 0 {
		return nil, trace.ConnectionProblem(nil, "no backends")
	}
	l.currentIndex = ((l.currentIndex + 1) % len(l.backends))
	return &l.backends[l.currentIndex], nil
}

func (l *LoadBalancer) closeListener() {
	l.Lock()
	defer l.Unlock()
	if l.listener == nil {
		return
	}
	if l.listenerClosed {
		return
	}
	l.listenerClosed = true
	l.listener.Close()
}

func (l *LoadBalancer) isClosed() bool {
	l.RLock()
	defer l.RUnlock()
	return l.listenerClosed
}

func (l *LoadBalancer) Close() error {
	l.closeListener()
	return nil
}

// ListenAndServe starts listening socket and serves connections on it
func (l *LoadBalancer) ListenAndServe() error {
	if err := l.Listen(); err != nil {
		return trace.Wrap(err)
	}
	return l.Serve()
}

// Listen creates a listener on the frontend addr
func (l *LoadBalancer) Listen() error {
	var err error
	l.listener, err = net.Listen(l.frontend.AddrNetwork, l.frontend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	l.Debugf("created listening socket")
	return nil
}

// Serve starts accepting connections
func (l *LoadBalancer) Serve() error {
	backoffTimer := time.NewTicker(5 * time.Second)
	defer backoffTimer.Stop()
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			if l.isClosed() {
				return trace.ConnectionProblem(nil, "listener is closed")
			}
			select {
			case <-backoffTimer.C:
				l.Debugf("backoff on network error")
			case <-l.ctx.Done():
				return trace.ConnectionProblem(nil, "context is closing")
			}
		}
		go l.forwardConnection(conn)
	}
}

func (l *LoadBalancer) forwardConnection(conn net.Conn) {
	err := l.forward(conn)
	if err != nil {
		l.Warningf("failed to forward connection: %v", err)
	}
}

func (l *LoadBalancer) forward(conn net.Conn) error {
	defer conn.Close()

	backend, err := l.nextBackend()
	if err != nil {
		return trace.Wrap(err)
	}

	connID := l.trackConnection(*backend, conn)
	defer l.untrackConnection(*backend, connID)

	backendConn, err := net.Dial(backend.AddrNetwork, backend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	defer backendConn.Close()

	backendConnID := l.trackConnection(*backend, backendConn)
	defer l.untrackConnection(*backend, backendConnID)

	logger := l.WithFields(log.Fields{
		"source": conn.RemoteAddr(),
		"dest":   backendConn.RemoteAddr(),
	})
	logger.Debugf("forward")

	messagesC := make(chan error, 2)

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(conn, backendConn)
		messagesC <- err
	}()

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(backendConn, conn)
		messagesC <- err
	}()

	var lastErr error
	for i := 0; i < 2; i++ {
		select {
		case err := <-messagesC:
			if err != nil && err != io.EOF {
				logger.Warningf("connection problem: %v %T", trace.DebugReport(err), err)
				lastErr = err
			}
		case <-l.ctx.Done():
			return trace.ConnectionProblem(nil, "context is closing")
		}
	}

	return lastErr
}
<commit_msg>fix typo<commit_after>/*
Copyright 2017 Gravitational, Inc.

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

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

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

package utils

import (
	"context"
	"io"
	"net"
	"sync"
	"time"

	"github.com/gravitational/trace"
	log "github.com/sirupsen/logrus"
)

// NewLoadBalancer returns new load balancer listening on frontend
// and redirecting requests to backends using round robin algo
func NewLoadBalancer(ctx context.Context, frontend NetAddr, backends ...NetAddr) (*LoadBalancer, error) {
	if ctx == nil {
		return nil, trace.BadParameter("missing parameter context")
	}
	return &LoadBalancer{
		frontend:     frontend,
		ctx:          ctx,
		backends:     backends,
		currentIndex: -1,
		Entry: log.WithFields(log.Fields{
			trace.Component: "loadbalancer",
			trace.ComponentFields: log.Fields{
				"listen": frontend.String(),
			},
		}),
		connections: make(map[NetAddr]map[int64]net.Conn),
	}, nil
}

// LoadBalancer implements naive round robin TCP load
// balancer used in tests.
type LoadBalancer struct {
	sync.RWMutex
	connID int64
	*log.Entry
	frontend       NetAddr
	backends       []NetAddr
	ctx            context.Context
	currentIndex   int
	listener       net.Listener
	listenerClosed bool
	connections    map[NetAddr]map[int64]net.Conn
}

// trackeConnection adds connection to the connection tracker
func (l *LoadBalancer) trackConnection(backend NetAddr, conn net.Conn) int64 {
	l.Lock()
	defer l.Unlock()
	l.connID += 1
	tracker, ok := l.connections[backend]
	if !ok {
		tracker = make(map[int64]net.Conn)
		l.connections[backend] = tracker
	}
	tracker[l.connID] = conn
	return l.connID
}

// untrackConnection removes connection from connection tracker
func (l *LoadBalancer) untrackConnection(backend NetAddr, id int64) {
	l.Lock()
	defer l.Unlock()
	tracker, ok := l.connections[backend]
	if !ok {
		return
	}
	delete(tracker, id)
}

// dropConnections drops connections associated with backend
func (l *LoadBalancer) dropConnections(backend NetAddr) {
	tracker := l.connections[backend]
	for _, conn := range tracker {
		conn.Close()
	}
	delete(l.connections, backend)
}

// AddBackend adds backend
func (l *LoadBalancer) AddBackend(b NetAddr) {
	l.Lock()
	defer l.Unlock()
	l.backends = append(l.backends, b)
	l.Debugf("backends %v", l.backends)
}

// RemoveBackend removes backend
func (l *LoadBalancer) RemoveBackend(b NetAddr) {
	l.Lock()
	defer l.Unlock()
	l.currentIndex = -1
	for i := range l.backends {
		if l.backends[i].Equals(b) {
			l.backends = append(l.backends[:i], l.backends[i+1:]...)
			l.dropConnections(b)
			return
		}
	}
}

func (l *LoadBalancer) nextBackend() (*NetAddr, error) {
	l.Lock()
	defer l.Unlock()
	if len(l.backends) == 0 {
		return nil, trace.ConnectionProblem(nil, "no backends")
	}
	l.currentIndex = ((l.currentIndex + 1) % len(l.backends))
	return &l.backends[l.currentIndex], nil
}

func (l *LoadBalancer) closeListener() {
	l.Lock()
	defer l.Unlock()
	if l.listener == nil {
		return
	}
	if l.listenerClosed {
		return
	}
	l.listenerClosed = true
	l.listener.Close()
}

func (l *LoadBalancer) isClosed() bool {
	l.RLock()
	defer l.RUnlock()
	return l.listenerClosed
}

func (l *LoadBalancer) Close() error {
	l.closeListener()
	return nil
}

// ListenAndServe starts listening socket and serves connections on it
func (l *LoadBalancer) ListenAndServe() error {
	if err := l.Listen(); err != nil {
		return trace.Wrap(err)
	}
	return l.Serve()
}

// Listen creates a listener on the frontend addr
func (l *LoadBalancer) Listen() error {
	var err error
	l.listener, err = net.Listen(l.frontend.AddrNetwork, l.frontend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	l.Debugf("created listening socket")
	return nil
}

// Serve starts accepting connections
func (l *LoadBalancer) Serve() error {
	backoffTimer := time.NewTicker(5 * time.Second)
	defer backoffTimer.Stop()
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			if l.isClosed() {
				return trace.ConnectionProblem(nil, "listener is closed")
			}
			select {
			case <-backoffTimer.C:
				l.Debugf("backoff on network error")
			case <-l.ctx.Done():
				return trace.ConnectionProblem(nil, "context is closing")
			}
		}
		go l.forwardConnection(conn)
	}
}

func (l *LoadBalancer) forwardConnection(conn net.Conn) {
	err := l.forward(conn)
	if err != nil {
		l.Warningf("failed to forward connection: %v", err)
	}
}

func (l *LoadBalancer) forward(conn net.Conn) error {
	defer conn.Close()

	backend, err := l.nextBackend()
	if err != nil {
		return trace.Wrap(err)
	}

	connID := l.trackConnection(*backend, conn)
	defer l.untrackConnection(*backend, connID)

	backendConn, err := net.Dial(backend.AddrNetwork, backend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	defer backendConn.Close()

	backendConnID := l.trackConnection(*backend, backendConn)
	defer l.untrackConnection(*backend, backendConnID)

	logger := l.WithFields(log.Fields{
		"source": conn.RemoteAddr(),
		"dest":   backendConn.RemoteAddr(),
	})
	logger.Debugf("forward")

	messagesC := make(chan error, 2)

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(conn, backendConn)
		messagesC <- err
	}()

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(backendConn, conn)
		messagesC <- err
	}()

	var lastErr error
	for i := 0; i < 2; i++ {
		select {
		case err := <-messagesC:
			if err != nil && err != io.EOF {
				logger.Warningf("connection problem: %v %T", trace.DebugReport(err), err)
				lastErr = err
			}
		case <-l.ctx.Done():
			return trace.ConnectionProblem(nil, "context is closing")
		}
	}

	return lastErr
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Gravitational, Inc.

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

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

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

package utils

import (
	"context"
	"io"
	"net"
	"sync"
	"time"

	"github.com/gravitational/trace"
	log "github.com/sirupsen/logrus"
)

// NewLoadBalancer returns new load balancer listening on frontend
// and redirecting requests to backends using round robin algo
func NewLoadBalancer(ctx context.Context, frontend NetAddr, backends ...NetAddr) (*LoadBalancer, error) {
	if ctx == nil {
		return nil, trace.BadParameter("missing parameter context")
	}
	waitCtx, waitCancel := context.WithCancel(ctx)
	return &LoadBalancer{
		frontend:     frontend,
		ctx:          ctx,
		backends:     backends,
		currentIndex: -1,
		waitCtx:      waitCtx,
		waitCancel:   waitCancel,
		Entry: log.WithFields(log.Fields{
			trace.Component: "loadbalancer",
			trace.ComponentFields: log.Fields{
				"listen": frontend.String(),
			},
		}),
		connections: make(map[NetAddr]map[int64]net.Conn),
	}, nil
}

// LoadBalancer implements naive round robin TCP load
// balancer used in tests.
type LoadBalancer struct {
	sync.RWMutex
	connID int64
	*log.Entry
	frontend       NetAddr
	backends       []NetAddr
	ctx            context.Context
	currentIndex   int
	listener       net.Listener
	listenerClosed bool
	connections    map[NetAddr]map[int64]net.Conn
	waitCtx        context.Context
	waitCancel     context.CancelFunc
}

// trackeConnection adds connection to the connection tracker
func (l *LoadBalancer) trackConnection(backend NetAddr, conn net.Conn) int64 {
	l.Lock()
	defer l.Unlock()
	l.connID++
	tracker, ok := l.connections[backend]
	if !ok {
		tracker = make(map[int64]net.Conn)
		l.connections[backend] = tracker
	}
	tracker[l.connID] = conn
	return l.connID
}

// untrackConnection removes connection from connection tracker
func (l *LoadBalancer) untrackConnection(backend NetAddr, id int64) {
	l.Lock()
	defer l.Unlock()
	tracker, ok := l.connections[backend]
	if !ok {
		return
	}
	delete(tracker, id)
}

// dropConnections drops connections associated with backend
func (l *LoadBalancer) dropConnections(backend NetAddr) {
	tracker := l.connections[backend]
	for _, conn := range tracker {
		conn.Close()
	}
	delete(l.connections, backend)
}

// AddBackend adds backend
func (l *LoadBalancer) AddBackend(b NetAddr) {
	l.Lock()
	defer l.Unlock()
	l.backends = append(l.backends, b)
	l.Debugf("Backends %v.", l.backends)
}

// RemoveBackend removes backend
func (l *LoadBalancer) RemoveBackend(b NetAddr) error {
	l.Lock()
	defer l.Unlock()
	l.currentIndex = -1
	for i := range l.backends {
		if l.backends[i] == b {
			l.backends = append(l.backends[:i], l.backends[i+1:]...)
			l.dropConnections(b)
			return nil
		}
	}
	return trace.NotFound("lb has no backend matching: %+v", b)
}

func (l *LoadBalancer) nextBackend() (*NetAddr, error) {
	l.Lock()
	defer l.Unlock()
	if len(l.backends) == 0 {
		return nil, trace.ConnectionProblem(nil, "no backends")
	}
	l.currentIndex = ((l.currentIndex + 1) % len(l.backends))
	return &l.backends[l.currentIndex], nil
}

func (l *LoadBalancer) closeListener() {
	l.Lock()
	defer l.Unlock()
	if l.listener == nil {
		return
	}
	if l.listenerClosed {
		return
	}
	l.listenerClosed = true
	l.listener.Close()
}

func (l *LoadBalancer) isClosed() bool {
	l.RLock()
	defer l.RUnlock()
	return l.listenerClosed
}

func (l *LoadBalancer) Close() error {
	l.closeListener()
	return nil
}

// Listen creates a listener on the frontend addr
func (l *LoadBalancer) Listen() error {
	var err error
	l.listener, err = net.Listen(l.frontend.AddrNetwork, l.frontend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	l.Debugf("created listening socket")
	return nil
}

// Addr returns the frontend listener address. Call this after Listen,
// otherwise Addr returns nil.
func (l *LoadBalancer) Addr() net.Addr {
	if l.listener == nil {
		return nil
	}
	return l.listener.Addr()
}

// Serve starts accepting connections
func (l *LoadBalancer) Serve() error {
	defer l.waitCancel()
	backoffTimer := time.NewTicker(5 * time.Second)
	defer backoffTimer.Stop()
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			if l.isClosed() {
				return trace.ConnectionProblem(nil, "listener is closed")
			}
			select {
			case <-backoffTimer.C:
				l.Debugf("Backoff on network error.")
			case <-l.ctx.Done():
				return trace.ConnectionProblem(nil, "context is closing")
			}
		} else {
			go l.forwardConnection(conn)
		}
	}
}

func (l *LoadBalancer) forwardConnection(conn net.Conn) {
	err := l.forward(conn)
	if err != nil {
		l.Warningf("Failed to forward connection: %v.", err)
	}
}

// Wait is here to workaround issue https://github.com/golang/go/issues/10527
// in tests
func (l *LoadBalancer) Wait() {
	<-l.waitCtx.Done()
}

func (l *LoadBalancer) forward(conn net.Conn) error {
	defer conn.Close()

	backend, err := l.nextBackend()
	if err != nil {
		return trace.Wrap(err)
	}

	connID := l.trackConnection(*backend, conn)
	defer l.untrackConnection(*backend, connID)

	backendConn, err := net.Dial(backend.AddrNetwork, backend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	defer backendConn.Close()

	backendConnID := l.trackConnection(*backend, backendConn)
	defer l.untrackConnection(*backend, backendConnID)

	logger := l.WithFields(log.Fields{
		"source": conn.RemoteAddr(),
		"dest":   backendConn.RemoteAddr(),
	})
	logger.Debugf("forward")

	messagesC := make(chan error, 2)

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(conn, backendConn)
		messagesC <- err
	}()

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(backendConn, conn)
		messagesC <- err
	}()

	var lastErr error
	for i := 0; i < 2; i++ {
		select {
		case err := <-messagesC:
			if err != nil && err != io.EOF {
				logger.Warningf("connection problem: %v %T", trace.DebugReport(err), err)
				lastErr = err
			}
		case <-l.ctx.Done():
			return trace.ConnectionProblem(nil, "context is closing")
		}
	}

	return lastErr
}
<commit_msg>Fix race condition in LoadBalancer (#8608)<commit_after>/*
Copyright 2017 Gravitational, Inc.

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

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

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

package utils

import (
	"context"
	"io"
	"net"
	"sync"
	"time"

	"github.com/gravitational/trace"
	log "github.com/sirupsen/logrus"
)

// NewLoadBalancer returns new load balancer listening on frontend
// and redirecting requests to backends using round robin algo
func NewLoadBalancer(ctx context.Context, frontend NetAddr, backends ...NetAddr) (*LoadBalancer, error) {
	if ctx == nil {
		return nil, trace.BadParameter("missing parameter context")
	}
	waitCtx, waitCancel := context.WithCancel(ctx)
	return &LoadBalancer{
		frontend:     frontend,
		ctx:          ctx,
		backends:     backends,
		currentIndex: -1,
		waitCtx:      waitCtx,
		waitCancel:   waitCancel,
		Entry: log.WithFields(log.Fields{
			trace.Component: "loadbalancer",
			trace.ComponentFields: log.Fields{
				"listen": frontend.String(),
			},
		}),
		connections: make(map[NetAddr]map[int64]net.Conn),
	}, nil
}

// LoadBalancer implements naive round robin TCP load
// balancer used in tests.
type LoadBalancer struct {
	sync.RWMutex
	connID int64
	*log.Entry
	frontend       NetAddr
	backends       []NetAddr
	ctx            context.Context
	currentIndex   int
	listener       net.Listener
	listenerClosed bool
	connections    map[NetAddr]map[int64]net.Conn
	waitCtx        context.Context
	waitCancel     context.CancelFunc
}

// trackeConnection adds connection to the connection tracker
func (l *LoadBalancer) trackConnection(backend NetAddr, conn net.Conn) int64 {
	l.Lock()
	defer l.Unlock()
	l.connID++
	tracker, ok := l.connections[backend]
	if !ok {
		tracker = make(map[int64]net.Conn)
		l.connections[backend] = tracker
	}
	tracker[l.connID] = conn
	return l.connID
}

// untrackConnection removes connection from connection tracker
func (l *LoadBalancer) untrackConnection(backend NetAddr, id int64) {
	l.Lock()
	defer l.Unlock()
	tracker, ok := l.connections[backend]
	if !ok {
		return
	}
	delete(tracker, id)
}

// dropConnections drops connections associated with backend
func (l *LoadBalancer) dropConnections(backend NetAddr) {
	tracker := l.connections[backend]
	for _, conn := range tracker {
		conn.Close()
	}
	delete(l.connections, backend)
}

// AddBackend adds backend
func (l *LoadBalancer) AddBackend(b NetAddr) {
	l.Lock()
	defer l.Unlock()
	l.backends = append(l.backends, b)
	l.Debugf("Backends %v.", l.backends)
}

// RemoveBackend removes backend
func (l *LoadBalancer) RemoveBackend(b NetAddr) error {
	l.Lock()
	defer l.Unlock()
	l.currentIndex = -1
	for i := range l.backends {
		if l.backends[i] == b {
			l.backends = append(l.backends[:i], l.backends[i+1:]...)
			l.dropConnections(b)
			return nil
		}
	}
	return trace.NotFound("lb has no backend matching: %+v", b)
}

func (l *LoadBalancer) nextBackend() (NetAddr, error) {
	l.Lock()
	defer l.Unlock()
	if len(l.backends) == 0 {
		return NetAddr{}, trace.ConnectionProblem(nil, "no backends")
	}
	l.currentIndex = ((l.currentIndex + 1) % len(l.backends))
	return l.backends[l.currentIndex], nil
}

func (l *LoadBalancer) closeListener() {
	l.Lock()
	defer l.Unlock()
	if l.listener == nil {
		return
	}
	if l.listenerClosed {
		return
	}
	l.listenerClosed = true
	l.listener.Close()
}

func (l *LoadBalancer) isClosed() bool {
	l.RLock()
	defer l.RUnlock()
	return l.listenerClosed
}

func (l *LoadBalancer) Close() error {
	l.closeListener()
	return nil
}

// Listen creates a listener on the frontend addr
func (l *LoadBalancer) Listen() error {
	var err error
	l.listener, err = net.Listen(l.frontend.AddrNetwork, l.frontend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	l.Debugf("created listening socket")
	return nil
}

// Addr returns the frontend listener address. Call this after Listen,
// otherwise Addr returns nil.
func (l *LoadBalancer) Addr() net.Addr {
	if l.listener == nil {
		return nil
	}
	return l.listener.Addr()
}

// Serve starts accepting connections
func (l *LoadBalancer) Serve() error {
	defer l.waitCancel()
	backoffTimer := time.NewTicker(5 * time.Second)
	defer backoffTimer.Stop()
	for {
		conn, err := l.listener.Accept()
		if err != nil {
			if l.isClosed() {
				return trace.ConnectionProblem(nil, "listener is closed")
			}
			select {
			case <-backoffTimer.C:
				l.Debugf("Backoff on network error.")
			case <-l.ctx.Done():
				return trace.ConnectionProblem(nil, "context is closing")
			}
		} else {
			go l.forwardConnection(conn)
		}
	}
}

func (l *LoadBalancer) forwardConnection(conn net.Conn) {
	err := l.forward(conn)
	if err != nil {
		l.Warningf("Failed to forward connection: %v.", err)
	}
}

// Wait is here to workaround issue https://github.com/golang/go/issues/10527
// in tests
func (l *LoadBalancer) Wait() {
	<-l.waitCtx.Done()
}

func (l *LoadBalancer) forward(conn net.Conn) error {
	defer conn.Close()

	backend, err := l.nextBackend()
	if err != nil {
		return trace.Wrap(err)
	}

	connID := l.trackConnection(backend, conn)
	defer l.untrackConnection(backend, connID)

	backendConn, err := net.Dial(backend.AddrNetwork, backend.Addr)
	if err != nil {
		return trace.ConvertSystemError(err)
	}
	defer backendConn.Close()

	backendConnID := l.trackConnection(backend, backendConn)
	defer l.untrackConnection(backend, backendConnID)

	logger := l.WithFields(log.Fields{
		"source": conn.RemoteAddr(),
		"dest":   backendConn.RemoteAddr(),
	})
	logger.Debugf("forward")

	messagesC := make(chan error, 2)

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(conn, backendConn)
		messagesC <- err
	}()

	go func() {
		defer conn.Close()
		defer backendConn.Close()
		_, err := io.Copy(backendConn, conn)
		messagesC <- err
	}()

	var lastErr error
	for i := 0; i < 2; i++ {
		select {
		case err := <-messagesC:
			if err != nil && err != io.EOF {
				logger.Warningf("connection problem: %v %T", trace.DebugReport(err), err)
				lastErr = err
			}
		case <-l.ctx.Done():
			return trace.ConnectionProblem(nil, "context is closing")
		}
	}

	return lastErr
}
<|endoftext|>


<commit_before>package bundle

import (
	"archive/zip"
	"bytes"
	"errors"
	"github.com/BrentNoorda/slfsrv/ssutil"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const Version string = "0.0.1"

/***************** SelfserverReadSeeker - ReadSeeker implementation *****************/

type SelfservingReadSeeker struct {
	seek   int64
	curpos int64
	f      *zip.File
	fSize  int64
	rc     io.ReadCloser
}

func NewSelfservingReadSeeker(reader *zip.Reader, filespec string) (*SelfservingReadSeeker, error) {
	var f *zip.File
	for _, f = range reader.File {
		if f.Name == filespec {
			return &SelfservingReadSeeker{seek: 0, curpos: 0, f: f, fSize: int64(f.UncompressedSize64), rc: nil}, nil
		}
	}
	return nil, errors.New("file \"" + filespec + "\" not found in bundle")
}

func (ssrs *SelfservingReadSeeker) Close() {
	if ssrs.rc != nil {
		ssrs.rc.Close()
		ssrs.rc = nil
		ssrs.seek = 0
		ssrs.curpos = 0
	}
}

func (ssrs *SelfservingReadSeeker) Read(p []byte) (int, error) {
	var pLen int = len(p)

	if pLen == 0 {
		// asking for no data
		return 0, nil
	} else if ssrs.fSize <= ssrs.seek {
		// cannot read beyond EOF
		return 0, io.EOF
	} else {

		var err error

		if ssrs.rc == nil {
			if ssrs.rc, err = ssrs.f.Open(); err != nil {
				return 0, err
			}
		}

		var readCount int

		// if not currently at the read position, then skip to there
		for ssrs.curpos < ssrs.seek {
			const BUF_CHUNK_SIZE int64 = 20000
			var wantToReadCount int64 = ssrs.seek - ssrs.curpos
			if BUF_CHUNK_SIZE < wantToReadCount {
				wantToReadCount = BUF_CHUNK_SIZE
			}
			var buffer []byte = make([]byte, wantToReadCount)
			readCount, err = io.ReadFull(ssrs.rc, buffer)
			if (err != nil) || (readCount != int(wantToReadCount)) {
				return 0, io.EOF
			}
			ssrs.curpos += wantToReadCount
		}

		readCount, err = io.ReadFull(ssrs.rc, p)
		ssrs.curpos += int64(readCount)
		ssrs.seek = ssrs.curpos
		return readCount, err
	}
}

func (ssrs *SelfservingReadSeeker) Seek(offset int64, whence int) (int64, error) {
	var err error
	if whence == 1 {
		offset += ssrs.seek
	} else if whence == 2 {
		offset = ssrs.fSize + offset
	}
	/* else whence is 0 and sets file position absolutely */
	if offset < 0 {
		err = errors.New("invalid negative file offset")
	} else {
		if ssrs.rc != nil {
			if offset < ssrs.curpos {
				ssrs.Close()
			}
		}
		ssrs.seek = offset
	}
	return ssrs.seek, err
}

/***************** selfserverReaderAt - ReaderAt implementation *****************/

type selfservingReaderAt struct {
	offset int64
	f      *os.File
}

func (s selfservingReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
	return s.f.ReadAt(p, off+s.offset)
}

func ReadFile(reader *zip.Reader, filespec string) ([]byte, error) {

	var f *zip.File
	for _, f = range reader.File {
		if f.Name == filespec {
			var readcount int
			var err error
			var filesize uint64 = f.UncompressedSize64
			var data []byte
			var rc io.ReadCloser

			if rc, err = f.Open(); err != nil {
				return data, err
			}
			if data, err = ioutil.ReadAll(rc); err != nil {
				rc.Close()
			} else {
				err = rc.Close()
			}
			if err != nil {
				return data, err
			}

			readcount = len(data)
			if uint64(readcount) != filesize {
				return data, errors.New("bytes missing from file")
			}

			return data, nil

		}
	}
	return nil, errors.New("file \"" + filespec + "\" not found in bundle")
}

func Reader(zipFilespec string, offsetByte0 int) (reader *zip.Reader, initialUrl string, err error) {

	var fileSize int64
	var f *os.File

	if f, err = os.Open(zipFilespec); err != nil {
		return
	}

	// determine total size of file zipFilespec
	var fi os.FileInfo
	if fi, err = f.Stat(); err != nil {
		return
	}
	fileSize = fi.Size()

	var readerAt selfservingReaderAt = selfservingReaderAt{offset: int64(offsetByte0), f: f}

	if reader, err = zip.NewReader(readerAt, fileSize-readerAt.offset); err != nil {
		return
	}

	// check that the version string is correct, and pull out initialUrl
	var data []byte
	if data, err = ReadFile(reader, "slfsrv"); err != nil {
		return
	}
	/* not checking the slfsrv version yet */

	if data, err = ReadFile(reader, "initialUrl"); err != nil {
		return
	}
	initialUrl = string(data)

	return
}

func BundleDirectory(rootPath string, initialUrl string) (buf *bytes.Buffer, err error) {
	buf = new(bytes.Buffer)
	var w *zip.Writer = zip.NewWriter(buf)

	// the first entry is always version, then all of the files
	var f io.Writer
	f, _ = w.Create("slfsrv")
	f.Write([]byte(Version))

	// the second entry is always initialUrl, then all of the files
	f, _ = w.Create("initialUrl")
	f.Write([]byte(initialUrl))

	// rootpath must be a directory
	var isDir bool
	if isDir, err = ssutil.IsDir(rootPath); err != nil {
		return
	}
	if !isDir {
		err = errors.New("path \"" + rootPath + "\" must be a directory")
		return
	}

	// add ALL of the files in rootPath
	var skipLen int = len(rootPath) + 1
	var fileData []byte
	walkFunc := func(path string, info os.FileInfo, err error) error {
		if err == nil {
			if !info.IsDir() {
				var relativePath string = path[skipLen:]

				if f, err = w.Create(filepath.ToSlash(relativePath)); err != nil {
					return err
				}

				if fileData, err = ioutil.ReadFile(path); err != nil {
					return err
				}

				if _, err = f.Write(fileData); err != nil {
					return err
				}

			}
		}
		return err
	}
	if err = filepath.Walk(rootPath, walkFunc); err != nil {
		return
	}

	err = w.Close()

	return
}

func saveOutputFile(filespec string, data []byte) error {
	return ioutil.WriteFile(filespec, data, 0644)
}

func Bundle(outFilespec string, replaceOK bool, rootPath string, initialUrl string, verbose bool) (err error) {

	// output must be file name, not a directory
	var isDir bool
	if isDir, err = ssutil.IsDir(outFilespec); err != nil {
		return err
	}
	if isDir {
		return errors.New("bundle file \"" + outFilespec + "\" must not be a directory")
	}

	// verify that there is not already an output file, or it's OK to create that file
	if !replaceOK {
		var fileOrDirExists bool
		if fileOrDirExists, err = ssutil.FileOrDirExists(outFilespec); err != nil {
			return err
		}
		if fileOrDirExists {
			return errors.New("bundle file \"" + outFilespec + "\" already exists; cannot overwrite without -replace flag")
		}
	}

	var zipContents *bytes.Buffer
	if zipContents, err = BundleDirectory(rootPath, initialUrl); err != nil {
		return err
	}

	err = saveOutputFile(outFilespec, zipContents.Bytes())

	return
}
<commit_msg>reuse large buffer for "seeking" in zip file, to avoid what I guess are probably GC performance problems in making a new buffer each time<commit_after>package bundle

import (
	"archive/zip"
	"bytes"
	"errors"
	"github.com/BrentNoorda/slfsrv/ssutil"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const Version string = "0.0.1"

/***************** SelfserverReadSeeker - ReadSeeker implementation *****************/

type SelfservingReadSeeker struct {
	seek   int64
	curpos int64
	f      *zip.File
	fSize  int64
	rc     io.ReadCloser
}

func NewSelfservingReadSeeker(reader *zip.Reader, filespec string) (*SelfservingReadSeeker, error) {
	var f *zip.File
	for _, f = range reader.File {
		if f.Name == filespec {
			return &SelfservingReadSeeker{seek: 0, curpos: 0, f: f, fSize: int64(f.UncompressedSize64), rc: nil}, nil
		}
	}
	return nil, errors.New("file \"" + filespec + "\" not found in bundle")
}

func (ssrs *SelfservingReadSeeker) Close() {
	if ssrs.rc != nil {
		ssrs.rc.Close()
		ssrs.rc = nil
		ssrs.seek = 0
		ssrs.curpos = 0
	}
}

func (ssrs *SelfservingReadSeeker) Read(p []byte) (int, error) {
	var pLen int = len(p)

	if pLen == 0 {
		// asking for no data
		return 0, nil
	} else if ssrs.fSize <= ssrs.seek {
		// cannot read beyond EOF
		return 0, io.EOF
	} else {

		var err error

		if ssrs.rc == nil {
			if ssrs.rc, err = ssrs.f.Open(); err != nil {
				return 0, err
			}
		}

		var readCount int

		// if not currently at the read position, then skip to there
		if ssrs.curpos != ssrs.seek {
			var bigBuffer [100000]byte
			for ssrs.curpos < ssrs.seek {
				var wantToReadCount int64 = ssrs.seek - ssrs.curpos
				if int64(len(bigBuffer)) < wantToReadCount {
					wantToReadCount = int64(len(bigBuffer))
				}
				readCount, err = io.ReadFull(ssrs.rc, bigBuffer[0:wantToReadCount])
				if (err != nil) || (readCount != int(wantToReadCount)) {
					return 0, io.EOF
				}
				ssrs.curpos += wantToReadCount
			}
		}

		readCount, err = io.ReadFull(ssrs.rc, p)
		ssrs.curpos += int64(readCount)
		ssrs.seek = ssrs.curpos
		return readCount, err
	}
}

func (ssrs *SelfservingReadSeeker) Seek(offset int64, whence int) (int64, error) {
	var err error
	if whence == 1 {
		offset += ssrs.seek
	} else if whence == 2 {
		offset = ssrs.fSize + offset
	}
	/* else whence is 0 and sets file position absolutely */
	if offset < 0 {
		err = errors.New("invalid negative file offset")
	} else {
		if ssrs.rc != nil {
			if offset < ssrs.curpos {
				ssrs.Close()
			}
		}
		ssrs.seek = offset
	}
	return ssrs.seek, err
}

/***************** selfserverReaderAt - ReaderAt implementation *****************/

type selfservingReaderAt struct {
	offset int64
	f      *os.File
}

func (s selfservingReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
	return s.f.ReadAt(p, off+s.offset)
}

func ReadFile(reader *zip.Reader, filespec string) ([]byte, error) {

	var f *zip.File
	for _, f = range reader.File {
		if f.Name == filespec {
			var readcount int
			var err error
			var filesize uint64 = f.UncompressedSize64
			var data []byte
			var rc io.ReadCloser

			if rc, err = f.Open(); err != nil {
				return data, err
			}
			if data, err = ioutil.ReadAll(rc); err != nil {
				rc.Close()
			} else {
				err = rc.Close()
			}
			if err != nil {
				return data, err
			}

			readcount = len(data)
			if uint64(readcount) != filesize {
				return data, errors.New("bytes missing from file")
			}

			return data, nil

		}
	}
	return nil, errors.New("file \"" + filespec + "\" not found in bundle")
}

func Reader(zipFilespec string, offsetByte0 int) (reader *zip.Reader, initialUrl string, err error) {

	var fileSize int64
	var f *os.File

	if f, err = os.Open(zipFilespec); err != nil {
		return
	}

	// determine total size of file zipFilespec
	var fi os.FileInfo
	if fi, err = f.Stat(); err != nil {
		return
	}
	fileSize = fi.Size()

	var readerAt selfservingReaderAt = selfservingReaderAt{offset: int64(offsetByte0), f: f}

	if reader, err = zip.NewReader(readerAt, fileSize-readerAt.offset); err != nil {
		return
	}

	// check that the version string is correct, and pull out initialUrl
	var data []byte
	if data, err = ReadFile(reader, "slfsrv"); err != nil {
		return
	}
	/* not checking the slfsrv version yet */

	if data, err = ReadFile(reader, "initialUrl"); err != nil {
		return
	}
	initialUrl = string(data)

	return
}

func BundleDirectory(rootPath string, initialUrl string) (buf *bytes.Buffer, err error) {
	buf = new(bytes.Buffer)
	var w *zip.Writer = zip.NewWriter(buf)

	// the first entry is always version, then all of the files
	var f io.Writer
	f, _ = w.Create("slfsrv")
	f.Write([]byte(Version))

	// the second entry is always initialUrl, then all of the files
	f, _ = w.Create("initialUrl")
	f.Write([]byte(initialUrl))

	// rootpath must be a directory
	var isDir bool
	if isDir, err = ssutil.IsDir(rootPath); err != nil {
		return
	}
	if !isDir {
		err = errors.New("path \"" + rootPath + "\" must be a directory")
		return
	}

	// add ALL of the files in rootPath
	var skipLen int = len(rootPath) + 1
	var fileData []byte
	walkFunc := func(path string, info os.FileInfo, err error) error {
		if err == nil {
			if !info.IsDir() {
				var relativePath string = path[skipLen:]

				if f, err = w.Create(filepath.ToSlash(relativePath)); err != nil {
					return err
				}

				if fileData, err = ioutil.ReadFile(path); err != nil {
					return err
				}

				if _, err = f.Write(fileData); err != nil {
					return err
				}

			}
		}
		return err
	}
	if err = filepath.Walk(rootPath, walkFunc); err != nil {
		return
	}

	err = w.Close()

	return
}

func saveOutputFile(filespec string, data []byte) error {
	return ioutil.WriteFile(filespec, data, 0644)
}

func Bundle(outFilespec string, replaceOK bool, rootPath string, initialUrl string, verbose bool) (err error) {

	// output must be file name, not a directory
	var isDir bool
	if isDir, err = ssutil.IsDir(outFilespec); err != nil {
		return err
	}
	if isDir {
		return errors.New("bundle file \"" + outFilespec + "\" must not be a directory")
	}

	// verify that there is not already an output file, or it's OK to create that file
	if !replaceOK {
		var fileOrDirExists bool
		if fileOrDirExists, err = ssutil.FileOrDirExists(outFilespec); err != nil {
			return err
		}
		if fileOrDirExists {
			return errors.New("bundle file \"" + outFilespec + "\" already exists; cannot overwrite without -replace flag")
		}
	}

	var zipContents *bytes.Buffer
	if zipContents, err = BundleDirectory(rootPath, initialUrl); err != nil {
		return err
	}

	err = saveOutputFile(outFilespec, zipContents.Bytes())

	return
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/pkg/errors"
)

// TunnelRoute is the full record for a route.
type TunnelRoute struct {
	Network    string    `json:"network"`
	TunnelId   string    `json:"tunnel_id"`
	TunnelName string    `json:"tunnel_name"`
	Comment    string    `json:"comment"`
	CreatedAt  time.Time `json:"created_at"`
	DeletedAt  time.Time `json:"deleted_at"`
}

// tunnelRouteListResponse is the API response for listing tunnel routes.
type tunnelRouteListResponse struct {
	Response
	Result []TunnelRoute `json:"result"`
}

type tunnelRouteResponse struct {
	Response
	Result TunnelRoute `json:"result"`
}

func extractTunnelRouteResponse(responseBody []byte, err error) (TunnelRoute, error) {
	if err != nil {
		return TunnelRoute{}, err
	}

	var routeResponse tunnelRouteResponse
	err = json.Unmarshal(responseBody, &routeResponse)
	if err != nil {
		return TunnelRoute{}, errors.Wrap(err, errUnmarshalError)
	}

	return routeResponse.Result, nil
}

// TunnelRoutes lists all defined routes for tunnels in the account.
//
// See: https://api.cloudflare.com/#tunnel-route-list-tunnel-routes
func (api *API) TunnelRoutes(ctx context.Context) ([]TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes", AccountRouteRoot, api.AccountID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)

	if err != nil {
		return []TunnelRoute{}, err
	}

	var resp tunnelRouteListResponse
	err = json.Unmarshal(res, &resp)
	if err != nil {
		return []TunnelRoute{}, errors.Wrap(err, errUnmarshalError)
	}

	return resp.Result, nil
}

// TunnelRouteForIp finds the Tunnel Route that encompasses the given IP
//
// See: https://api.cloudflare.com/#tunnel-route-get-tunnel-route-by-ip
func (api *API) TunnelRouteForIp(ctx context.Context, ip string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/ip/%s", AccountRouteRoot, api.AccountID, ip)
	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodGet, uri, nil))
}

// CreateTunnelRoute add a new route to the account's routing table for the given tunnel
//
// See: https://api.cloudflare.com/#tunnel-route-create-route
func (api *API) CreateTunnelRoute(ctx context.Context, tunnelId string, ipNetwork string) (TunnelRoute, error) {
	return api.createTunnelRoute(ctx, tunnelId, ipNetwork, "")
}

// CreateTunnelRouteWithComment add a new route to the account's routing table for the given tunnel
//
// See: https://api.cloudflare.com/#tunnel-route-create-route
func (api *API) CreateTunnelRouteWithComment(ctx context.Context, tunnelId string, ipNetwork string, comment string) (TunnelRoute, error) {
	return api.createTunnelRoute(ctx, tunnelId, ipNetwork, comment)
}

func (api *API) createTunnelRoute(ctx context.Context, tunnelId string, ipNetwork string, comment string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/network/%s", AccountRouteRoot, api.AccountID, url.PathEscape(ipNetwork))

	params := make(map[string]string)
	params["tunnel_id"] = tunnelId
	if comment != "" {
		params["comment"] = comment
	}

	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodPost, uri, params))
}

func (api *API) DeleteTunnelRoute(ctx context.Context, ipNetwork string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/network/%s", AccountRouteRoot, api.AccountID, url.PathEscape(ipNetwork))
	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodDelete, uri, nil))
}

// UpdateTunnelRoute update an existing route in the account's routing table for the given tunnel
func (api *API) UpdateTunnelRoute(ctx context.Context, tunnelId string, currentNetwork string, newNetwork string) (TunnelRoute, error) {
	return api.updateTunnelRoute(ctx, tunnelId, currentNetwork, newNetwork, "")
}

// UpdateTunnelRouteWithComment update an existing route in the account's routing table for the given tunnel
func (api *API) UpdateTunnelRouteWithComment(ctx context.Context, tunnelId string, currentNetwork string, newNetwork string, comment string) (TunnelRoute, error) {
	return api.updateTunnelRoute(ctx, tunnelId, currentNetwork, newNetwork, comment)
}

func (api *API) updateTunnelRoute(ctx context.Context, tunnelId string, currentNetwork string, newNetwork string, comment string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/network/%s", AccountRouteRoot, api.AccountID, url.PathEscape(currentNetwork))

	params := map[string]string{
		"tunnel_id":   tunnelId,
		"new_network": newNetwork,
	}
	if comment != "" {
		params["comment"] = comment
	}

	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodPatch, uri, params))
}
<commit_msg>Add links to additional tunnel route api docs<commit_after>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/pkg/errors"
)

// TunnelRoute is the full record for a route.
type TunnelRoute struct {
	Network    string    `json:"network"`
	TunnelId   string    `json:"tunnel_id"`
	TunnelName string    `json:"tunnel_name"`
	Comment    string    `json:"comment"`
	CreatedAt  time.Time `json:"created_at"`
	DeletedAt  time.Time `json:"deleted_at"`
}

// tunnelRouteListResponse is the API response for listing tunnel routes.
type tunnelRouteListResponse struct {
	Response
	Result []TunnelRoute `json:"result"`
}

type tunnelRouteResponse struct {
	Response
	Result TunnelRoute `json:"result"`
}

func extractTunnelRouteResponse(responseBody []byte, err error) (TunnelRoute, error) {
	if err != nil {
		return TunnelRoute{}, err
	}

	var routeResponse tunnelRouteResponse
	err = json.Unmarshal(responseBody, &routeResponse)
	if err != nil {
		return TunnelRoute{}, errors.Wrap(err, errUnmarshalError)
	}

	return routeResponse.Result, nil
}

// TunnelRoutes lists all defined routes for tunnels in the account.
//
// See: https://api.cloudflare.com/#tunnel-route-list-tunnel-routes
func (api *API) TunnelRoutes(ctx context.Context) ([]TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes", AccountRouteRoot, api.AccountID)
	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)

	if err != nil {
		return []TunnelRoute{}, err
	}

	var resp tunnelRouteListResponse
	err = json.Unmarshal(res, &resp)
	if err != nil {
		return []TunnelRoute{}, errors.Wrap(err, errUnmarshalError)
	}

	return resp.Result, nil
}

// TunnelRouteForIp finds the Tunnel Route that encompasses the given IP
//
// See: https://api.cloudflare.com/#tunnel-route-get-tunnel-route-by-ip
func (api *API) TunnelRouteForIp(ctx context.Context, ip string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/ip/%s", AccountRouteRoot, api.AccountID, ip)
	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodGet, uri, nil))
}

// CreateTunnelRoute add a new route to the account's routing table for the given tunnel
//
// See: https://api.cloudflare.com/#tunnel-route-create-route
func (api *API) CreateTunnelRoute(ctx context.Context, tunnelId string, ipNetwork string) (TunnelRoute, error) {
	return api.createTunnelRoute(ctx, tunnelId, ipNetwork, "")
}

// CreateTunnelRouteWithComment add a new route to the account's routing table for the given tunnel
//
// See: https://api.cloudflare.com/#tunnel-route-create-route
func (api *API) CreateTunnelRouteWithComment(ctx context.Context, tunnelId string, ipNetwork string, comment string) (TunnelRoute, error) {
	return api.createTunnelRoute(ctx, tunnelId, ipNetwork, comment)
}

func (api *API) createTunnelRoute(ctx context.Context, tunnelId string, ipNetwork string, comment string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/network/%s", AccountRouteRoot, api.AccountID, url.PathEscape(ipNetwork))

	params := make(map[string]string)
	params["tunnel_id"] = tunnelId
	if comment != "" {
		params["comment"] = comment
	}

	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodPost, uri, params))
}

// DeleteTunnelRoute delete an existing route from the account's routing table
//
// See: https://api.cloudflare.com/#tunnel-route-delete-route
func (api *API) DeleteTunnelRoute(ctx context.Context, ipNetwork string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/network/%s", AccountRouteRoot, api.AccountID, url.PathEscape(ipNetwork))
	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodDelete, uri, nil))
}

// UpdateTunnelRoute update an existing route in the account's routing table for the given tunnel
//
// See: https://api.cloudflare.com/#tunnel-route-update-route
func (api *API) UpdateTunnelRoute(ctx context.Context, tunnelId string, currentNetwork string, newNetwork string) (TunnelRoute, error) {
	return api.updateTunnelRoute(ctx, tunnelId, currentNetwork, newNetwork, "")
}

// UpdateTunnelRouteWithComment update an existing route in the account's routing table for the given tunnel
//
// See: https://api.cloudflare.com/#tunnel-route-update-route
func (api *API) UpdateTunnelRouteWithComment(ctx context.Context, tunnelId string, currentNetwork string, newNetwork string, comment string) (TunnelRoute, error) {
	return api.updateTunnelRoute(ctx, tunnelId, currentNetwork, newNetwork, comment)
}

func (api *API) updateTunnelRoute(ctx context.Context, tunnelId string, currentNetwork string, newNetwork string, comment string) (TunnelRoute, error) {
	uri := fmt.Sprintf("/%s/%s/teamnet/routes/network/%s", AccountRouteRoot, api.AccountID, url.PathEscape(currentNetwork))

	params := map[string]string{
		"tunnel_id":   tunnelId,
		"new_network": newNetwork,
	}
	if comment != "" {
		params["comment"] = comment
	}

	return extractTunnelRouteResponse(api.makeRequestContext(ctx, http.MethodPatch, uri, params))
}
<|endoftext|>


<commit_before>package cacher

import (
	"sync"
	"time"
)

type (
	CacheFiller func() (data interface{}, err error)

	Cacher struct {
		data map[string]*cacheItem
		mux  sync.Mutex
	}
)

// New returns a new cacher
// purgeEvery sets the timer for purging old cache, set to 0 to never purge.
func New(purgeEvery time.Duration) *Cacher {
	c := &Cacher{
		data: map[string]*cacheItem{},
	}
	if purgeEvery > 0 {
		go c.purge(purgeEvery)
	}
	return c
}

func (c *Cacher) Get(key string, fn CacheFiller, ttl time.Duration) (data interface{}, err error) {
	var ci *cacheItem
	c.mux.Lock()
	if ci = c.data[key]; ci == nil {
		ci = &cacheItem{
			fn:  fn,
			ttl: int64(ttl),
		}
		c.data[key] = ci
	}
	c.mux.Unlock()

	return ci.call()
}

func (c *Cacher) Delete(key string) {
	c.mux.Lock()
	delete(c.data, key)
	c.mux.Unlock()
}

func (c *Cacher) purge(ttl time.Duration) {
	for {
		ts := time.Now().Add(ttl).UnixNano()
		c.mux.Lock()
		for key, ci := range c.data {
			if ci.expiresAt > ts {
				delete(c.data, key)
			}
		}
		c.mux.Unlock()
		time.Sleep(ttl)
	}
}

type cacheItem struct {
	fn        CacheFiller
	expiresAt int64
	ttl       int64

	data interface{}
	err  error

	sync.Mutex
}

func (ci *cacheItem) call() (data interface{}, err error) {
	ts := time.Now().UnixNano()
	ci.Lock()
	if ci.expiresAt == 0 || ci.expiresAt < ts {
		data, err = ci.fn()
		ci.expiresAt = ts + ci.ttl
	} else {
		data, err = ci.data, ci.err
	}
	ci.Unlock()
	return
}
<commit_msg>use seconds rather than nano<commit_after>package cacher

import (
	"sync"
	"time"
)

type (
	CacheFiller func() (data interface{}, err error)

	Cacher struct {
		data map[string]*cacheItem
		mux  sync.Mutex
	}
)

// New returns a new cacher
// purgeEvery sets the timer for purging old cache, set to 0 to never purge.
func New(purgeEvery time.Duration) *Cacher {
	c := &Cacher{
		data: map[string]*cacheItem{},
	}
	if purgeEvery > 0 {
		go c.purge(purgeEvery)
	}
	return c
}

func (c *Cacher) Get(key string, fn CacheFiller, ttl time.Duration) (data interface{}, err error) {
	var ci *cacheItem
	c.mux.Lock()
	if ci = c.data[key]; ci == nil {
		ci = &cacheItem{
			fn:  fn,
			ttl: int64(ttl / time.Second),
		}
		c.data[key] = ci
	}
	c.mux.Unlock()

	return ci.call()
}

func (c *Cacher) Delete(key string) {
	c.mux.Lock()
	delete(c.data, key)
	c.mux.Unlock()
}

func (c *Cacher) purge(ttl time.Duration) {
	for {
		ts := time.Now().Add(ttl).Unix()
		c.mux.Lock()
		for key, ci := range c.data {
			if ci.expiresAt > ts {
				delete(c.data, key)
			}
		}
		c.mux.Unlock()
		time.Sleep(ttl)
	}
}

type cacheItem struct {
	fn        CacheFiller
	expiresAt int64
	ttl       int64

	data interface{}
	err  error

	sync.Mutex
}

func (ci *cacheItem) call() (data interface{}, err error) {
	ts := time.Now().Unix()
	ci.Lock()
	if ci.expiresAt == 0 || ci.expiresAt < ts {
		data, err = ci.fn()
		ci.expiresAt = ts + ci.ttl
	} else {
		data, err = ci.data, ci.err
	}
	ci.Unlock()
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Alexander Zaytsev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package main is a simple TCP proxy server.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/z0rr0/121proxy/proxy"
)

const (
	// Config is a default configuration file name
	Config = "config.json"
	// Name is a program name
	Name = "121proxy"
	// Comment is a general program comment
	Comment = " It is a simple TCP proxy server.\n It forwards incoming TCP requests to remote server without any data changes."
)

var (
	// Version is a program version
	Version = "v0.0"
	// Revision is CVS GIT version
	Revision = "git:000000"
	// BuildDate is date of build
	BuildDate = "1900-00-00_00:00:00UTC"
)

func interrupt() error {
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
	return fmt.Errorf("%v", <-c)
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("abnormal termination, reason: %v\n", r)
		}
	}()
	errc := make(chan error)
	go func() {
		errc <- interrupt()
	}()
	debug := flag.Bool("debug", false, "debug mode")
	version := flag.Bool("version", false, "print version info")
	config := flag.String("config", Config, "configuration file")
	flag.Parse()
	if *version {
		fmt.Printf("%v: %v %v %v\n%v\n", Name, Version, Revision, BuildDate, Comment)
		// flag.PrintDefaults()
		return
	}
	p, err := proxy.New(*config, *debug)
	if err != nil {
		panic(err)
	}
	go func() {
		errc <- p.Start()
	}()
	fmt.Printf("termination, reason: %v\n", <-errc)
}
<commit_msg>verion info updated<commit_after>// Copyright (c) 2015, Alexander Zaytsev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package main is a simple TCP proxy server.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/z0rr0/121proxy/proxy"
)

const (
	// Config is a default configuration file name
	Config = "config.json"
	// Name is a program name
	Name = "121proxy"
	// Comment is a general program comment
	Comment = " It is a simple TCP proxy server.\n It forwards incoming TCP requests to remote server without any data changes."
)

var (
	// Version is a program version
	Version = "0.0.0"
	// Revision is CVS GIT version
	Revision = "git:000000"
	// BuildDate is date of build
	BuildDate = "1900-00-00_00:00:00UTC"
)

func interrupt() error {
	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
	return fmt.Errorf("%v", <-c)
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("abnormal termination, reason: %v\n", r)
		}
	}()
	errc := make(chan error)
	go func() {
		errc <- interrupt()
	}()
	debug := flag.Bool("debug", false, "debug mode")
	version := flag.Bool("version", false, "print version info")
	config := flag.String("config", Config, "configuration file")
	flag.Parse()
	if *version {
		fmt.Printf("%v %v %v %v\n%v\n", Name, Version, Revision, BuildDate, Comment)
		// flag.PrintDefaults()
		return
	}
	p, err := proxy.New(*config, *debug)
	if err != nil {
		panic(err)
	}
	go func() {
		errc <- p.Start()
	}()
	fmt.Printf("termination, reason: %v\n", <-errc)
}
<|endoftext|>


<commit_before>package main
import (
	"net/http"
	"log"
	"github.com/OrdonTeam/atomas/atomas"
	"github.com/nu7hatch/gouuid"
	"os"
)

func nextUUID() string {
	uuid, err := uuid.NewV4()
	if (err != nil) {
		log.Fatal(err)
	}
	return uuid.String()
}

func elementGenerator(_ int) int {
	return 4
}

func main() {
	http.HandleFunc("/new_game", atomas.CreateCreateGameHandler(nextUUID, elementGenerator))
	http.HandleFunc("/game/", atomas.CreateGetGameHandler())
	port := os.Args[1]
	log.Fatal(http.ListenAndServe(port, nil))
}<commit_msg>pass empty map in main<commit_after>package main
import (
	"net/http"
	"log"
	"github.com/OrdonTeam/atomas/atomas"
	"github.com/nu7hatch/gouuid"
	"os"
)

func nextUUID() string {
	uuid, err := uuid.NewV4()
	if (err != nil) {
		log.Fatal(err)
	}
	return uuid.String()
}

func elementGenerator(_ int) int {
	return 4
}

func main() {
	http.HandleFunc("/new_game", atomas.CreateCreateGameHandler(nextUUID, elementGenerator))
	http.HandleFunc("/game/", atomas.CreateGetGameHandler(map[string]atomas.GameDTO))
	port := os.Args[1]
	log.Fatal(http.ListenAndServe(port, nil))
}<|endoftext|>


<commit_before>package hob

import (
	"encoding/json"
)

type TwoPhaseSet struct {
	Type  string          `json:"type"`
	A     map[string]bool `json:"-"`
	R     map[string]bool `json:"-"`
	JSONA []string        `json:"a"`
	JSONR []string        `json:"r"`
}

func NewTwoPhaseSet() (twoPhaseSet *TwoPhaseSet, err error) {
	add := make(map[string]bool)
	remove := make(map[string]bool)

	twoPhaseSet = &TwoPhaseSet{
		Type: "2p-set",
		A:    add,
		R:    remove,
	}
	return
}

func (twoPhaseSet *TwoPhaseSet) Add(value string) (err error) {
	twoPhaseSet.A[value] = true
	return
}

func (twoPhaseSet *TwoPhaseSet) Remove(value string) (err error) {
	twoPhaseSet.R[value] = true
	return
}

func (twoPhaseSet *TwoPhaseSet) Test(value string) (is_member bool, err error) {
	if _, ok := twoPhaseSet.R[value]; ok {
		is_member = false
		return
	}

	if _, ok := twoPhaseSet.A[value]; ok {
		is_member = true
		return
	}
	return
}

func (twoPhaseSet *TwoPhaseSet) JSON() (json_bytes []byte, err error) {
	for k, _ := range twoPhaseSet.A {
		twoPhaseSet.JSONA = append(twoPhaseSet.JSONA, k)
	}

	for k, _ := range twoPhaseSet.R {
		twoPhaseSet.JSONR = append(twoPhaseSet.JSONR, k)
	}

	json_bytes, err = json.Marshal(twoPhaseSet)
	return
}

func (twoPhaseSet *TwoPhaseSet) Clone() (clone *TwoPhaseSet, err error) {
	clone = &TwoPhaseSet{
		Type: twoPhaseSet.Type,
		A:    twoPhaseSet.A,
		R:    twoPhaseSet.R,
	}
	return
}

func (twoPhaseSet *TwoPhaseSet) Merge(oTwoPhaseSet *TwoPhaseSet) (merged_set *TwoPhaseSet, err error) {
	clone, err := twoPhaseSet.Clone()
	if err != nil {
		return nil, err
	}

	merged_set = clone

	return
}

func (sbmap Map) Union() {

}
<commit_msg>Remove bad Map<commit_after>package hob

import (
	"encoding/json"
)

type TwoPhaseSet struct {
	Type  string          `json:"type"`
	A     map[string]bool `json:"-"`
	R     map[string]bool `json:"-"`
	JSONA []string        `json:"a"`
	JSONR []string        `json:"r"`
}

func NewTwoPhaseSet() (twoPhaseSet *TwoPhaseSet, err error) {
	add := make(map[string]bool)
	remove := make(map[string]bool)

	twoPhaseSet = &TwoPhaseSet{
		Type: "2p-set",
		A:    add,
		R:    remove,
	}
	return
}

func (twoPhaseSet *TwoPhaseSet) Add(value string) (err error) {
	twoPhaseSet.A[value] = true
	return
}

func (twoPhaseSet *TwoPhaseSet) Remove(value string) (err error) {
	twoPhaseSet.R[value] = true
	return
}

func (twoPhaseSet *TwoPhaseSet) Test(value string) (is_member bool, err error) {
	if _, ok := twoPhaseSet.R[value]; ok {
		is_member = false
		return
	}

	if _, ok := twoPhaseSet.A[value]; ok {
		is_member = true
		return
	}
	return
}

func (twoPhaseSet *TwoPhaseSet) JSON() (json_bytes []byte, err error) {
	for k, _ := range twoPhaseSet.A {
		twoPhaseSet.JSONA = append(twoPhaseSet.JSONA, k)
	}

	for k, _ := range twoPhaseSet.R {
		twoPhaseSet.JSONR = append(twoPhaseSet.JSONR, k)
	}

	json_bytes, err = json.Marshal(twoPhaseSet)
	return
}

func (twoPhaseSet *TwoPhaseSet) Clone() (clone *TwoPhaseSet, err error) {
	clone = &TwoPhaseSet{
		Type: twoPhaseSet.Type,
		A:    twoPhaseSet.A,
		R:    twoPhaseSet.R,
	}
	return
}

func (twoPhaseSet *TwoPhaseSet) Merge(oTwoPhaseSet *TwoPhaseSet) (merged_set *TwoPhaseSet, err error) {
	clone, err := twoPhaseSet.Clone()
	if err != nil {
		return nil, err
	}

	merged_set = clone

	return
}
<|endoftext|>