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

// Command line test that exercises the verification and key server,
// simulating a mobile device uploading TEKs.
//
//
package main

import (
	"context"
	"encoding/base64"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/google/exposure-notifications-verification-server/pkg/clients"
	"github.com/google/exposure-notifications-verification-server/pkg/jsonclient"

	verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1"
	"github.com/google/exposure-notifications-server/pkg/logging"
	"github.com/google/exposure-notifications-server/pkg/util"
	"github.com/google/exposure-notifications-server/pkg/verification"

	"github.com/sethvargo/go-envconfig"
	"github.com/sethvargo/go-signalcontext"
)

type Config struct {
	VerificationAdminAPIServer string `env:"VERIFICATION_ADMIN_API, default=http://localhost:8081"`
	VerificationAdminAPIKey    string `env:"VERIFICATION_ADMIN_API_KEY,required"`
	VerificationAPIServer      string `env:"VERIFICATION_SERVER_API, default=http://localhost:8082"`
	VerificationAPIServerKey   string `env:"VERIFICATION_SERVER_API_KEY,required"`
	KeyServer                  string `env:"KEY_SERVER, default=http://localhost:8080"`
	HealthAuthorityCode        string `env:"HEALTH_AUTHORITY_CODE,required"`

	// Publish config
	Region string `env:"REGION,default=US"`
}

const (
	timeout        = 2 * time.Second
	oneDay         = 24 * time.Hour
	intervalLength = 10 * time.Minute
	maxInterval    = 144
)

func timeToInterval(t time.Time) int32 {
	return int32(t.UTC().Truncate(oneDay).Unix() / int64(intervalLength.Seconds()))
}

func main() {
	ctx, done := signalcontext.OnInterrupt()

	debug, _ := strconv.ParseBool(os.Getenv("LOG_DEBUG"))
	logger := logging.NewLogger(debug)
	ctx = logging.WithLogger(ctx, logger)

	err := realMain(ctx)
	done()

	if err != nil {
		logger.Fatal(err)
	}
	logger.Info("successful shutdown")
}

func realMain(ctx context.Context) error {
	logger := logging.FromContext(ctx)
	var config Config
	if err := envconfig.ProcessWith(ctx, &config, envconfig.OsLookuper()); err != nil {
		return fmt.Errorf("unable to process environment: %w", err)
	}

	doRevision := flag.Bool("revise", false, "--revise means to do a likely diagnosis and then revise to confirmed. one new key is added in between.")
	verbose := flag.Bool("v", false, "ALL THE MESSAGES!")
	flag.Parse()

	testType := "confirmed"
	iterations := 1
	if *doRevision {
		testType = "likely"
		iterations++
	}
	symptomDate := time.Now().UTC().Add(-48 * time.Hour).Format("2006-01-02")
	revisionToken := ""

	now := time.Now().UTC()
	curDayInterval := timeToInterval(now)
	nextInterval := curDayInterval

	teks := make([]verifyapi.ExposureKey, 14)
	for i := 0; i < len(teks); i++ {
		key, err := util.RandomExposureKey(nextInterval, maxInterval, 0)
		if err != nil {
			return fmt.Errorf("not enough entropy: %w", err)
		}
		teks[i] = key
		nextInterval -= maxInterval
	}

	for i := 0; i < iterations; i++ {
		// Issue the verification code.
		logger.Infof("Issuing verification code")
		codeRequest, code, err := clients.IssueCode(ctx, config.VerificationAdminAPIServer, config.VerificationAdminAPIKey, testType, symptomDate, timeout)
		if err != nil {
			return fmt.Errorf("error issuing verification code: %w", err)
		} else if code.Error != "" {
			return fmt.Errorf("issue API Error: %+v", code)
		}
		if *verbose {
			logger.Infof("Code Request: %+v", codeRequest)
			logger.Infof("Code Response: %+v", code)
		}

		// Get the verification token
		logger.Infof("Verifying code and getting token")
		tokenRequest, token, err := clients.GetToken(ctx, config.VerificationAPIServer, config.VerificationAPIServerKey, code.VerificationCode, timeout)
		if err != nil {
			return fmt.Errorf("error verifying code: %w", err)
		} else if token.Error != "" {
			return fmt.Errorf("verification API Error %+v", token)
		}
		if *verbose {
			logger.Infof("Token Request: %+v", tokenRequest)
			logger.Infof("Token Response: %+v", token)
		}

		statusReq, codeStatus, err := clients.CheckCodeStatus(ctx, config.VerificationAdminAPIServer, config.VerificationAdminAPIKey, code.UUID, timeout)
		if err != nil {
			return fmt.Errorf("error issuing verification code: %w", err)
		} else if codeStatus.Error != "" {
			return fmt.Errorf("issue API Error: %+v", codeStatus)
		}
		if *verbose {
			logger.Infof("Code Status Request: %+v", statusReq)
			logger.Infof("Code Status Response: %+v", codeStatus)
		}
		if !codeStatus.Claimed {
			return fmt.Errorf("expected claimed OTP code for %s", statusReq.UUID)
		}

		// Get the verification certificate
		logger.Infof("Calculating HMAC and getting verification certificate")
		hmacSecret := make([]byte, 32)
		hmacValue, err := verification.CalculateExposureKeyHMAC(teks, hmacSecret)
		if err != nil {
			return fmt.Errorf("error calculating tek HMAC: %w", err)
		}
		hmacB64 := base64.StdEncoding.EncodeToString(hmacValue)
		certRequest, certificate, err := clients.GetCertificate(ctx, config.VerificationAPIServer, config.VerificationAPIServerKey, token.VerificationToken, hmacB64, timeout)
		if err != nil {
			return fmt.Errorf("error getting verification certificate: %w", err)
		} else if certificate.Error != "" {
			return fmt.Errorf("certificate API Error: %+v", certificate)
		}
		if *verbose {
			logger.Infof("Certificate Request: %+v", certRequest)
			logger.Infof("Certificate Response: %+v", certificate)
		}

		// Upload the TEKs
		publish := verifyapi.Publish{
			Keys:                teks,
			HealthAuthorityID:   config.HealthAuthorityCode,
			VerificationPayload: certificate.Certificate,
			HMACKey:             base64.StdEncoding.EncodeToString(hmacSecret),
			RevisionToken:       revisionToken,
		}

		// Make the publish request.
		logger.Infof("Publish TEKs to the key server")
		var response verifyapi.PublishResponse
		client := &http.Client{
			Timeout: timeout,
		}
		if *verbose {
			logger.Infof("Publish request: %+v", publish)
		}
		if err := jsonclient.MakeRequest(ctx, client, config.KeyServer, http.Header{}, &publish, &response); err != nil {
			return fmt.Errorf("error publishing teks: %w", err)
		} else if response.ErrorMessage != "" {
			return fmt.Errorf("publish API error: %+v", response)
		}
		logger.Infof("Inserted %v exposures", response.InsertedExposures)
		if *verbose {
			logger.Infof("Publish response: %+v", response)
		}

		if *doRevision {
			testType = "confirmed"
			revisionToken = response.RevisionToken

			// Generate 1 more TEK
			key, err := util.RandomExposureKey(curDayInterval, maxInterval, 0)
			if err != nil {
				return fmt.Errorf("not enough entropy: %w", err)
			}
			teks = append(teks, key)
		}
	}

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

// Command line test that exercises the verification and key server,
// simulating a mobile device uploading TEKs.
//
//
package main

import (
	"context"
	"encoding/base64"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/google/exposure-notifications-verification-server/pkg/clients"
	"github.com/google/exposure-notifications-verification-server/pkg/jsonclient"

	verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1"
	"github.com/google/exposure-notifications-server/pkg/logging"
	"github.com/google/exposure-notifications-server/pkg/util"
	"github.com/google/exposure-notifications-server/pkg/verification"

	"github.com/sethvargo/go-envconfig"
	"github.com/sethvargo/go-signalcontext"
)

type Config struct {
	VerificationAdminAPIServer string `env:"VERIFICATION_ADMIN_API, default=http://localhost:8081"`
	VerificationAdminAPIKey    string `env:"VERIFICATION_ADMIN_API_KEY,required"`
	VerificationAPIServer      string `env:"VERIFICATION_SERVER_API, default=http://localhost:8082"`
	VerificationAPIServerKey   string `env:"VERIFICATION_SERVER_API_KEY,required"`
	KeyServer                  string `env:"KEY_SERVER, default=http://localhost:8080"`
	HealthAuthorityCode        string `env:"HEALTH_AUTHORITY_CODE,required"`

	// Publish config
	Region string `env:"REGION,default=US"`
}

const (
	timeout        = 2 * time.Second
	oneDay         = 24 * time.Hour
	intervalLength = 10 * time.Minute
	maxInterval    = 144
)

func timeToInterval(t time.Time) int32 {
	return int32(t.UTC().Truncate(oneDay).Unix() / int64(intervalLength.Seconds()))
}

func main() {
	ctx, done := signalcontext.OnInterrupt()

	debug, _ := strconv.ParseBool(os.Getenv("LOG_DEBUG"))
	logger := logging.NewLogger(debug)
	ctx = logging.WithLogger(ctx, logger)

	err := realMain(ctx)
	done()

	if err != nil {
		logger.Fatal(err)
	}
	logger.Info("successful shutdown")
}

func realMain(ctx context.Context) error {
	logger := logging.FromContext(ctx)
	var config Config
	if err := envconfig.ProcessWith(ctx, &config, envconfig.OsLookuper()); err != nil {
		return fmt.Errorf("unable to process environment: %w", err)
	}

	doRevision := flag.Bool("revise", false, "--revise means to do a likely diagnosis and then revise to confirmed. one new key is added in between.")
	verbose := flag.Bool("v", false, "ALL THE MESSAGES!")
	flag.Parse()

	testType := "confirmed"
	iterations := 1
	if *doRevision {
		testType = "likely"
		iterations++
	}
	symptomDate := time.Now().UTC().Add(-48 * time.Hour).Format("2006-01-02")
	revisionToken := ""

	now := time.Now().UTC()
	curDayInterval := timeToInterval(now)
	nextInterval := curDayInterval

	teks := make([]verifyapi.ExposureKey, 14)
	for i := 0; i < len(teks); i++ {
		key, err := util.RandomExposureKey(nextInterval, maxInterval, 0)
		if err != nil {
			return fmt.Errorf("not enough entropy: %w", err)
		}
		teks[i] = key
		nextInterval -= maxInterval
	}

	for i := 0; i < iterations; i++ {
		// Issue the verification code.
		logger.Infof("Issuing verification code")
		codeRequest, code, err := clients.IssueCode(ctx, config.VerificationAdminAPIServer, config.VerificationAdminAPIKey, testType, symptomDate, timeout)
		if err != nil {
			return fmt.Errorf("error issuing verification code: %w", err)
		} else if code.Error != "" {
			return fmt.Errorf("issue API Error: %+v", code)
		}
		if *verbose {
			logger.Infof("Code Request: %+v", codeRequest)
			logger.Infof("Code Response: %+v", code)
		}

		// Get the verification token
		logger.Infof("Verifying code and getting token")
		tokenRequest, token, err := clients.GetToken(ctx, config.VerificationAPIServer, config.VerificationAPIServerKey, code.VerificationCode, timeout)
		if err != nil {
			return fmt.Errorf("error verifying code: %w", err)
		} else if token.Error != "" {
			return fmt.Errorf("verification API Error %+v", token)
		}
		if *verbose {
			logger.Infof("Token Request: %+v", tokenRequest)
			logger.Infof("Token Response: %+v", token)
		}

		statusReq, codeStatus, err := clients.CheckCodeStatus(ctx, config.VerificationAdminAPIServer, config.VerificationAdminAPIKey, code.UUID, timeout)
		if err != nil {
			return fmt.Errorf("error check code status: %w", err)
		} else if codeStatus.Error != "" {
			return fmt.Errorf("issue API Error: %+v", codeStatus)
		}
		if *verbose {
			logger.Infof("Code Status Request: %+v", statusReq)
			logger.Infof("Code Status Response: %+v", codeStatus)
		}
		if !codeStatus.Claimed {
			return fmt.Errorf("expected claimed OTP code for %s", statusReq.UUID)
		}

		// Get the verification certificate
		logger.Infof("Calculating HMAC and getting verification certificate")
		hmacSecret := make([]byte, 32)
		hmacValue, err := verification.CalculateExposureKeyHMAC(teks, hmacSecret)
		if err != nil {
			return fmt.Errorf("error calculating tek HMAC: %w", err)
		}
		hmacB64 := base64.StdEncoding.EncodeToString(hmacValue)
		certRequest, certificate, err := clients.GetCertificate(ctx, config.VerificationAPIServer, config.VerificationAPIServerKey, token.VerificationToken, hmacB64, timeout)
		if err != nil {
			return fmt.Errorf("error getting verification certificate: %w", err)
		} else if certificate.Error != "" {
			return fmt.Errorf("certificate API Error: %+v", certificate)
		}
		if *verbose {
			logger.Infof("Certificate Request: %+v", certRequest)
			logger.Infof("Certificate Response: %+v", certificate)
		}

		// Upload the TEKs
		publish := verifyapi.Publish{
			Keys:                teks,
			HealthAuthorityID:   config.HealthAuthorityCode,
			VerificationPayload: certificate.Certificate,
			HMACKey:             base64.StdEncoding.EncodeToString(hmacSecret),
			RevisionToken:       revisionToken,
		}

		// Make the publish request.
		logger.Infof("Publish TEKs to the key server")
		var response verifyapi.PublishResponse
		client := &http.Client{
			Timeout: timeout,
		}
		if *verbose {
			logger.Infof("Publish request: %+v", publish)
		}
		if err := jsonclient.MakeRequest(ctx, client, config.KeyServer, http.Header{}, &publish, &response); err != nil {
			return fmt.Errorf("error publishing teks: %w", err)
		} else if response.ErrorMessage != "" {
			return fmt.Errorf("publish API error: %+v", response)
		}
		logger.Infof("Inserted %v exposures", response.InsertedExposures)
		if *verbose {
			logger.Infof("Publish response: %+v", response)
		}

		if *doRevision {
			testType = "confirmed"
			revisionToken = response.RevisionToken

			// Generate 1 more TEK
			key, err := util.RandomExposureKey(curDayInterval, maxInterval, 0)
			if err != nil {
				return fmt.Errorf("not enough entropy: %w", err)
			}
			teks = append(teks, key)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func dataSourceAwsEip() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsEipRead,

		Schema: map[string]*schema.Schema{
			"association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"domain": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"filter": ec2CustomFiltersSchema(),
			"id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"instance_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"network_interface_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"network_interface_owner_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"private_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"private_dns": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"public_ip": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"public_dns": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"public_ipv4_pool": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"carrier_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"customer_owned_ipv4_pool": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"customer_owned_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"tags": tagsSchemaComputed(),
		},
	}
}

func dataSourceAwsEipRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	req := &ec2.DescribeAddressesInput{}

	if v, ok := d.GetOk("id"); ok {
		req.AllocationIds = []*string{aws.String(v.(string))}
	}

	if v, ok := d.GetOk("public_ip"); ok {
		req.PublicIps = []*string{aws.String(v.(string))}
	}

	req.Filters = []*ec2.Filter{}

	req.Filters = append(req.Filters, buildEC2CustomFilterList(
		d.Get("filter").(*schema.Set),
	)...)

	if tags, tagsOk := d.GetOk("tags"); tagsOk {
		req.Filters = append(req.Filters, buildEC2TagFilterList(
			keyvaluetags.New(tags.(map[string]interface{})).Ec2Tags(),
		)...)
	}

	if len(req.Filters) == 0 {
		// Don't send an empty filters list; the EC2 API won't accept it.
		req.Filters = nil
	}

	resp, err := conn.DescribeAddresses(req)
	if err != nil {
		return fmt.Errorf("error describing EC2 Address: %s", err)
	}
	if resp == nil || len(resp.Addresses) == 0 {
		return fmt.Errorf("no matching Elastic IP found")
	}
	if len(resp.Addresses) > 1 {
		return fmt.Errorf("multiple Elastic IPs matched; use additional constraints to reduce matches to a single Elastic IP")
	}

	eip := resp.Addresses[0]

	if aws.StringValue(eip.Domain) == ec2.DomainTypeVpc {
		d.SetId(aws.StringValue(eip.AllocationId))
	} else {
		log.Printf("[DEBUG] Reading EIP, has no AllocationId, this means we have a Classic EIP, the id will also be the public ip : %s", req)
		d.SetId(aws.StringValue(eip.PublicIp))
	}

	d.Set("association_id", eip.AssociationId)
	d.Set("domain", eip.Domain)
	d.Set("instance_id", eip.InstanceId)
	d.Set("network_interface_id", eip.NetworkInterfaceId)
	d.Set("network_interface_owner_id", eip.NetworkInterfaceOwnerId)

	region := *conn.Config.Region

	d.Set("private_ip", eip.PrivateIpAddress)
	if eip.PrivateIpAddress != nil {
<<<<<<< HEAD
		dashIP := strings.Replace(*eip.PrivateIpAddress, ".", "-", -1)

		if region == endpoints.UsEast1RegionID {
			d.Set("private_dns", fmt.Sprintf("ip-%s.ec2.internal", dashIP))
		} else {
			d.Set("private_dns", fmt.Sprintf("ip-%s.%s.compute.internal", dashIP, region))
		}
=======
		d.Set("private_dns", fmt.Sprintf("ip-%s.%s", resourceAwsEc2DashIP(*eip.PrivateIpAddress), resourceAwsEc2RegionalPrivateDnsSuffix(region)))
>>>>>>> beae41d73 (tests/eip: Fix hardcoded region)
	}

	d.Set("public_ip", eip.PublicIp)
	if eip.PublicIp != nil {
<<<<<<< HEAD
		dashIP := strings.Replace(*eip.PublicIp, ".", "-", -1)

		if region == endpoints.UsEast1RegionID {
			d.Set("public_dns", meta.(*AWSClient).PartitionHostname(fmt.Sprintf("ec2-%s.compute-1", dashIP)))
		} else {
			d.Set("public_dns", meta.(*AWSClient).PartitionHostname(fmt.Sprintf("ec2-%s.%s.compute", dashIP, region)))
		}
=======
		d.Set("public_dns", meta.(*AWSClient).PartitionHostname(fmt.Sprintf("ec2-%s.%s", resourceAwsEc2DashIP(*eip.PublicIp), resourceAwsEc2RegionalPublicDnsSuffix(region))))
>>>>>>> beae41d73 (tests/eip: Fix hardcoded region)
	}
	d.Set("public_ipv4_pool", eip.PublicIpv4Pool)
	d.Set("carrier_ip", eip.CarrierIp)
	d.Set("customer_owned_ipv4_pool", eip.CustomerOwnedIpv4Pool)
	d.Set("customer_owned_ip", eip.CustomerOwnedIp)

	if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(eip.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}
<commit_msg>data/eip: Simplify IP construction<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func dataSourceAwsEip() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsEipRead,

		Schema: map[string]*schema.Schema{
			"association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"domain": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"filter": ec2CustomFiltersSchema(),
			"id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"instance_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"network_interface_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"network_interface_owner_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"private_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"private_dns": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"public_ip": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"public_dns": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"public_ipv4_pool": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"carrier_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"customer_owned_ipv4_pool": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"customer_owned_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"tags": tagsSchemaComputed(),
		},
	}
}

func dataSourceAwsEipRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	req := &ec2.DescribeAddressesInput{}

	if v, ok := d.GetOk("id"); ok {
		req.AllocationIds = []*string{aws.String(v.(string))}
	}

	if v, ok := d.GetOk("public_ip"); ok {
		req.PublicIps = []*string{aws.String(v.(string))}
	}

	req.Filters = []*ec2.Filter{}

	req.Filters = append(req.Filters, buildEC2CustomFilterList(
		d.Get("filter").(*schema.Set),
	)...)

	if tags, tagsOk := d.GetOk("tags"); tagsOk {
		req.Filters = append(req.Filters, buildEC2TagFilterList(
			keyvaluetags.New(tags.(map[string]interface{})).Ec2Tags(),
		)...)
	}

	if len(req.Filters) == 0 {
		// Don't send an empty filters list; the EC2 API won't accept it.
		req.Filters = nil
	}

	resp, err := conn.DescribeAddresses(req)
	if err != nil {
		return fmt.Errorf("error describing EC2 Address: %s", err)
	}
	if resp == nil || len(resp.Addresses) == 0 {
		return fmt.Errorf("no matching Elastic IP found")
	}
	if len(resp.Addresses) > 1 {
		return fmt.Errorf("multiple Elastic IPs matched; use additional constraints to reduce matches to a single Elastic IP")
	}

	eip := resp.Addresses[0]

	if aws.StringValue(eip.Domain) == ec2.DomainTypeVpc {
		d.SetId(aws.StringValue(eip.AllocationId))
	} else {
		log.Printf("[DEBUG] Reading EIP, has no AllocationId, this means we have a Classic EIP, the id will also be the public ip : %s", req)
		d.SetId(aws.StringValue(eip.PublicIp))
	}

	d.Set("association_id", eip.AssociationId)
	d.Set("domain", eip.Domain)
	d.Set("instance_id", eip.InstanceId)
	d.Set("network_interface_id", eip.NetworkInterfaceId)
	d.Set("network_interface_owner_id", eip.NetworkInterfaceOwnerId)

	region := *conn.Config.Region

	d.Set("private_ip", eip.PrivateIpAddress)
	if eip.PrivateIpAddress != nil {
		d.Set("private_dns", fmt.Sprintf("ip-%s.%s", resourceAwsEc2DashIP(*eip.PrivateIpAddress), resourceAwsEc2RegionalPrivateDnsSuffix(region)))
	}

	d.Set("public_ip", eip.PublicIp)
	if eip.PublicIp != nil {
		d.Set("public_dns", meta.(*AWSClient).PartitionHostname(fmt.Sprintf("ec2-%s.%s", resourceAwsEc2DashIP(*eip.PublicIp), resourceAwsEc2RegionalPublicDnsSuffix(region))))
	}
	d.Set("public_ipv4_pool", eip.PublicIpv4Pool)
	d.Set("carrier_ip", eip.CarrierIp)
	d.Set("customer_owned_ipv4_pool", eip.CustomerOwnedIpv4Pool)
	d.Set("customer_owned_ip", eip.CustomerOwnedIp)

	if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(eip.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 ca

import (
	"crypto/x509"
	"net"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	"github.com/jetstack/cert-manager/test/e2e/util"
)

var _ = framework.CertManagerDescribe("CA CertificateRequest", func() {
	f := framework.NewDefaultFramework("create-ca-certificate")
	h := f.Helper()

	issuerName := "test-ca-issuer"
	issuerSecretName := "ca-issuer-signing-keypair"
	certificateRequestName := "test-ca-certificaterequest"

	exampleDNSNames := []string{"dnsName1.co", "dnsName2.ninja"}
	exampleIPAddresses := []net.IP{
		[]byte{8, 8, 8, 8},
		[]byte{1, 1, 1, 1},
	}
	exampleURIs := []string{"spiffe://foo.foo.example.net", "spiffe://foo.bar.example.net"}

	JustBeforeEach(func() {
		By("Creating an Issuer")
		_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerCAIssuer(issuerName, issuerSecretName))
		Expect(err).NotTo(HaveOccurred())
		By("Waiting for Issuer to become Ready")
		err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
			issuerName,
			v1alpha1.IssuerCondition{
				Type:   v1alpha1.IssuerConditionReady,
				Status: v1alpha1.ConditionTrue,
			})
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		By("Cleaning up")
		f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(issuerSecretName, nil)
		f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Delete(issuerName, nil)
	})

	Context("when the CA is the root", func() {
		BeforeEach(func() {
			By("Creating a signing keypair fixture")
			_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(newSigningKeypairSecret(issuerSecretName))
			Expect(err).NotTo(HaveOccurred())
		})

		It("should generate a valid certificate from CSR", func() {
			certRequestClient := f.CertManagerClientSet.CertmanagerV1alpha1().CertificateRequests(f.Namespace.Name)

			By("Creating a CertificateRequest")
			cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1alpha1.IssuerKind,
				&metav1.Duration{
					Duration: time.Hour * 24 * 90,
				},
				exampleDNSNames, exampleIPAddresses, exampleURIs, x509.RSA)
			Expect(err).NotTo(HaveOccurred())
			_, err = certRequestClient.Create(cr)
			Expect(err).NotTo(HaveOccurred())
			By("Verifying the Certificate is valid")
			err = h.WaitCertificateRequestIssuedValidTLS(f.Namespace.Name, certificateRequestName, time.Second*30, key, []byte(rootCert))
			Expect(err).NotTo(HaveOccurred())
		})

		It("should be able to obtain an ECDSA key from a RSA backed issuer", func() {
			certRequestClient := f.CertManagerClientSet.CertmanagerV1alpha1().CertificateRequests(f.Namespace.Name)

			By("Creating a CertificateRequest")
			cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1alpha1.IssuerKind,
				&metav1.Duration{
					Duration: time.Hour * 24 * 90,
				},
				exampleDNSNames, exampleIPAddresses, exampleURIs, x509.ECDSA)
			Expect(err).NotTo(HaveOccurred())
			_, err = certRequestClient.Create(cr)
			Expect(err).NotTo(HaveOccurred())
			By("Verifying the Certificate is valid")
			err = h.WaitCertificateRequestIssuedValidTLS(f.Namespace.Name, certificateRequestName, time.Second*30, key, []byte(rootCert))
			Expect(err).NotTo(HaveOccurred())
		})

		cases := []struct {
			inputDuration    *metav1.Duration
			expectedDuration time.Duration
			label            string
		}{
			{
				inputDuration:    &metav1.Duration{time.Hour * 24 * 35},
				expectedDuration: time.Hour * 24 * 35,
				label:            "35 days",
			},
			{
				inputDuration:    nil,
				expectedDuration: time.Hour * 24 * 90,
				label:            "the default duration (90 days)",
			},
		}
		for _, v := range cases {
			v := v
			It("should generate a signed certificate valid for "+v.label, func() {
				crClient := f.CertManagerClientSet.CertmanagerV1alpha1().CertificateRequests(f.Namespace.Name)

				By("Creating a CertificateRequest")
				cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1alpha1.IssuerKind, v.inputDuration,
					exampleDNSNames, exampleIPAddresses, exampleURIs, x509.RSA)
				Expect(err).NotTo(HaveOccurred())
				cr, err = crClient.Create(cr)
				Expect(err).NotTo(HaveOccurred())

				By("Verifying the CertificateRequest is valid")
				err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Second*30, key)
				Expect(err).NotTo(HaveOccurred())
				f.CertificateRequestDurationValid(cr, v.expectedDuration)
			})
		}
	})
})
<commit_msg>Update cr variable after waiting for it to be issued<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 ca

import (
	"crypto/x509"
	"net"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack/cert-manager/test/e2e/framework"
	"github.com/jetstack/cert-manager/test/e2e/util"
)

var _ = framework.CertManagerDescribe("CA CertificateRequest", func() {
	f := framework.NewDefaultFramework("create-ca-certificate")
	h := f.Helper()

	issuerName := "test-ca-issuer"
	issuerSecretName := "ca-issuer-signing-keypair"
	certificateRequestName := "test-ca-certificaterequest"

	exampleDNSNames := []string{"dnsName1.co", "dnsName2.ninja"}
	exampleIPAddresses := []net.IP{
		[]byte{8, 8, 8, 8},
		[]byte{1, 1, 1, 1},
	}
	exampleURIs := []string{"spiffe://foo.foo.example.net", "spiffe://foo.bar.example.net"}

	JustBeforeEach(func() {
		By("Creating an Issuer")
		_, err := f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Create(util.NewCertManagerCAIssuer(issuerName, issuerSecretName))
		Expect(err).NotTo(HaveOccurred())
		By("Waiting for Issuer to become Ready")
		err = util.WaitForIssuerCondition(f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name),
			issuerName,
			v1alpha1.IssuerCondition{
				Type:   v1alpha1.IssuerConditionReady,
				Status: v1alpha1.ConditionTrue,
			})
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		By("Cleaning up")
		f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Delete(issuerSecretName, nil)
		f.CertManagerClientSet.CertmanagerV1alpha1().Issuers(f.Namespace.Name).Delete(issuerName, nil)
	})

	Context("when the CA is the root", func() {
		BeforeEach(func() {
			By("Creating a signing keypair fixture")
			_, err := f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(newSigningKeypairSecret(issuerSecretName))
			Expect(err).NotTo(HaveOccurred())
		})

		It("should generate a valid certificate from CSR", func() {
			certRequestClient := f.CertManagerClientSet.CertmanagerV1alpha1().CertificateRequests(f.Namespace.Name)

			By("Creating a CertificateRequest")
			cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1alpha1.IssuerKind,
				&metav1.Duration{
					Duration: time.Hour * 24 * 90,
				},
				exampleDNSNames, exampleIPAddresses, exampleURIs, x509.RSA)
			Expect(err).NotTo(HaveOccurred())
			_, err = certRequestClient.Create(cr)
			Expect(err).NotTo(HaveOccurred())
			By("Verifying the Certificate is valid")
			err = h.WaitCertificateRequestIssuedValidTLS(f.Namespace.Name, certificateRequestName, time.Second*30, key, []byte(rootCert))
			Expect(err).NotTo(HaveOccurred())
		})

		It("should be able to obtain an ECDSA key from a RSA backed issuer", func() {
			certRequestClient := f.CertManagerClientSet.CertmanagerV1alpha1().CertificateRequests(f.Namespace.Name)

			By("Creating a CertificateRequest")
			cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1alpha1.IssuerKind,
				&metav1.Duration{
					Duration: time.Hour * 24 * 90,
				},
				exampleDNSNames, exampleIPAddresses, exampleURIs, x509.ECDSA)
			Expect(err).NotTo(HaveOccurred())
			_, err = certRequestClient.Create(cr)
			Expect(err).NotTo(HaveOccurred())
			By("Verifying the Certificate is valid")
			err = h.WaitCertificateRequestIssuedValidTLS(f.Namespace.Name, certificateRequestName, time.Second*30, key, []byte(rootCert))
			Expect(err).NotTo(HaveOccurred())
		})

		cases := []struct {
			inputDuration    *metav1.Duration
			expectedDuration time.Duration
			label            string
		}{
			{
				inputDuration:    &metav1.Duration{time.Hour * 24 * 35},
				expectedDuration: time.Hour * 24 * 35,
				label:            "35 days",
			},
			{
				inputDuration:    nil,
				expectedDuration: time.Hour * 24 * 90,
				label:            "the default duration (90 days)",
			},
		}
		for _, v := range cases {
			v := v
			It("should generate a signed certificate valid for "+v.label, func() {
				crClient := f.CertManagerClientSet.CertmanagerV1alpha1().CertificateRequests(f.Namespace.Name)

				By("Creating a CertificateRequest")
				cr, key, err := util.NewCertManagerBasicCertificateRequest(certificateRequestName, issuerName, v1alpha1.IssuerKind, v.inputDuration,
					exampleDNSNames, exampleIPAddresses, exampleURIs, x509.RSA)
				Expect(err).NotTo(HaveOccurred())
				cr, err = crClient.Create(cr)
				Expect(err).NotTo(HaveOccurred())

				By("Verifying the CertificateRequest is valid")
				err = h.WaitCertificateRequestIssuedValid(f.Namespace.Name, certificateRequestName, time.Second*30, key)
				Expect(err).NotTo(HaveOccurred())
				cr, err = crClient.Get(cr.Name, metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())
				f.CertificateRequestDurationValid(cr, v.expectedDuration)
			})
		}
	})
})
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/service/directconnect"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceAwsDxLag() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDxLagCreate,
		Read:   resourceAwsDxLagRead,
		Update: resourceAwsDxLagUpdate,
		Delete: resourceAwsDxLagDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"connections_bandwidth": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateDxConnectionBandWidth(),
			},
			"location": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"number_of_connections": {
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Removed:  "Use `aws_dx_connection` and `aws_dx_connection_association` resources instead",
			},
			"force_destroy": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"jumbo_frame_capable": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"tags": tagsSchema(),
			"has_logical_redundancy": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

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

	req := &directconnect.CreateLagInput{
		ConnectionsBandwidth: aws.String(d.Get("connections_bandwidth").(string)),
		LagName:              aws.String(d.Get("name").(string)),
		Location:             aws.String(d.Get("location").(string)),
		NumberOfConnections:  aws.Int64(int64(1)),
	}

	log.Printf("[DEBUG] Creating Direct Connect LAG: %#v", req)
	resp, err := conn.CreateLag(req)
	if err != nil {
		return fmt.Errorf("error creating Direct Connect LAG: %s", err)
	}

	d.SetId(aws.StringValue(resp.LagId))

	// Delete unmanaged connection
	connectionID := aws.StringValue(resp.Connections[0].ConnectionId)
	deleteConnectionInput := &directconnect.DeleteConnectionInput{
		ConnectionId: resp.Connections[0].ConnectionId,
	}

	log.Printf("[DEBUG] Deleting newly created and unmanaged Direct Connect LAG (%s) Connection: %s", d.Id(), connectionID)
	if _, err := conn.DeleteConnection(deleteConnectionInput); err != nil {
		return fmt.Errorf("error deleting newly created and unmanaged Direct Connect LAG (%s) Connection (%s): %s", d.Id(), connectionID, err)
	}

	return resourceAwsDxLagUpdate(d, meta)
}

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

	resp, err := conn.DescribeLags(&directconnect.DescribeLagsInput{
		LagId: aws.String(d.Id()),
	})
	if err != nil {
		if isNoSuchDxLagErr(err) {
			log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return err
	}

	if len(resp.Lags) < 1 {
		log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}
	if len(resp.Lags) != 1 {
		return fmt.Errorf("Number of Direct Connect LAGs (%s) isn't one, got %d", d.Id(), len(resp.Lags))
	}
	lag := resp.Lags[0]
	if d.Id() != aws.StringValue(lag.LagId) {
		return fmt.Errorf("Direct Connect LAG (%s) not found", d.Id())
	}

	if aws.StringValue(lag.LagState) == directconnect.LagStateDeleted {
		log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Region:    meta.(*AWSClient).region,
		Service:   "directconnect",
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("dxlag/%s", d.Id()),
	}.String()
	d.Set("arn", arn)
	d.Set("name", lag.LagName)
	d.Set("connections_bandwidth", lag.ConnectionsBandwidth)
	d.Set("location", lag.Location)
	d.Set("jumbo_frame_capable", lag.JumboFrameCapable)
	d.Set("has_logical_redundancy", lag.HasLogicalRedundancy)

	err1 := getTagsDX(conn, d, arn)
	return err1
}

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

	d.Partial(true)

	if d.HasChange("name") {
		req := &directconnect.UpdateLagInput{
			LagId:   aws.String(d.Id()),
			LagName: aws.String(d.Get("name").(string)),
		}

		log.Printf("[DEBUG] Updating Direct Connect LAG: %#v", req)
		_, err := conn.UpdateLag(req)
		if err != nil {
			return err
		} else {
			d.SetPartial("name")
		}
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Region:    meta.(*AWSClient).region,
		Service:   "directconnect",
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("dxlag/%s", d.Id()),
	}.String()
	if err := setTagsDX(conn, d, arn); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	d.Partial(false)

	return resourceAwsDxLagRead(d, meta)
}

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

	if d.Get("force_destroy").(bool) {
		resp, err := conn.DescribeLags(&directconnect.DescribeLagsInput{
			LagId: aws.String(d.Id()),
		})
		if err != nil {
			if isNoSuchDxLagErr(err) {
				return nil
			}
			return err
		}

		if len(resp.Lags) < 1 {
			return nil
		}
		lag := resp.Lags[0]
		for _, v := range lag.Connections {
			log.Printf("[DEBUG] Deleting Direct Connect connection: %s", aws.StringValue(v.ConnectionId))
			_, err := conn.DeleteConnection(&directconnect.DeleteConnectionInput{
				ConnectionId: v.ConnectionId,
			})
			if err != nil && !isNoSuchDxConnectionErr(err) {
				return err
			}
		}
	}

	log.Printf("[DEBUG] Deleting Direct Connect LAG: %s", d.Id())
	_, err := conn.DeleteLag(&directconnect.DeleteLagInput{
		LagId: aws.String(d.Id()),
	})
	if err != nil {
		if isNoSuchDxLagErr(err) {
			return nil
		}
		return err
	}

	deleteStateConf := &resource.StateChangeConf{
		Pending:    []string{directconnect.LagStateAvailable, directconnect.LagStateRequested, directconnect.LagStatePending, directconnect.LagStateDeleting},
		Target:     []string{directconnect.LagStateDeleted},
		Refresh:    dxLagRefreshStateFunc(conn, d.Id()),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}
	_, err = deleteStateConf.WaitForState()
	if err != nil {
		return fmt.Errorf("Error waiting for Direct Connect LAG (%s) to be deleted: %s", d.Id(), err)
	}

	return nil
}

func dxLagRefreshStateFunc(conn *directconnect.DirectConnect, lagId string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		input := &directconnect.DescribeLagsInput{
			LagId: aws.String(lagId),
		}
		resp, err := conn.DescribeLags(input)
		if err != nil {
			return nil, "failed", err
		}
		if len(resp.Lags) < 1 {
			return resp, directconnect.LagStateDeleted, nil
		}
		return resp, *resp.Lags[0].LagState, nil
	}
}

func isNoSuchDxLagErr(err error) bool {
	return isAWSErr(err, "DirectConnectClientException", "Could not find Lag with ID")
}
<commit_msg>r/aws_dx_lag: Refactor tagging logic to keyvaluetags package.<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/service/directconnect"
	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

func resourceAwsDxLag() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsDxLagCreate,
		Read:   resourceAwsDxLagRead,
		Update: resourceAwsDxLagUpdate,
		Delete: resourceAwsDxLagDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"connections_bandwidth": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateDxConnectionBandWidth(),
			},
			"location": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"number_of_connections": {
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
				ForceNew: true,
				Removed:  "Use `aws_dx_connection` and `aws_dx_connection_association` resources instead",
			},
			"force_destroy": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"jumbo_frame_capable": {
				Type:     schema.TypeBool,
				Computed: true,
			},
			"tags": tagsSchema(),
			"has_logical_redundancy": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

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

	req := &directconnect.CreateLagInput{
		ConnectionsBandwidth: aws.String(d.Get("connections_bandwidth").(string)),
		LagName:              aws.String(d.Get("name").(string)),
		Location:             aws.String(d.Get("location").(string)),
		NumberOfConnections:  aws.Int64(int64(1)),
	}

	if v := d.Get("tags").(map[string]interface{}); len(v) > 0 {
		req.Tags = keyvaluetags.New(v).IgnoreAws().DirectconnectTags()
	}

	log.Printf("[DEBUG] Creating Direct Connect LAG: %#v", req)
	resp, err := conn.CreateLag(req)
	if err != nil {
		return fmt.Errorf("error creating Direct Connect LAG: %s", err)
	}

	d.SetId(aws.StringValue(resp.LagId))

	// Delete unmanaged connection
	connectionID := aws.StringValue(resp.Connections[0].ConnectionId)
	deleteConnectionInput := &directconnect.DeleteConnectionInput{
		ConnectionId: resp.Connections[0].ConnectionId,
	}

	log.Printf("[DEBUG] Deleting newly created and unmanaged Direct Connect LAG (%s) Connection: %s", d.Id(), connectionID)
	if _, err := conn.DeleteConnection(deleteConnectionInput); err != nil {
		return fmt.Errorf("error deleting newly created and unmanaged Direct Connect LAG (%s) Connection (%s): %s", d.Id(), connectionID, err)
	}

	return resourceAwsDxLagRead(d, meta)
}

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

	resp, err := conn.DescribeLags(&directconnect.DescribeLagsInput{
		LagId: aws.String(d.Id()),
	})
	if err != nil {
		if isNoSuchDxLagErr(err) {
			log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id())
			d.SetId("")
			return nil
		}
		return err
	}

	if len(resp.Lags) < 1 {
		log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}
	if len(resp.Lags) != 1 {
		return fmt.Errorf("Number of Direct Connect LAGs (%s) isn't one, got %d", d.Id(), len(resp.Lags))
	}
	lag := resp.Lags[0]
	if d.Id() != aws.StringValue(lag.LagId) {
		return fmt.Errorf("Direct Connect LAG (%s) not found", d.Id())
	}

	if aws.StringValue(lag.LagState) == directconnect.LagStateDeleted {
		log.Printf("[WARN] Direct Connect LAG (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	arn := arn.ARN{
		Partition: meta.(*AWSClient).partition,
		Region:    meta.(*AWSClient).region,
		Service:   "directconnect",
		AccountID: meta.(*AWSClient).accountid,
		Resource:  fmt.Sprintf("dxlag/%s", d.Id()),
	}.String()
	d.Set("arn", arn)
	d.Set("name", lag.LagName)
	d.Set("connections_bandwidth", lag.ConnectionsBandwidth)
	d.Set("location", lag.Location)
	d.Set("jumbo_frame_capable", lag.JumboFrameCapable)
	d.Set("has_logical_redundancy", lag.HasLogicalRedundancy)

	tags, err := keyvaluetags.DirectconnectListTags(conn, arn)

	if err != nil {
		return fmt.Errorf("error listing tags for Direct Connect LAG (%s): %s", arn, err)
	}

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

	return nil
}

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

	if d.HasChange("name") {
		req := &directconnect.UpdateLagInput{
			LagId:   aws.String(d.Id()),
			LagName: aws.String(d.Get("name").(string)),
		}

		log.Printf("[DEBUG] Updating Direct Connect LAG: %#v", req)
		_, err := conn.UpdateLag(req)
		if err != nil {
			return fmt.Errorf("error updating Direct Connect LAG (%s): %s", d.Id(), err)
		}
	}

	arn := d.Get("arn").(string)
	if d.HasChange("tags") {
		o, n := d.GetChange("tags")

		if err := keyvaluetags.DirectconnectUpdateTags(conn, arn, o, n); err != nil {
			return fmt.Errorf("error updating Direct Connect LAG (%s) tags: %s", arn, err)
		}
	}

	return resourceAwsDxLagRead(d, meta)
}

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

	if d.Get("force_destroy").(bool) {
		resp, err := conn.DescribeLags(&directconnect.DescribeLagsInput{
			LagId: aws.String(d.Id()),
		})
		if err != nil {
			if isNoSuchDxLagErr(err) {
				return nil
			}
			return err
		}

		if len(resp.Lags) < 1 {
			return nil
		}
		lag := resp.Lags[0]
		for _, v := range lag.Connections {
			log.Printf("[DEBUG] Deleting Direct Connect connection: %s", aws.StringValue(v.ConnectionId))
			_, err := conn.DeleteConnection(&directconnect.DeleteConnectionInput{
				ConnectionId: v.ConnectionId,
			})
			if err != nil && !isNoSuchDxConnectionErr(err) {
				return err
			}
		}
	}

	log.Printf("[DEBUG] Deleting Direct Connect LAG: %s", d.Id())
	_, err := conn.DeleteLag(&directconnect.DeleteLagInput{
		LagId: aws.String(d.Id()),
	})
	if err != nil {
		if isNoSuchDxLagErr(err) {
			return nil
		}
		return err
	}

	deleteStateConf := &resource.StateChangeConf{
		Pending:    []string{directconnect.LagStateAvailable, directconnect.LagStateRequested, directconnect.LagStatePending, directconnect.LagStateDeleting},
		Target:     []string{directconnect.LagStateDeleted},
		Refresh:    dxLagRefreshStateFunc(conn, d.Id()),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}
	_, err = deleteStateConf.WaitForState()
	if err != nil {
		return fmt.Errorf("Error waiting for Direct Connect LAG (%s) to be deleted: %s", d.Id(), err)
	}

	return nil
}

func dxLagRefreshStateFunc(conn *directconnect.DirectConnect, lagId string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		input := &directconnect.DescribeLagsInput{
			LagId: aws.String(lagId),
		}
		resp, err := conn.DescribeLags(input)
		if err != nil {
			return nil, "failed", err
		}
		if len(resp.Lags) < 1 {
			return resp, directconnect.LagStateDeleted, nil
		}
		return resp, *resp.Lags[0].LagState, nil
	}
}

func isNoSuchDxLagErr(err error) bool {
	return isAWSErr(err, "DirectConnectClientException", "Could not find Lag with ID")
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes/empty"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/grpcplugin/platformplugin"
	"github.com/ovh/cds/sdk/interpolate"
)

/*
This plugin have to be used as a deployment platform plugin

Arsenal deployment plugin must configured as following:
	name: arsenal-deployment-plugin
	type: platform
	author: "François Samin"
	description: "OVH Arsenal Deployment Plugin"

$ cdsctl admin plugins import arsenal-deployment-plugin.yml

Build the present binaries and import in CDS:
	os: linux
	arch: amd64
	cmd: <path-to-binary-file>

$ cdsctl admin plugins binary-add arsenal-deployment-plugin arsenal-deployment-plugin-bin.yml <path-to-binary-file>

Arsenal platform must configured as following
	name: Arsenal
	default_config:
		host:
			type: string
	deployment: true
	deployment_default_config:
		version:
			type: string
		deployment.token:
			type: password
		retry.max:
			type: string
			value: 10
		retry.delay:
			type: string
			value 5
	plugin: arsenal-deployment-plugin
*/

type arsenalDeploymentPlugin struct {
	platformplugin.Common
}

func (e *arsenalDeploymentPlugin) Manifest(ctx context.Context, _ *empty.Empty) (*platformplugin.PlatformPluginManifest, error) {
	return &platformplugin.PlatformPluginManifest{
		Name:        "OVH Arsenal Deployment Plugin",
		Author:      "François Samin",
		Description: "OVH Arsenal Deployment Plugin",
		Version:     sdk.VERSION,
	}, nil
}

const deployData = `{
	"version": "{{.cds.platform.version}}",
	"metadata": {
		"CDS_APPLICATION": "{{.cds.application}}",
		"CDS_RUN": "{{.cds.run}}",
		"CDS_ENVIRONMENT": "{{.cds.platform}}",
		"CDS_GIT_BRANCH": "{{.git.branch}}",
		"CDS_WORKFLOW": "{{.cds.workflow}}",
		"CDS_PROJECT": "{{.cds.project}}",
		"CDS_VERSION": "{{.cds.version}}",
		"CDS_GIT_REPOSITORY": "{{.git.repository}}",
		"CDS_GIT_HASH": "{{.git.hash}}"
	}
}`

func (e *arsenalDeploymentPlugin) Deploy(ctx context.Context, q *platformplugin.DeployQuery) (*platformplugin.DeployResult, error) {
	var application = q.GetOptions()["cds.application"]
	var arsenalHost = q.GetOptions()["cds.platform.host"]
	var arsenalDeploymentToken = q.GetOptions()["cds.platform.deployment.token"]
	var maxRetryStr = q.GetOptions()["cds.platform.retry.max"]
	var delayRetryStr = q.GetOptions()["cds.platform.retry.delay"]
	maxRetry, err := strconv.Atoi(maxRetryStr)
	if err != nil {
		fmt.Printf("Error parsing cds.platform.retry.max: %v. Default value will be used\n", err)
		maxRetry = 10
	}
	delayRetry, err := strconv.Atoi(delayRetryStr)
	if err != nil {
		fmt.Printf("Error parsing cds.platform.retry.max: %v. Default value will be used\n", err)
		delayRetry = 5
	}

	deployData, err := interpolate.Do(deployData, q.GetOptions())
	if err != nil {
		return fail("Error: unable to interpolate data: %v. Please check you platform configuration\n", err)
	}

	httpClient := &http.Client{
		Timeout: 15 * time.Second,
	}

	// Prepare the request
	req, err := http.NewRequest(http.MethodPost, arsenalHost+"/deploy", strings.NewReader(deployData))
	if err != nil {
		return fail("Error: unable to prepare request on %s/deploy: %v", arsenalHost, err)
	}
	req.Header.Add("X-Arsenal-Deployment-Token", arsenalDeploymentToken)

	fmt.Printf("Deploying %s on Arsenal at %s...\n", application, arsenalHost)

	// Do the request
	res, err := httpClient.Do(req)
	if err != nil {
		return fail("Error: Post %s/deploy failed: %v. Please check you platform configuration", arsenalHost, err)
	}
	defer res.Body.Close()

	//Check the result
	body, _ := ioutil.ReadAll(res.Body)
	if res.StatusCode != http.StatusOK {
		fmt.Println(string(body))
		return fail("deployment failure")
	}

	//Read the followUp token
	bodyResult := map[string]string{}
	if err := json.Unmarshal(body, &bodyResult); err != nil {
		return fail("Error: Unable to read body: %v", err)
	}
	var followUpToken = bodyResult["followup_token"]

	//Retry loop to follow the deployment status
	var retry = 0
	var success bool
	for retry < maxRetry {
		if retry > 0 {
			fmt.Printf("Retrying in %s seconds...\n", delayRetryStr)
			time.Sleep(time.Duration(delayRetry) * time.Second)
		}

		fmt.Println("Fetching followup status on deployment...")
		req, err := http.NewRequest(http.MethodGet, arsenalHost+"/follow", nil)
		if err != nil {
			return fail("Error: unable to prepare request on %s/follow: %v", arsenalHost, err)
		}
		req.Header.Add("X-Arsenal-Followup-Token", followUpToken)

		res, err := httpClient.Do(req)
		if err != nil {
			return fail("Deployment failed: %v. Please check you platform configuration", err)
		}
		defer res.Body.Close()

		body, _ := ioutil.ReadAll(res.Body)
		if res.StatusCode != http.StatusOK {
			fmt.Println(string(body))
			return fail("deployment failure")
		}

		//Read the followUp token
		bodyResult := map[string]interface{}{}
		if err := json.Unmarshal(body, &bodyResult); err != nil {
			return fail("Error: Unable to read body: %v", err)
		}

		doneB, doneIsBool := bodyResult["done"].(bool)
		doneS, doneIsString := bodyResult["done"].(string)
		if (doneIsBool && doneB) || (doneIsString && doneS == "true") {
			success = true
			break
		} else {
			fmt.Println("Not done yet")
		}
		retry++
	}

	if !success {
		return fail("deployment failed")
	}

	return &platformplugin.DeployResult{
		Status: sdk.StatusSuccess.String(),
	}, nil
}

func (e *arsenalDeploymentPlugin) DeployStatus(ctx context.Context, q *platformplugin.DeployStatusQuery) (*platformplugin.DeployResult, error) {
	return &platformplugin.DeployResult{
		Status: sdk.StatusSuccess.String(),
	}, nil
}

func main() {
	e := arsenalDeploymentPlugin{}
	if err := platformplugin.Start(context.Background(), &e); err != nil {
		panic(err)
	}
	return

}

func fail(format string, args ...interface{}) (*platformplugin.DeployResult, error) {
	msg := fmt.Sprintf(format, args...)
	fmt.Println(msg)
	return &platformplugin.DeployResult{
		Details: msg,
		Status:  sdk.StatusFail.String(),
	}, nil
}
<commit_msg>feat(plugin/arsenal): log body if err (#2715)<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes/empty"

	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/grpcplugin/platformplugin"
	"github.com/ovh/cds/sdk/interpolate"
)

/*
This plugin have to be used as a deployment platform plugin

Arsenal deployment plugin must configured as following:
	name: arsenal-deployment-plugin
	type: platform
	author: "François Samin"
	description: "OVH Arsenal Deployment Plugin"

$ cdsctl admin plugins import arsenal-deployment-plugin.yml

Build the present binaries and import in CDS:
	os: linux
	arch: amd64
	cmd: <path-to-binary-file>

$ cdsctl admin plugins binary-add arsenal-deployment-plugin arsenal-deployment-plugin-bin.yml <path-to-binary-file>

Arsenal platform must configured as following
	name: Arsenal
	default_config:
		host:
			type: string
	deployment: true
	deployment_default_config:
		version:
			type: string
		deployment.token:
			type: password
		retry.max:
			type: string
			value: 10
		retry.delay:
			type: string
			value 5
	plugin: arsenal-deployment-plugin
*/

type arsenalDeploymentPlugin struct {
	platformplugin.Common
}

func (e *arsenalDeploymentPlugin) Manifest(ctx context.Context, _ *empty.Empty) (*platformplugin.PlatformPluginManifest, error) {
	return &platformplugin.PlatformPluginManifest{
		Name:        "OVH Arsenal Deployment Plugin",
		Author:      "François Samin",
		Description: "OVH Arsenal Deployment Plugin",
		Version:     sdk.VERSION,
	}, nil
}

const deployData = `{
	"version": "{{.cds.platform.version}}",
	"metadata": {
		"CDS_APPLICATION": "{{.cds.application}}",
		"CDS_RUN": "{{.cds.run}}",
		"CDS_ENVIRONMENT": "{{.cds.platform}}",
		"CDS_GIT_BRANCH": "{{.git.branch}}",
		"CDS_WORKFLOW": "{{.cds.workflow}}",
		"CDS_PROJECT": "{{.cds.project}}",
		"CDS_VERSION": "{{.cds.version}}",
		"CDS_GIT_REPOSITORY": "{{.git.repository}}",
		"CDS_GIT_HASH": "{{.git.hash}}"
	}
}`

func (e *arsenalDeploymentPlugin) Deploy(ctx context.Context, q *platformplugin.DeployQuery) (*platformplugin.DeployResult, error) {
	var application = q.GetOptions()["cds.application"]
	var arsenalHost = q.GetOptions()["cds.platform.host"]
	var arsenalDeploymentToken = q.GetOptions()["cds.platform.deployment.token"]
	var maxRetryStr = q.GetOptions()["cds.platform.retry.max"]
	var delayRetryStr = q.GetOptions()["cds.platform.retry.delay"]
	maxRetry, err := strconv.Atoi(maxRetryStr)
	if err != nil {
		fmt.Printf("Error parsing cds.platform.retry.max: %v. Default value will be used\n", err)
		maxRetry = 10
	}
	delayRetry, err := strconv.Atoi(delayRetryStr)
	if err != nil {
		fmt.Printf("Error parsing cds.platform.retry.max: %v. Default value will be used\n", err)
		delayRetry = 5
	}

	deployData, err := interpolate.Do(deployData, q.GetOptions())
	if err != nil {
		return fail("Error: unable to interpolate data: %v. Please check you platform configuration\n", err)
	}

	httpClient := &http.Client{
		Timeout: 15 * time.Second,
	}

	// Prepare the request
	req, err := http.NewRequest(http.MethodPost, arsenalHost+"/deploy", strings.NewReader(deployData))
	if err != nil {
		return fail("Error: unable to prepare request on %s/deploy: %v", arsenalHost, err)
	}
	req.Header.Add("X-Arsenal-Deployment-Token", arsenalDeploymentToken)

	fmt.Printf("Deploying %s on Arsenal at %s...\n", application, arsenalHost)

	// Do the request
	res, err := httpClient.Do(req)
	if err != nil {
		return fail("Error: Post %s/deploy failed: %v. Please check you platform configuration", arsenalHost, err)
	}
	defer res.Body.Close()

	//Check the result
	body, _ := ioutil.ReadAll(res.Body)
	if res.StatusCode != http.StatusOK {
		fmt.Println("Body: ", string(body))
		return fail("deployment failure")
	}

	//Read the followUp token
	bodyResult := map[string]string{}
	if err := json.Unmarshal(body, &bodyResult); err != nil {
		return fail("Error: Unable to read body: %v", err)
	}
	var followUpToken = bodyResult["followup_token"]

	//Retry loop to follow the deployment status
	var retry = 0
	var success bool
	for retry < maxRetry {
		if retry > 0 {
			fmt.Printf("Retrying in %s seconds...\n", delayRetryStr)
			time.Sleep(time.Duration(delayRetry) * time.Second)
		}

		fmt.Println("Fetching followup status on deployment...")
		req, err := http.NewRequest(http.MethodGet, arsenalHost+"/follow", nil)
		if err != nil {
			return fail("Error: unable to prepare request on %s/follow: %v", arsenalHost, err)
		}
		req.Header.Add("X-Arsenal-Followup-Token", followUpToken)

		res, err := httpClient.Do(req)
		if err != nil {
			return fail("Deployment failed: %v. Please check you platform configuration", err)
		}
		defer res.Body.Close()

		body, _ := ioutil.ReadAll(res.Body)
		if res.StatusCode != http.StatusOK {
			fmt.Println("Body: ", string(body))
			return fail("deployment failure")
		}

		//Read the followUp token
		bodyResult := map[string]interface{}{}
		if err := json.Unmarshal(body, &bodyResult); err != nil {
			return fail("Error: Unable to read body: %v", err)
		}

		doneB, doneIsBool := bodyResult["done"].(bool)
		doneS, doneIsString := bodyResult["done"].(string)
		if (doneIsBool && doneB) || (doneIsString && doneS == "true") {
			success = true
			break
		} else {
			fmt.Println("Not done yet")
		}
		retry++
	}

	if !success {
		return fail("deployment failed")
	}

	return &platformplugin.DeployResult{
		Status: sdk.StatusSuccess.String(),
	}, nil
}

func (e *arsenalDeploymentPlugin) DeployStatus(ctx context.Context, q *platformplugin.DeployStatusQuery) (*platformplugin.DeployResult, error) {
	return &platformplugin.DeployResult{
		Status: sdk.StatusSuccess.String(),
	}, nil
}

func main() {
	e := arsenalDeploymentPlugin{}
	if err := platformplugin.Start(context.Background(), &e); err != nil {
		panic(err)
	}
	return

}

func fail(format string, args ...interface{}) (*platformplugin.DeployResult, error) {
	msg := fmt.Sprintf(format, args...)
	fmt.Println(msg)
	return &platformplugin.DeployResult{
		Details: msg,
		Status:  sdk.StatusFail.String(),
	}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/Imgur/incus"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"
)

var store *incus.Storage

// Inserted at compile time by -ldflags "-X main.BUILD foo"
var BUILD string

func main() {
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	store = nil

	defer func() {
		if err := recover(); err != nil {
			log.Printf("Caught panic in the main thread: %s", err)
			shutdown()
		}
	}()

	conf := incus.NewConfig()
	initLogger(conf)
	log.Printf("Incus built on %s", BUILD)

	InstallSignalHandlers()

	store = incus.NewStore(&conf)

	incus.CLIENT_BROAD = conf.GetBool("client_broadcasts")
	server := incus.NewServer(&conf, store)

	go server.RecordStats(1 * time.Second)
	go server.LogConnectedClientsPeriodically(20 * time.Second)
	go server.ListenFromRedis()
	go server.ListenFromSockets()
	go server.ListenFromLongpoll()
	go server.ListenForHTTPPings()
	go server.SendHeartbeatsPeriodically(20 * time.Second)

	go listenAndServeTLS(conf)
	listenAndServe(conf)
}

func listenAndServe(conf incus.Configuration) {
	listenAddr := fmt.Sprintf(":%s", conf.Get("listening_port"))
	err := http.ListenAndServe(listenAddr, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func listenAndServeTLS(conf incus.Configuration) {
	if conf.GetBool("tls_enabled") {
		tlsListenAddr := fmt.Sprintf(":%s", conf.Get("tls_port"))
		err := http.ListenAndServeTLS(tlsListenAddr, conf.Get("cert_file"), conf.Get("key_file"), nil)
		if err != nil {
			log.Println(err)
			log.Fatal(err)
		}
	}
}

func InstallSignalHandlers() {
	go func() {
		signals := make(chan os.Signal, 1)
		signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
		sig := <-signals
		log.Printf("%v caught, incus is going down...", sig)
		log.Printf("Waiting %d seconds for goroutines to shut down...", 5)

		select {
		case <-time.After(5 * time.Second):
			shutdown()
		case sig := <-signals:
			log.Printf("%v caught again. Exiting immediately...", sig)
			shutdown()
		}
	}()
}

func initLogger(conf incus.Configuration) {
	incus.DEBUG = false
	if conf.Get("log_level") == "debug" {
		incus.DEBUG = true
	}
}

func shutdown() {
	log.Println("Terminated")
	os.Exit(0)
}
<commit_msg>style: Graceful shutdown timeout<commit_after>package main

import (
	"fmt"
	"github.com/Imgur/incus"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"
)

const gracefulShutdownTimeout = 5

var store *incus.Storage

// Inserted at compile time by -ldflags "-X main.BUILD foo"
var BUILD string

func main() {
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	store = nil

	defer func() {
		if err := recover(); err != nil {
			log.Printf("Caught panic in the main thread: %s", err)
			shutdown()
		}
	}()

	conf := incus.NewConfig()
	initLogger(conf)
	log.Printf("Incus built on %s", BUILD)

	InstallSignalHandlers()

	store = incus.NewStore(&conf)

	incus.CLIENT_BROAD = conf.GetBool("client_broadcasts")
	server := incus.NewServer(&conf, store)

	go server.RecordStats(1 * time.Second)
	go server.LogConnectedClientsPeriodically(20 * time.Second)
	go server.ListenFromRedis()
	go server.ListenFromSockets()
	go server.ListenFromLongpoll()
	go server.ListenForHTTPPings()
	go server.SendHeartbeatsPeriodically(20 * time.Second)

	go listenAndServeTLS(conf)
	listenAndServe(conf)
}

func listenAndServe(conf incus.Configuration) {
	listenAddr := fmt.Sprintf(":%s", conf.Get("listening_port"))
	err := http.ListenAndServe(listenAddr, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func listenAndServeTLS(conf incus.Configuration) {
	if conf.GetBool("tls_enabled") {
		tlsListenAddr := fmt.Sprintf(":%s", conf.Get("tls_port"))
		err := http.ListenAndServeTLS(tlsListenAddr, conf.Get("cert_file"), conf.Get("key_file"), nil)
		if err != nil {
			log.Println(err)
			log.Fatal(err)
		}
	}
}

func InstallSignalHandlers() {
	go func() {
		signals := make(chan os.Signal, 1)
		signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
		sig := <-signals
		log.Printf("%v caught, incus is going down...", sig)
		log.Printf("Waiting %d seconds for goroutines to shut down...", gracefulShutdownTimeout)

		select {
		case <-time.After(gracefulShutdownTimeout * time.Second):
			shutdown()
		case sig := <-signals:
			log.Printf("%v caught again. Exiting immediately...", sig)
			shutdown()
		}
	}()
}

func initLogger(conf incus.Configuration) {
	incus.DEBUG = false
	if conf.Get("log_level") == "debug" {
		incus.DEBUG = true
	}
}

func shutdown() {
	log.Println("Terminated")
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"github.com/ckpt/backend-services/tournaments"
	"github.com/m4rw3r/uuid"
	"github.com/zenazn/goji/web"
	"net/http"
	"sort"
	"strconv"
)

func createNewTournament(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	info := new(tournaments.Info)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(info); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}
	nTournament, err := tournaments.NewTournament(*info)
	if err != nil {
		return &appError{err, "Failed to create new tournament", 500}
	}
	w.Header().Set("Location", "/tournaments/"+nTournament.UUID.String())
	w.WriteHeader(201)
	return nil
}

func listAllTournaments(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tournamentList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant load tournaments", 500}
	}
	encoder := json.NewEncoder(w)
	encoder.Encode(tournamentList)
	return nil
}

func getTournament(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	uuid, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(uuid)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}
	encoder := json.NewEncoder(w)
	encoder.Encode(tournament)
	return nil
}

func updateTournamentInfo(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	uuid, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(uuid)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}
	tempInfo := new(tournaments.Info)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(tempInfo); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if err := tournament.UpdateInfo(*tempInfo); err != nil {
		return &appError{err, "Failed to update tournament info", 500}
	}
	w.WriteHeader(204)
	return nil
}

func setTournamentPlayed(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	uuid, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(uuid)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	tempInfo := make(map[string]bool)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&tempInfo); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if err := tournament.SetPlayed(tempInfo["played"]); err != nil {
		return &appError{err, "Failed to update tournament played status", 500}
	}
	w.WriteHeader(204)
	return nil
}

func setTournamentResult(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	type Result struct {
		Result []uuid.UUID
	}

	resultData := new(Result)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(resultData); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if err := tournament.SetResult(resultData.Result); err != nil {
		return &appError{err, "Failed to update tournament result", 500}
	}
	w.WriteHeader(204)
	return nil
}

func addTournamentNoShow(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	absenteeData := new(tournaments.Absentee)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(absenteeData); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if !c.Env["authIsAdmin"].(bool) || absenteeData.Player.IsZero() {
		absenteeData.Player = c.Env["authPlayer"].(uuid.UUID)
	}

	if err := tournament.AddNoShow(absenteeData.Player, absenteeData.Reason); err != nil {
		return &appError{err, "Failed to set absentee for tournament", 500}
	}
	w.WriteHeader(204)
	return nil
}

func removeTournamentNoShow(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	pID, err := uuid.FromString(c.URLParams["playeruuid"])

	if !c.Env["authIsAdmin"].(bool) || pID != c.Env["authPlayer"].(uuid.UUID) {
		return &appError{errors.New("Unauthorized"), "Must be given player or admin to remove absentee", 403}
	}

	if err := tournament.RemoveNoShow(pID); err != nil {
		return &appError{err, "Failed to remove absentee for tournament", 500}
	}
	w.WriteHeader(204)
	return nil
}

func getTournamentResult(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	encoder := json.NewEncoder(w)
	encoder.Encode(tournament.Result)
	return nil
}

func listAllSeasons(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	allTournaments, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant load tournaments", 404}
	}

	seasonList := allTournaments.Seasons()
	sort.Ints(seasonList)
	encoder := json.NewEncoder(w)
	encoder.Encode(map[string][]int{"seasons": seasonList})
	return nil
}

func listTournamentsBySeason(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, err := strconv.Atoi(c.URLParams["year"])
	tList, err := tournaments.TournamentsBySeason(season)
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	encoder := json.NewEncoder(w)
	encoder.Encode(map[string][]*tournaments.Tournament{"tournaments": tList})
	return nil
}

func getSeasonStandings(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, _ := strconv.Atoi(c.URLParams["year"])
	sortedStandings := tournaments.SeasonStandings(season)

	encoder := json.NewEncoder(w)
	encoder.Encode(sortedStandings)
	return nil
}

func getSeasonStats(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, _ := strconv.Atoi(c.URLParams["year"])

	seasonStats := tournaments.SeasonStats([]int{season})

	encoder := json.NewEncoder(w)
	encoder.Encode(seasonStats)
	return nil
}

func getSeasonTitles(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, _ := strconv.Atoi(c.URLParams["year"])

	seasonTitles := tournaments.Titles([]int{season})

	encoder := json.NewEncoder(w)
	encoder.Encode(seasonTitles)
	return nil
}

func getTotalStandings(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	tList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	seasons := tList.Seasons()

	totalStandings := tournaments.TotalStandings(seasons)

	encoder := json.NewEncoder(w)
	encoder.Encode(totalStandings)
	return nil
}

func getTotalStats(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	seasons := tList.Seasons()

	fullStats := tournaments.SeasonStats(seasons)

	encoder := json.NewEncoder(w)
	encoder.Encode(fullStats)
	return nil
}

func getTotalTitles(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	seasons := tList.Seasons()

	allTitles := tournaments.Titles(seasons)

	encoder := json.NewEncoder(w)
	encoder.Encode(allTitles)
	return nil
}
<commit_msg>Fix bug in logic when removing noshows<commit_after>package main

import (
	"encoding/json"
	"errors"
	"github.com/ckpt/backend-services/tournaments"
	"github.com/m4rw3r/uuid"
	"github.com/zenazn/goji/web"
	"net/http"
	"sort"
	"strconv"
)

func createNewTournament(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	info := new(tournaments.Info)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(info); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}
	nTournament, err := tournaments.NewTournament(*info)
	if err != nil {
		return &appError{err, "Failed to create new tournament", 500}
	}
	w.Header().Set("Location", "/tournaments/"+nTournament.UUID.String())
	w.WriteHeader(201)
	return nil
}

func listAllTournaments(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tournamentList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant load tournaments", 500}
	}
	encoder := json.NewEncoder(w)
	encoder.Encode(tournamentList)
	return nil
}

func getTournament(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	uuid, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(uuid)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}
	encoder := json.NewEncoder(w)
	encoder.Encode(tournament)
	return nil
}

func updateTournamentInfo(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	uuid, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(uuid)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}
	tempInfo := new(tournaments.Info)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(tempInfo); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if err := tournament.UpdateInfo(*tempInfo); err != nil {
		return &appError{err, "Failed to update tournament info", 500}
	}
	w.WriteHeader(204)
	return nil
}

func setTournamentPlayed(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	uuid, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(uuid)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	tempInfo := make(map[string]bool)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&tempInfo); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if err := tournament.SetPlayed(tempInfo["played"]); err != nil {
		return &appError{err, "Failed to update tournament played status", 500}
	}
	w.WriteHeader(204)
	return nil
}

func setTournamentResult(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	type Result struct {
		Result []uuid.UUID
	}

	resultData := new(Result)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(resultData); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if err := tournament.SetResult(resultData.Result); err != nil {
		return &appError{err, "Failed to update tournament result", 500}
	}
	w.WriteHeader(204)
	return nil
}

func addTournamentNoShow(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	absenteeData := new(tournaments.Absentee)
	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(absenteeData); err != nil {
		return &appError{err, "Invalid JSON", 400}
	}

	if !c.Env["authIsAdmin"].(bool) || absenteeData.Player.IsZero() {
		absenteeData.Player = c.Env["authPlayer"].(uuid.UUID)
	}

	if err := tournament.AddNoShow(absenteeData.Player, absenteeData.Reason); err != nil {
		return &appError{err, "Failed to set absentee for tournament", 500}
	}
	w.WriteHeader(204)
	return nil
}

func removeTournamentNoShow(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	pID, err := uuid.FromString(c.URLParams["playeruuid"])

	if !c.Env["authIsAdmin"].(bool) && pID != c.Env["authPlayer"].(uuid.UUID) {
		return &appError{errors.New("Unauthorized"), "Must be given player or admin to remove absentee", 403}
	}

	if err := tournament.RemoveNoShow(pID); err != nil {
		return &appError{err, "Failed to remove absentee for tournament", 500}
	}
	w.WriteHeader(204)
	return nil
}

func getTournamentResult(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tID, err := uuid.FromString(c.URLParams["uuid"])
	tournament, err := tournaments.TournamentByUUID(tID)
	if err != nil {
		return &appError{err, "Cant find tournament", 404}
	}

	encoder := json.NewEncoder(w)
	encoder.Encode(tournament.Result)
	return nil
}

func listAllSeasons(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	allTournaments, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant load tournaments", 404}
	}

	seasonList := allTournaments.Seasons()
	sort.Ints(seasonList)
	encoder := json.NewEncoder(w)
	encoder.Encode(map[string][]int{"seasons": seasonList})
	return nil
}

func listTournamentsBySeason(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, err := strconv.Atoi(c.URLParams["year"])
	tList, err := tournaments.TournamentsBySeason(season)
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	encoder := json.NewEncoder(w)
	encoder.Encode(map[string][]*tournaments.Tournament{"tournaments": tList})
	return nil
}

func getSeasonStandings(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, _ := strconv.Atoi(c.URLParams["year"])
	sortedStandings := tournaments.SeasonStandings(season)

	encoder := json.NewEncoder(w)
	encoder.Encode(sortedStandings)
	return nil
}

func getSeasonStats(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, _ := strconv.Atoi(c.URLParams["year"])

	seasonStats := tournaments.SeasonStats([]int{season})

	encoder := json.NewEncoder(w)
	encoder.Encode(seasonStats)
	return nil
}

func getSeasonTitles(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	season, _ := strconv.Atoi(c.URLParams["year"])

	seasonTitles := tournaments.Titles([]int{season})

	encoder := json.NewEncoder(w)
	encoder.Encode(seasonTitles)
	return nil
}

func getTotalStandings(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	tList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	seasons := tList.Seasons()

	totalStandings := tournaments.TotalStandings(seasons)

	encoder := json.NewEncoder(w)
	encoder.Encode(totalStandings)
	return nil
}

func getTotalStats(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	seasons := tList.Seasons()

	fullStats := tournaments.SeasonStats(seasons)

	encoder := json.NewEncoder(w)
	encoder.Encode(fullStats)
	return nil
}

func getTotalTitles(c web.C, w http.ResponseWriter, r *http.Request) *appError {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	tList, err := tournaments.AllTournaments()
	if err != nil {
		return &appError{err, "Cant find tournaments", 404}
	}

	seasons := tList.Seasons()

	allTitles := tournaments.Titles(seasons)

	encoder := json.NewEncoder(w)
	encoder.Encode(allTitles)
	return nil
}
<|endoftext|>