<commit_before>/*
Copyright 2017 by the 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 main

import (
	"encoding/json"
	"fmt"
	"os"

	"sigs.k8s.io/aws-iam-authenticator/pkg/metrics"
	"sigs.k8s.io/aws-iam-authenticator/pkg/token"

	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var verifyCmd = &cobra.Command{
	Use:   "verify",
	Short: "Verify a token for debugging purpose",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		tok := viper.GetString("token")
		output := viper.GetString("output")
		clusterID := viper.GetString("clusterID")
		partition := viper.GetString("partition")

		if tok == "" {
			fmt.Fprintf(os.Stderr, "error: token not specified\n")
			cmd.Usage()
			os.Exit(1)
		}

		if clusterID == "" {
			fmt.Fprintf(os.Stderr, "error: cluster ID not specified\n")
			cmd.Usage()
			os.Exit(1)
		}

		id, err := token.NewVerifier(clusterID, partition).Verify(tok)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not verify token: %v\n", err)
			os.Exit(1)
		}

		if output == "json" {
			value, err := json.MarshalIndent(id, "", "    ")
			if err != nil {
				fmt.Fprintf(os.Stderr, "could not unmarshal token: %v\n", err)
			}
			fmt.Printf("%s\n", value)
		} else {
			fmt.Printf("%+v\n", id)
		}
	},
}

func init() {
	rootCmd.AddCommand(verifyCmd)
	metrics.InitMetrics(prometheus.DefaultRegisterer)
	verifyCmd.Flags().StringP("token", "t", "", "Token to verify")
	verifyCmd.Flags().StringP("output", "o", "", "Output format. Only `json` is supported currently.")
	viper.BindPFlag("token", verifyCmd.Flags().Lookup("token"))
	viper.BindPFlag("output", verifyCmd.Flags().Lookup("output"))

	partitionKeys := []string{}
	for _, p := range endpoints.DefaultPartitions() {
		partitionKeys = append(partitionKeys, p.ID())
	}

	verifyCmd.Flags().String("partition",
		endpoints.AwsPartitionID,
		fmt.Sprintf("The AWS partition. Must be one of: %v", partitionKeys))
	viper.BindPFlag("partition", verifyCmd.Flags().Lookup("partition"))

}
<commit_msg>Remove duplicate InitMetrics<commit_after>/*
Copyright 2017 by the 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 main

import (
	"encoding/json"
	"fmt"
	"os"

	"sigs.k8s.io/aws-iam-authenticator/pkg/metrics"
	"sigs.k8s.io/aws-iam-authenticator/pkg/token"

	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var verifyCmd = &cobra.Command{
	Use:   "verify",
	Short: "Verify a token for debugging purpose",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		tok := viper.GetString("token")
		output := viper.GetString("output")
		clusterID := viper.GetString("clusterID")
		partition := viper.GetString("partition")

		if tok == "" {
			fmt.Fprintf(os.Stderr, "error: token not specified\n")
			cmd.Usage()
			os.Exit(1)
		}

		if clusterID == "" {
			fmt.Fprintf(os.Stderr, "error: cluster ID not specified\n")
			cmd.Usage()
			os.Exit(1)
		}

		id, err := token.NewVerifier(clusterID, partition).Verify(tok)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not verify token: %v\n", err)
			os.Exit(1)
		}

		if output == "json" {
			value, err := json.MarshalIndent(id, "", "    ")
			if err != nil {
				fmt.Fprintf(os.Stderr, "could not unmarshal token: %v\n", err)
			}
			fmt.Printf("%s\n", value)
		} else {
			fmt.Printf("%+v\n", id)
		}
	},
}

func init() {
	rootCmd.AddCommand(verifyCmd)
	verifyCmd.Flags().StringP("token", "t", "", "Token to verify")
	verifyCmd.Flags().StringP("output", "o", "", "Output format. Only `json` is supported currently.")
	viper.BindPFlag("token", verifyCmd.Flags().Lookup("token"))
	viper.BindPFlag("output", verifyCmd.Flags().Lookup("output"))

	partitionKeys := []string{}
	for _, p := range endpoints.DefaultPartitions() {
		partitionKeys = append(partitionKeys, p.ID())
	}

	verifyCmd.Flags().String("partition",
		endpoints.AwsPartitionID,
		fmt.Sprintf("The AWS partition. Must be one of: %v", partitionKeys))
	viper.BindPFlag("partition", verifyCmd.Flags().Lookup("partition"))

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"flag"
	atx "github.com/craigulmer/airlinetracks"
)

func readFile(fname string, carrier string, region string, verbose bool){

	var trdr *atx.TrackReader
	var err error

	if carrier!="" && region!="" {
		panic("Currently, can only filter on ONE thing")
	}

	if carrier=="" {

		if region=="" {
			trdr, err = atx.Open(fname)
		} else {
			trdr, err = atx.OpenWithFilter(fname, atx.NewFilterByRegion(region))
		}
	} else {
		trdr, err = atx.OpenWithFilter(fname, atx.NewFilterByCarrier(carrier))
	}
	if err!=nil {
		fmt.Println("Error opening "+fname)
		return
	}

	for {
		track,_ := trdr.GetNext()
		if track==nil { break }
		if verbose {
			fmt.Println("Track: "+track.ToString())
		}
	}

	//Stats are maintained in the reader
	fmt.Println("ValidTracks:",trdr.ValidTracks,"TotalTracks:",trdr.TotalTracks, "CarrierFilter: '"+carrier+"'")
	trdr.Close()
}

func main() {

	var file_src = flag.String("file", "2014-10-28.txt.gz", "Input File")
	var carrier = flag.String("carrier", "", "Filter to a specific carrier")
	var region = flag.String("region","", "Filter to flights that crossed a particular region")
	var verbose = flag.Bool("verbose", false, "Verbose")
	flag.Parse()

	readFile(*file_src, *carrier,  *region, *verbose,)

}
<commit_msg>ENH: Minor cleanup of op description<commit_after>package main

import (
	"fmt"
	"flag"
	atx "github.com/craigulmer/airlinetracks"
)

func readFile(fname string, carrier string, region string, verbose bool){

	var trdr *atx.TrackReader
	var err error
	var op_name string
	if carrier!="" && region!="" {
		panic("Currently, can only filter on ONE thing")
	}

	switch {
	case region!="":
		trdr, err = atx.OpenWithFilter(fname, atx.NewFilterByRegion(region));
		op_name="Region: "+region
	case carrier!="":
		trdr, err = atx.OpenWithFilter(fname, atx.NewFilterByCarrier(carrier));
		op_name="Carrier: "+carrier
	default:
		trdr, err = atx.Open(fname)
	}
	if err!=nil {
		fmt.Println("Error opening "+fname)
		return
	}

	//Walk through each track until nothing left
	for {
		track,_ := trdr.GetNext()
		if track==nil { break }
		if verbose {
			fmt.Println("Track: "+track.ToString())
		}
	}

	//Stats are maintained in the reader
	fmt.Println("ValidTracks:",trdr.ValidTracks,"TotalTracks:",trdr.TotalTracks, op_name)
	trdr.Close()
}

func main() {

	var file_src = flag.String("file", "2014-10-28.txt.gz", "Input File")
	var carrier = flag.String("carrier", "", "Filter to a specific carrier")
	var region = flag.String("region","", "Filter to flights that crossed a particular region")
	var verbose = flag.Bool("verbose", false, "Verbose")
	flag.Parse()

	readFile(*file_src, *carrier,  *region, *verbose,)

}
<|endoftext|>


<commit_before>package desec

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"

	"github.com/StackExchange/dnscontrol/v3/models"
	"github.com/StackExchange/dnscontrol/v3/pkg/diff"
	"github.com/StackExchange/dnscontrol/v3/pkg/printer"
	"github.com/StackExchange/dnscontrol/v3/providers"
	"github.com/miekg/dns/dnsutil"
)

/*
desec API DNS provider:
Info required in `creds.json`:
   - auth-token
*/

// NewDeSec creates the provider.
func NewDeSec(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
	c := &api{}
	c.creds.token = m["auth-token"]
	if c.creds.token == "" {
		return nil, fmt.Errorf("missing deSEC auth-token")
	}

	// Get a domain to validate authentication
	if err := c.fetchDomainList(); err != nil {
		return nil, err
	}

	return c, nil
}

var features = providers.DocumentationNotes{
	providers.DocDualHost:            providers.Unimplemented(),
	providers.DocOfficiallySupported: providers.Cannot(),
	providers.DocCreateDomains:       providers.Can(),
	providers.CanUseAlias:            providers.Cannot(),
	providers.CanUseSRV:              providers.Can(),
	providers.CanUseDS:               providers.Can(),
	providers.CanUseSSHFP:            providers.Can(),
	providers.CanUseCAA:              providers.Can(),
	providers.CanUseTLSA:             providers.Can(),
	providers.CanUsePTR:              providers.Unimplemented(),
	providers.CanGetZones:            providers.Can(),
	providers.CanAutoDNSSEC:          providers.Cannot(),
}

var defaultNameServerNames = []string{
	"ns1.desec.io",
	"ns2.desec.org",
}

func init() {
	providers.RegisterDomainServiceProviderType("DESEC", NewDeSec, features)
}

// GetNameservers returns the nameservers for a domain.
func (c *api) GetNameservers(domain string) ([]*models.Nameserver, error) {
	return models.ToNameservers(defaultNameServerNames)
}

func (c *api) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	existing, err := c.GetZoneRecords(dc.Name)
	if err != nil {
		return nil, err
	}
	models.PostProcessRecords(existing)
	clean := PrepFoundRecords(existing)
	var minTTL uint32
	if ttl, ok := c.domainIndex[dc.Name]; !ok {
		minTTL = 3600
	} else {
		minTTL = ttl
	}
	PrepDesiredRecords(dc, minTTL)
	return c.GenerateDomainCorrections(dc, clean)
}

// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *api) GetZoneRecords(domain string) (models.Records, error) {
	records, err := c.getRecords(domain)
	if err != nil {
		return nil, err
	}

	// Convert them to DNScontrol's native format:
	existingRecords := []*models.RecordConfig{}
	for _, rr := range records {
		existingRecords = append(existingRecords, nativeToRecords(rr, domain)...)
	}
	return existingRecords, nil
}

// EnsureDomainExists returns an error if domain doesn't exist.
func (c *api) EnsureDomainExists(domain string) error {
	if err := c.fetchDomainList(); err != nil {
		return err
	}
	// domain already exists
	if _, ok := c.domainIndex[domain]; ok {
		return nil
	}
	return c.createDomain(domain)
}

// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
	// If there are records that need to be modified, removed, etc. we
	// do it here.  Usually this is a no-op.
	return recs
}

// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig, minTTL uint32) {
	// Sort through the dc.Records, eliminate any that can't be
	// supported; modify any that need adjustments to work with the
	// provider.  We try to do minimal changes otherwise it gets
	// confusing.

	dc.Punycode()
	recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
	for _, rec := range dc.Records {
		if rec.Type == "ALIAS" {
			// deSEC does not permit ALIAS records, just ignore it
			printer.Warnf("deSEC does not support alias records\n")
			continue
		}
		if rec.TTL < minTTL {
			if rec.Type != "NS" {
				printer.Warnf("Please contact support@desec.io if you need ttls < %d. Setting ttl of %s type %s from %d to %d\n", minTTL, rec.GetLabelFQDN(), rec.Type, rec.TTL, minTTL)
			}
			rec.TTL = minTTL
		}
		recordsToKeep = append(recordsToKeep, rec)
	}
	dc.Records = recordsToKeep
}

// GenerateDomainCorrections takes the desired and existing records
// and produces a Correction list.  The correction list is simply
// a list of functions to call to actually make the desired
// correction, and a message to output to the user when the change is
// made.
func (c *api) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {

	var corrections = []*models.Correction{}

	// diff existing vs. current.
	differ := diff.New(dc)
	keysToUpdate := differ.ChangedGroups(existing)
	if len(keysToUpdate) == 0 {
		return nil, nil
	}

	desiredRecords := dc.Records.GroupedByKey()
	var rrs []resourceRecord
	buf := &bytes.Buffer{}
	// For any key with an update, delete or replace those records.
	for label := range keysToUpdate {
		if _, ok := desiredRecords[label]; !ok {
			//we could not find this RecordKey in the desiredRecords
			//this means it must be deleted
			for i, msg := range keysToUpdate[label] {
				if i == 0 {
					rc := resourceRecord{}
					rc.Type = label.Type
					rc.Records = make([]string, 0) // empty array of records should delete this rrset
					rc.TTL = 3600
					shortname := dnsutil.TrimDomainName(label.NameFQDN, dc.Name)
					if shortname == "@" {
						shortname = ""
					}
					rc.Subname = shortname
					fmt.Fprintln(buf, msg)
					rrs = append(rrs, rc)
				} else {
					//just add the message
					fmt.Fprintln(buf, msg)
				}
			}
		} else {
			//it must be an update or create, both can be done with the same api call.
			ns := recordsToNative(desiredRecords[label], dc.Name)
			if len(ns) > 1 {
				panic("we got more than one resource record to create / modify")
			}
			for i, msg := range keysToUpdate[label] {
				if i == 0 {
					rrs = append(rrs, ns[0])
					fmt.Fprintln(buf, msg)
				} else {
					//noop just for printing the additional messages
					fmt.Fprintln(buf, msg)
				}
			}
		}
	}
	var msg string
	msg = fmt.Sprintf("Changes:\n%s", buf)
	corrections = append(corrections,
		&models.Correction{
			Msg: msg,
			F: func() error {
				rc := rrs
				err := c.upsertRR(rc, dc.Name)
				if err != nil {
					return err
				}
				return nil
			},
		})

	// NB(tlim): This sort is just to make updates look pretty. It is
	// cosmetic.  The risk here is that there may be some updates that
	// require a specific order (for example a delete before an add).
	// However the code doesn't seem to have such situation.  All tests
	// pass.  That said, if this breaks anything, the easiest fix might
	// be to just remove the sort.
	sort.Slice(corrections, func(i, j int) bool { return diff.CorrectionLess(corrections, i, j) })

	return corrections, nil
}
<commit_msg>desec: Supports PTR records out of the box (#801)<commit_after>package desec

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"

	"github.com/StackExchange/dnscontrol/v3/models"
	"github.com/StackExchange/dnscontrol/v3/pkg/diff"
	"github.com/StackExchange/dnscontrol/v3/pkg/printer"
	"github.com/StackExchange/dnscontrol/v3/providers"
	"github.com/miekg/dns/dnsutil"
)

/*
desec API DNS provider:
Info required in `creds.json`:
   - auth-token
*/

// NewDeSec creates the provider.
func NewDeSec(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
	c := &api{}
	c.creds.token = m["auth-token"]
	if c.creds.token == "" {
		return nil, fmt.Errorf("missing deSEC auth-token")
	}

	// Get a domain to validate authentication
	if err := c.fetchDomainList(); err != nil {
		return nil, err
	}

	return c, nil
}

var features = providers.DocumentationNotes{
	providers.DocDualHost:            providers.Unimplemented(),
	providers.DocOfficiallySupported: providers.Cannot(),
	providers.DocCreateDomains:       providers.Can(),
	providers.CanUseAlias:            providers.Cannot(),
	providers.CanUseSRV:              providers.Can(),
	providers.CanUseDS:               providers.Can(),
	providers.CanUseSSHFP:            providers.Can(),
	providers.CanUseCAA:              providers.Can(),
	providers.CanUseTLSA:             providers.Can(),
	providers.CanUsePTR:              providers.Can(),
	providers.CanGetZones:            providers.Can(),
	providers.CanAutoDNSSEC:          providers.Cannot(),
}

var defaultNameServerNames = []string{
	"ns1.desec.io",
	"ns2.desec.org",
}

func init() {
	providers.RegisterDomainServiceProviderType("DESEC", NewDeSec, features)
}

// GetNameservers returns the nameservers for a domain.
func (c *api) GetNameservers(domain string) ([]*models.Nameserver, error) {
	return models.ToNameservers(defaultNameServerNames)
}

func (c *api) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	existing, err := c.GetZoneRecords(dc.Name)
	if err != nil {
		return nil, err
	}
	models.PostProcessRecords(existing)
	clean := PrepFoundRecords(existing)
	var minTTL uint32
	if ttl, ok := c.domainIndex[dc.Name]; !ok {
		minTTL = 3600
	} else {
		minTTL = ttl
	}
	PrepDesiredRecords(dc, minTTL)
	return c.GenerateDomainCorrections(dc, clean)
}

// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (c *api) GetZoneRecords(domain string) (models.Records, error) {
	records, err := c.getRecords(domain)
	if err != nil {
		return nil, err
	}

	// Convert them to DNScontrol's native format:
	existingRecords := []*models.RecordConfig{}
	for _, rr := range records {
		existingRecords = append(existingRecords, nativeToRecords(rr, domain)...)
	}
	return existingRecords, nil
}

// EnsureDomainExists returns an error if domain doesn't exist.
func (c *api) EnsureDomainExists(domain string) error {
	if err := c.fetchDomainList(); err != nil {
		return err
	}
	// domain already exists
	if _, ok := c.domainIndex[domain]; ok {
		return nil
	}
	return c.createDomain(domain)
}

// PrepFoundRecords munges any records to make them compatible with
// this provider. Usually this is a no-op.
func PrepFoundRecords(recs models.Records) models.Records {
	// If there are records that need to be modified, removed, etc. we
	// do it here.  Usually this is a no-op.
	return recs
}

// PrepDesiredRecords munges any records to best suit this provider.
func PrepDesiredRecords(dc *models.DomainConfig, minTTL uint32) {
	// Sort through the dc.Records, eliminate any that can't be
	// supported; modify any that need adjustments to work with the
	// provider.  We try to do minimal changes otherwise it gets
	// confusing.

	dc.Punycode()
	recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
	for _, rec := range dc.Records {
		if rec.Type == "ALIAS" {
			// deSEC does not permit ALIAS records, just ignore it
			printer.Warnf("deSEC does not support alias records\n")
			continue
		}
		if rec.TTL < minTTL {
			if rec.Type != "NS" {
				printer.Warnf("Please contact support@desec.io if you need ttls < %d. Setting ttl of %s type %s from %d to %d\n", minTTL, rec.GetLabelFQDN(), rec.Type, rec.TTL, minTTL)
			}
			rec.TTL = minTTL
		}
		recordsToKeep = append(recordsToKeep, rec)
	}
	dc.Records = recordsToKeep
}

// GenerateDomainCorrections takes the desired and existing records
// and produces a Correction list.  The correction list is simply
// a list of functions to call to actually make the desired
// correction, and a message to output to the user when the change is
// made.
func (c *api) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {

	var corrections = []*models.Correction{}

	// diff existing vs. current.
	differ := diff.New(dc)
	keysToUpdate := differ.ChangedGroups(existing)
	if len(keysToUpdate) == 0 {
		return nil, nil
	}

	desiredRecords := dc.Records.GroupedByKey()
	var rrs []resourceRecord
	buf := &bytes.Buffer{}
	// For any key with an update, delete or replace those records.
	for label := range keysToUpdate {
		if _, ok := desiredRecords[label]; !ok {
			//we could not find this RecordKey in the desiredRecords
			//this means it must be deleted
			for i, msg := range keysToUpdate[label] {
				if i == 0 {
					rc := resourceRecord{}
					rc.Type = label.Type
					rc.Records = make([]string, 0) // empty array of records should delete this rrset
					rc.TTL = 3600
					shortname := dnsutil.TrimDomainName(label.NameFQDN, dc.Name)
					if shortname == "@" {
						shortname = ""
					}
					rc.Subname = shortname
					fmt.Fprintln(buf, msg)
					rrs = append(rrs, rc)
				} else {
					//just add the message
					fmt.Fprintln(buf, msg)
				}
			}
		} else {
			//it must be an update or create, both can be done with the same api call.
			ns := recordsToNative(desiredRecords[label], dc.Name)
			if len(ns) > 1 {
				panic("we got more than one resource record to create / modify")
			}
			for i, msg := range keysToUpdate[label] {
				if i == 0 {
					rrs = append(rrs, ns[0])
					fmt.Fprintln(buf, msg)
				} else {
					//noop just for printing the additional messages
					fmt.Fprintln(buf, msg)
				}
			}
		}
	}
	var msg string
	msg = fmt.Sprintf("Changes:\n%s", buf)
	corrections = append(corrections,
		&models.Correction{
			Msg: msg,
			F: func() error {
				rc := rrs
				err := c.upsertRR(rc, dc.Name)
				if err != nil {
					return err
				}
				return nil
			},
		})

	// NB(tlim): This sort is just to make updates look pretty. It is
	// cosmetic.  The risk here is that there may be some updates that
	// require a specific order (for example a delete before an add).
	// However the code doesn't seem to have such situation.  All tests
	// pass.  That said, if this breaks anything, the easiest fix might
	// be to just remove the sort.
	sort.Slice(corrections, func(i, j int) bool { return diff.CorrectionLess(corrections, i, j) })

	return corrections, nil
}
<|endoftext|>


<commit_before>// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package shell

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

const DefaultRemotePath = "/tmp/script.sh"

type config struct {
	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The shebang value used when running inline scripts.
	InlineShebang string `mapstructure:"inline_shebang"`

	// The local path of the shell script to upload and execute.
	Script string

	// An array of multiple scripts to run.
	Scripts []string

	// An array of environment variables that will be injected before
	// your command(s) are executed.
	Vars []string `mapstructure:"environment_vars"`

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	RemotePath string `mapstructure:"remote_path"`

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes, {{ .Vars }}
	// can be used to inject the environment_vars into the environment.
	ExecuteCommand string `mapstructure:"execute_command"`

	// Packer configurations, these come from Packer itself
	PackerBuildName   string `mapstructure:"packer_build_name"`
	PackerBuilderType string `mapstructure:"packer_builder_type"`

	tpl *common.Template
}

type Provisioner struct {
	config config
}

type ExecuteCommandTemplate struct {
	Vars string
	Path string
}

func (p *Provisioner) Prepare(raws ...interface{}) error {
	md, err := common.DecodeConfig(&p.config, raws...)
	if err != nil {
		return err
	}

	p.config.tpl, err = common.NewTemplate()
	if err != nil {
		return err
	}

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)

	if p.config.ExecuteCommand == "" {
		p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
	}

	if p.config.Inline != nil && len(p.config.Inline) == 0 {
		p.config.Inline = nil
	}

	if p.config.InlineShebang == "" {
		p.config.InlineShebang = "/bin/sh"
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = DefaultRemotePath
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	if p.config.Vars == nil {
		p.config.Vars = make([]string, 0)
	}

	if p.config.Script != "" && len(p.config.Scripts) > 0 {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only one of script or scripts can be specified."))
	}

	if p.config.Script != "" {
		p.config.Scripts = []string{p.config.Script}
	}

	templates := map[string]*string{
		"inline_shebang": &p.config.InlineShebang,
		"script":         &p.config.Script,
		"remote_path":    &p.config.RemotePath,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = p.config.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	sliceTemplates := map[string][]string{
		"inline":           p.config.Inline,
		"scripts":          p.config.Scripts,
		"environment_vars": p.config.Vars,
	}

	for n, slice := range sliceTemplates {
		for i, elem := range slice {
			var err error
			slice[i], err = p.config.tpl.Process(elem, nil)
			if err != nil {
				errs = packer.MultiErrorAppend(
					errs, fmt.Errorf("Error processing %s[%d]: %s", n, i, err))
			}
		}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Bad script '%s': %s", path, err))
		}
	}

	// Do a check for bad environment variables, such as '=foo', 'foobar'
	for _, kv := range p.config.Vars {
		vs := strings.Split(kv, "=")
		if len(vs) != 2 || vs[0] == "" {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Environment variable not in format 'key=value': %s", kv))
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
	scripts := make([]string, len(p.config.Scripts))
	copy(scripts, p.config.Scripts)

	// If we have an inline script, then turn that into a temporary
	// shell script and use that.
	if p.config.Inline != nil {
		tf, err := ioutil.TempFile("", "packer-shell")
		if err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}
		defer os.Remove(tf.Name())

		// Set the path to the temporary file
		scripts = append(scripts, tf.Name())

		// Write our contents to it
		writer := bufio.NewWriter(tf)
		writer.WriteString(fmt.Sprintf("#!%s\n", p.config.InlineShebang))
		for _, command := range p.config.Inline {
			if _, err := writer.WriteString(command + "\n"); err != nil {
				return fmt.Errorf("Error preparing shell script: %s", err)
			}
		}

		if err := writer.Flush(); err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}

		tf.Close()
	}

	// Build our variables up by adding in the build name and builder type
	envVars := make([]string, len(p.config.Vars)+2)
	envVars[0] = "PACKER_BUILD_NAME=" + p.config.PackerBuildName
	envVars[1] = "PACKER_BUILDER_TYPE=" + p.config.PackerBuilderType
	copy(envVars[2:], p.config.Vars)

	for _, path := range scripts {
		ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))

		log.Printf("Opening %s for reading", path)
		f, err := os.Open(path)
		if err != nil {
			return fmt.Errorf("Error opening shell script: %s", err)
		}
		defer f.Close()

		log.Printf("Uploading %s => %s", path, p.config.RemotePath)
		err = comm.Upload(p.config.RemotePath, f)
		if err != nil {
			return fmt.Errorf("Error uploading shell script: %s", err)
		}

		// Close the original file since we copied it
		f.Close()

		// Flatten the environment variables
		flattendVars := strings.Join(envVars, " ")

		// Compile the command
		command := p.config.tpl.Process(p.config.ExecuteCommand, &ExecuteCommandTemplate{
			Vars: flattendVars,
			Path: p.config.RemotePath,
		})

		cmd := &packer.RemoteCmd{Command: command}
		log.Printf("Executing command: %s", cmd.Command)
		if err := cmd.StartWithUi(comm, ui); err != nil {
			return fmt.Errorf("Failed executing command: %s", err)
		}

		if cmd.ExitStatus != 0 {
			return fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus)
		}
	}

	return nil
}
<commit_msg>provisioner/shell: tests passing and compiling<commit_after>// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package shell

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

const DefaultRemotePath = "/tmp/script.sh"

type config struct {
	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The shebang value used when running inline scripts.
	InlineShebang string `mapstructure:"inline_shebang"`

	// The local path of the shell script to upload and execute.
	Script string

	// An array of multiple scripts to run.
	Scripts []string

	// An array of environment variables that will be injected before
	// your command(s) are executed.
	Vars []string `mapstructure:"environment_vars"`

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	RemotePath string `mapstructure:"remote_path"`

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes, {{ .Vars }}
	// can be used to inject the environment_vars into the environment.
	ExecuteCommand string `mapstructure:"execute_command"`

	// Packer configurations, these come from Packer itself
	PackerBuildName   string `mapstructure:"packer_build_name"`
	PackerBuilderType string `mapstructure:"packer_builder_type"`

	tpl *common.Template
}

type Provisioner struct {
	config config
}

type ExecuteCommandTemplate struct {
	Vars string
	Path string
}

func (p *Provisioner) Prepare(raws ...interface{}) error {
	md, err := common.DecodeConfig(&p.config, raws...)
	if err != nil {
		return err
	}

	p.config.tpl, err = common.NewTemplate()
	if err != nil {
		return err
	}

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)

	if p.config.ExecuteCommand == "" {
		p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
	}

	if p.config.Inline != nil && len(p.config.Inline) == 0 {
		p.config.Inline = nil
	}

	if p.config.InlineShebang == "" {
		p.config.InlineShebang = "/bin/sh"
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = DefaultRemotePath
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	if p.config.Vars == nil {
		p.config.Vars = make([]string, 0)
	}

	if p.config.Script != "" && len(p.config.Scripts) > 0 {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only one of script or scripts can be specified."))
	}

	if p.config.Script != "" {
		p.config.Scripts = []string{p.config.Script}
	}

	templates := map[string]*string{
		"inline_shebang": &p.config.InlineShebang,
		"script":         &p.config.Script,
		"remote_path":    &p.config.RemotePath,
	}

	for n, ptr := range templates {
		var err error
		*ptr, err = p.config.tpl.Process(*ptr, nil)
		if err != nil {
			errs = packer.MultiErrorAppend(
				errs, fmt.Errorf("Error processing %s: %s", n, err))
		}
	}

	sliceTemplates := map[string][]string{
		"inline":           p.config.Inline,
		"scripts":          p.config.Scripts,
		"environment_vars": p.config.Vars,
	}

	for n, slice := range sliceTemplates {
		for i, elem := range slice {
			var err error
			slice[i], err = p.config.tpl.Process(elem, nil)
			if err != nil {
				errs = packer.MultiErrorAppend(
					errs, fmt.Errorf("Error processing %s[%d]: %s", n, i, err))
			}
		}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = packer.MultiErrorAppend(errs,
			errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Bad script '%s': %s", path, err))
		}
	}

	// Do a check for bad environment variables, such as '=foo', 'foobar'
	for _, kv := range p.config.Vars {
		vs := strings.Split(kv, "=")
		if len(vs) != 2 || vs[0] == "" {
			errs = packer.MultiErrorAppend(errs,
				fmt.Errorf("Environment variable not in format 'key=value': %s", kv))
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
	scripts := make([]string, len(p.config.Scripts))
	copy(scripts, p.config.Scripts)

	// If we have an inline script, then turn that into a temporary
	// shell script and use that.
	if p.config.Inline != nil {
		tf, err := ioutil.TempFile("", "packer-shell")
		if err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}
		defer os.Remove(tf.Name())

		// Set the path to the temporary file
		scripts = append(scripts, tf.Name())

		// Write our contents to it
		writer := bufio.NewWriter(tf)
		writer.WriteString(fmt.Sprintf("#!%s\n", p.config.InlineShebang))
		for _, command := range p.config.Inline {
			if _, err := writer.WriteString(command + "\n"); err != nil {
				return fmt.Errorf("Error preparing shell script: %s", err)
			}
		}

		if err := writer.Flush(); err != nil {
			return fmt.Errorf("Error preparing shell script: %s", err)
		}

		tf.Close()
	}

	// Build our variables up by adding in the build name and builder type
	envVars := make([]string, len(p.config.Vars)+2)
	envVars[0] = "PACKER_BUILD_NAME=" + p.config.PackerBuildName
	envVars[1] = "PACKER_BUILDER_TYPE=" + p.config.PackerBuilderType
	copy(envVars[2:], p.config.Vars)

	for _, path := range scripts {
		ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))

		log.Printf("Opening %s for reading", path)
		f, err := os.Open(path)
		if err != nil {
			return fmt.Errorf("Error opening shell script: %s", err)
		}
		defer f.Close()

		log.Printf("Uploading %s => %s", path, p.config.RemotePath)
		err = comm.Upload(p.config.RemotePath, f)
		if err != nil {
			return fmt.Errorf("Error uploading shell script: %s", err)
		}

		// Close the original file since we copied it
		f.Close()

		// Flatten the environment variables
		flattendVars := strings.Join(envVars, " ")

		// Compile the command
		command, err := p.config.tpl.Process(p.config.ExecuteCommand, &ExecuteCommandTemplate{
			Vars: flattendVars,
			Path: p.config.RemotePath,
		})
		if err != nil {
			return fmt.Errorf("Error processing command: %s", err)
		}

		cmd := &packer.RemoteCmd{Command: command}
		log.Printf("Executing command: %s", cmd.Command)
		if err := cmd.StartWithUi(comm, ui); err != nil {
			return fmt.Errorf("Failed executing command: %s", err)
		}

		if cmd.ExitStatus != 0 {
			return fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/ec2"
)

func resource_aws_internet_gateway_create(
	s *terraform.InstanceState,
	d *terraform.InstanceDiff,
	meta interface{}) (*terraform.InstanceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Create the gateway
	log.Printf("[DEBUG] Creating internet gateway")
	resp, err := ec2conn.CreateInternetGateway(nil)
	if err != nil {
		return nil, fmt.Errorf("Error creating subnet: %s", err)
	}

	// Get the ID and store it
	ig := &resp.InternetGateway
	s.ID = ig.InternetGatewayId
	log.Printf("[INFO] InternetGateway ID: %s", s.ID)

	// Update our attributes and return
	return resource_aws_internet_gateway_update(s, d, meta)
}

func resource_aws_internet_gateway_update(
	s *terraform.InstanceState,
	d *terraform.InstanceDiff,
	meta interface{}) (*terraform.InstanceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Merge the diff so we have the latest attributes
	rs := s.MergeDiff(d)

	// A note on the states below: the AWS docs (as of July, 2014) say
	// that the states would be: attached, attaching, detached, detaching,
	// but when running, I noticed that the state is usually "available" when
	// it is attached.

	// If we're already attached, detach it first
	if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil {
		return s, err
	}

	// Set the VPC ID to empty since we're detached at this point
	delete(rs.Attributes, "vpc_id")

	if attr, ok := d.Attributes["vpc_id"]; ok && attr.New != "" {
		err := resource_aws_internet_gateway_attach(ec2conn, s, attr.New)
		if err != nil {
			return rs, err
		}

		rs.Attributes["vpc_id"] = attr.New
	}

	return resource_aws_internet_gateway_update_state(rs, nil)
}

func resource_aws_internet_gateway_destroy(
	s *terraform.InstanceState,
	meta interface{}) error {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Detach if it is attached
	if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil {
		return err
	}

	log.Printf("[INFO] Deleting Internet Gateway: %s", s.ID)
	if _, err := ec2conn.DeleteInternetGateway(s.ID); err != nil {
		ec2err, ok := err.(*ec2.Error)
		if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
			return nil
		}

		return fmt.Errorf("Error deleting internet gateway: %s", err)
	}

	// Wait for the internet gateway to actually delete
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to delete", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"available"},
		Target:  "",
		Refresh: IGStateRefreshFunc(ec2conn, s.ID),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to destroy: %s",
			s.ID, err)
	}

	return nil
}

func resource_aws_internet_gateway_refresh(
	s *terraform.InstanceState,
	meta interface{}) (*terraform.InstanceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	igRaw, _, err := IGStateRefreshFunc(ec2conn, s.ID)()
	if err != nil {
		return s, err
	}
	if igRaw == nil {
		return nil, nil
	}

	ig := igRaw.(*ec2.InternetGateway)
	return resource_aws_internet_gateway_update_state(s, ig)
}

func resource_aws_internet_gateway_diff(
	s *terraform.InstanceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.InstanceDiff, error) {
	b := &diff.ResourceBuilder{
		Attrs: map[string]diff.AttrType{
			"vpc_id": diff.AttrTypeUpdate,
		},
	}

	return b.Diff(s, c)
}

func resource_aws_internet_gateway_attach(
	ec2conn *ec2.EC2,
	s *terraform.InstanceState,
	vpcId string) error {
	log.Printf(
		"[INFO] Attaching Internet Gateway '%s' to VPC '%s'",
		s.ID,
		vpcId)
	_, err := ec2conn.AttachInternetGateway(s.ID, vpcId)
	if err != nil {
		return err
	}

	// Wait for it to be fully attached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"detached", "attaching"},
		Target:  "available",
		Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "available"),
		Timeout: 1 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to attach: %s",
			s.ID, err)
	}

	return nil
}

func resource_aws_internet_gateway_detach(
	ec2conn *ec2.EC2,
	s *terraform.InstanceState) error {
	if s.Attributes["vpc_id"] == "" {
		return nil
	}

	log.Printf(
		"[INFO] Detaching Internet Gateway '%s' from VPC '%s'",
		s.ID,
		s.Attributes["vpc_id"])
	wait := true
	_, err := ec2conn.DetachInternetGateway(s.ID, s.Attributes["vpc_id"])
	if err != nil {
		ec2err, ok := err.(*ec2.Error)
		if ok {
			if ec2err.Code == "InvalidInternetGatewayID.NotFound" {
				err = nil
				wait = false
			} else if ec2err.Code == "Gateway.NotAttached" {
				err = nil
				wait = false
			}
		}

		if err != nil {
			return err
		}
	}

	delete(s.Attributes, "vpc_id")

	if !wait {
		return nil
	}

	// Wait for it to be fully detached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"attached", "detaching", "available"},
		Target:  "detached",
		Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "detached"),
		Timeout: 1 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to detach: %s",
			s.ID, err)
	}

	return nil
}

func resource_aws_internet_gateway_update_state(
	s *terraform.InstanceState,
	ig *ec2.InternetGateway) (*terraform.InstanceState, error) {
	return s, nil
}

// IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an internet gateway.
func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter())
		if err != nil {
			ec2err, ok := err.(*ec2.Error)
			if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := &resp.InternetGateways[0]
		return ig, "available", nil
	}
}

// IGAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used
// watch the state of an internet gateway's attachment.
func IGAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc {
	var start time.Time
	return func() (interface{}, string, error) {
		if start.IsZero() {
			start = time.Now()
		}

		resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter())
		if err != nil {
			ec2err, ok := err.(*ec2.Error)
			if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := &resp.InternetGateways[0]

		if time.Now().Sub(start) > 10*time.Second {
			return ig, expected, nil
		}

		if len(ig.Attachments) == 0 {
			// No attachments, we're detached
			return ig, "detached", nil
		}

		return ig, ig.Attachments[0].State, nil
	}
}
<commit_msg>providers/aws: retry destroying internet gateway for some time [GH-447]<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/diff"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/goamz/ec2"
)

func resource_aws_internet_gateway_create(
	s *terraform.InstanceState,
	d *terraform.InstanceDiff,
	meta interface{}) (*terraform.InstanceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Create the gateway
	log.Printf("[DEBUG] Creating internet gateway")
	resp, err := ec2conn.CreateInternetGateway(nil)
	if err != nil {
		return nil, fmt.Errorf("Error creating subnet: %s", err)
	}

	// Get the ID and store it
	ig := &resp.InternetGateway
	s.ID = ig.InternetGatewayId
	log.Printf("[INFO] InternetGateway ID: %s", s.ID)

	// Update our attributes and return
	return resource_aws_internet_gateway_update(s, d, meta)
}

func resource_aws_internet_gateway_update(
	s *terraform.InstanceState,
	d *terraform.InstanceDiff,
	meta interface{}) (*terraform.InstanceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Merge the diff so we have the latest attributes
	rs := s.MergeDiff(d)

	// A note on the states below: the AWS docs (as of July, 2014) say
	// that the states would be: attached, attaching, detached, detaching,
	// but when running, I noticed that the state is usually "available" when
	// it is attached.

	// If we're already attached, detach it first
	if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil {
		return s, err
	}

	// Set the VPC ID to empty since we're detached at this point
	delete(rs.Attributes, "vpc_id")

	if attr, ok := d.Attributes["vpc_id"]; ok && attr.New != "" {
		err := resource_aws_internet_gateway_attach(ec2conn, s, attr.New)
		if err != nil {
			return rs, err
		}

		rs.Attributes["vpc_id"] = attr.New
	}

	return resource_aws_internet_gateway_update_state(rs, nil)
}

func resource_aws_internet_gateway_destroy(
	s *terraform.InstanceState,
	meta interface{}) error {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	// Detach if it is attached
	if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil {
		return err
	}

	log.Printf("[INFO] Deleting Internet Gateway: %s", s.ID)
	return resource.Retry(5*time.Minute, func() error {
		_, err := ec2conn.DeleteInternetGateway(s.ID)
		if err != nil {
			ec2err, ok := err.(*ec2.Error)
			if !ok {
				return err
			}

			switch ec2err.Code {
			case "InvalidInternetGatewayID.NotFound":
				return nil
			case "DependencyViolation":
				return err // retry
			default:
				return resource.RetryError{err}
			}
		}

		return fmt.Errorf("Error deleting internet gateway: %s", err)
	})

	// Wait for the internet gateway to actually delete
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to delete", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"available"},
		Target:  "",
		Refresh: IGStateRefreshFunc(ec2conn, s.ID),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to destroy: %s",
			s.ID, err)
	}

	return nil
}

func resource_aws_internet_gateway_refresh(
	s *terraform.InstanceState,
	meta interface{}) (*terraform.InstanceState, error) {
	p := meta.(*ResourceProvider)
	ec2conn := p.ec2conn

	igRaw, _, err := IGStateRefreshFunc(ec2conn, s.ID)()
	if err != nil {
		return s, err
	}
	if igRaw == nil {
		return nil, nil
	}

	ig := igRaw.(*ec2.InternetGateway)
	return resource_aws_internet_gateway_update_state(s, ig)
}

func resource_aws_internet_gateway_diff(
	s *terraform.InstanceState,
	c *terraform.ResourceConfig,
	meta interface{}) (*terraform.InstanceDiff, error) {
	b := &diff.ResourceBuilder{
		Attrs: map[string]diff.AttrType{
			"vpc_id": diff.AttrTypeUpdate,
		},
	}

	return b.Diff(s, c)
}

func resource_aws_internet_gateway_attach(
	ec2conn *ec2.EC2,
	s *terraform.InstanceState,
	vpcId string) error {
	log.Printf(
		"[INFO] Attaching Internet Gateway '%s' to VPC '%s'",
		s.ID,
		vpcId)
	_, err := ec2conn.AttachInternetGateway(s.ID, vpcId)
	if err != nil {
		return err
	}

	// Wait for it to be fully attached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"detached", "attaching"},
		Target:  "available",
		Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "available"),
		Timeout: 1 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to attach: %s",
			s.ID, err)
	}

	return nil
}

func resource_aws_internet_gateway_detach(
	ec2conn *ec2.EC2,
	s *terraform.InstanceState) error {
	if s.Attributes["vpc_id"] == "" {
		return nil
	}

	log.Printf(
		"[INFO] Detaching Internet Gateway '%s' from VPC '%s'",
		s.ID,
		s.Attributes["vpc_id"])
	wait := true
	_, err := ec2conn.DetachInternetGateway(s.ID, s.Attributes["vpc_id"])
	if err != nil {
		ec2err, ok := err.(*ec2.Error)
		if ok {
			if ec2err.Code == "InvalidInternetGatewayID.NotFound" {
				err = nil
				wait = false
			} else if ec2err.Code == "Gateway.NotAttached" {
				err = nil
				wait = false
			}
		}

		if err != nil {
			return err
		}
	}

	delete(s.Attributes, "vpc_id")

	if !wait {
		return nil
	}

	// Wait for it to be fully detached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", s.ID)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"attached", "detaching", "available"},
		Target:  "detached",
		Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "detached"),
		Timeout: 1 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to detach: %s",
			s.ID, err)
	}

	return nil
}

func resource_aws_internet_gateway_update_state(
	s *terraform.InstanceState,
	ig *ec2.InternetGateway) (*terraform.InstanceState, error) {
	return s, nil
}

// IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an internet gateway.
func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter())
		if err != nil {
			ec2err, ok := err.(*ec2.Error)
			if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := &resp.InternetGateways[0]
		return ig, "available", nil
	}
}

// IGAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used
// watch the state of an internet gateway's attachment.
func IGAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc {
	var start time.Time
	return func() (interface{}, string, error) {
		if start.IsZero() {
			start = time.Now()
		}

		resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter())
		if err != nil {
			ec2err, ok := err.(*ec2.Error)
			if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := &resp.InternetGateways[0]

		if time.Now().Sub(start) > 10*time.Second {
			return ig, expected, nil
		}

		if len(ig.Attachments) == 0 {
			// No attachments, we're detached
			return ig, "detached", nil
		}

		return ig, ig.Attachments[0].State, nil
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsInternetGateway() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsInternetGatewayCreate,
		Read:   resourceAwsInternetGatewayRead,
		Update: resourceAwsInternetGatewayUpdate,
		Delete: resourceAwsInternetGatewayDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"vpc_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"tags": tagsSchema(),
		},
	}
}

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

	// Create the gateway
	log.Printf("[DEBUG] Creating internet gateway")
	var err error
	resp, err := conn.CreateInternetGateway(nil)
	if err != nil {
		return fmt.Errorf("Error creating internet gateway: %s", err)
	}

	// Get the ID and store it
	ig := *resp.InternetGateway
	d.SetId(*ig.InternetGatewayId)
	log.Printf("[INFO] InternetGateway ID: %s", d.Id())

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		igRaw, _, err := IGStateRefreshFunc(conn, d.Id())()
		if igRaw != nil {
			return nil
		}
		if err == nil {
			return resource.RetryableError(err)
		} else {
			return resource.NonRetryableError(err)
		}
	})

	if err != nil {
		return errwrap.Wrapf("{{err}}", err)
	}

	err = setTags(conn, d)
	if err != nil {
		return err
	}

	// Attach the new gateway to the correct vpc
	return resourceAwsInternetGatewayAttach(d, meta)
}

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

	igRaw, _, err := IGStateRefreshFunc(conn, d.Id())()
	if err != nil {
		return err
	}
	if igRaw == nil {
		// Seems we have lost our internet gateway
		d.SetId("")
		return nil
	}

	ig := igRaw.(*ec2.InternetGateway)
	if len(ig.Attachments) == 0 {
		// Gateway exists but not attached to the VPC
		d.Set("vpc_id", "")
	} else {
		d.Set("vpc_id", ig.Attachments[0].VpcId)
	}

	d.Set("tags", tagsToMap(ig.Tags))

	return nil
}

func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
	if d.HasChange("vpc_id") {
		// If we're already attached, detach it first
		if err := resourceAwsInternetGatewayDetach(d, meta); err != nil {
			return err
		}

		// Attach the gateway to the new vpc
		if err := resourceAwsInternetGatewayAttach(d, meta); err != nil {
			return err
		}
	}

	conn := meta.(*AWSClient).ec2conn

	if err := setTags(conn, d); err != nil {
		return err
	}

	d.SetPartial("tags")

	return nil
}

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

	// Detach if it is attached
	if err := resourceAwsInternetGatewayDetach(d, meta); err != nil {
		return err
	}

	log.Printf("[INFO] Deleting Internet Gateway: %s", d.Id())

	return resource.Retry(5*time.Minute, func() *resource.RetryError {
		_, err := conn.DeleteInternetGateway(&ec2.DeleteInternetGatewayInput{
			InternetGatewayId: aws.String(d.Id()),
		})
		if err == nil {
			return nil
		}

		ec2err, ok := err.(awserr.Error)
		if !ok {
			return resource.RetryableError(err)
		}

		switch ec2err.Code() {
		case "InvalidInternetGatewayID.NotFound":
			return nil
		case "DependencyViolation":
			return resource.RetryableError(err) // retry
		}

		return resource.NonRetryableError(err)
	})
}

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

	if d.Get("vpc_id").(string) == "" {
		log.Printf(
			"[DEBUG] Not attaching Internet Gateway '%s' as no VPC ID is set",
			d.Id())
		return nil
	}

	log.Printf(
		"[INFO] Attaching Internet Gateway '%s' to VPC '%s'",
		d.Id(),
		d.Get("vpc_id").(string))

	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		_, err := conn.AttachInternetGateway(&ec2.AttachInternetGatewayInput{
			InternetGatewayId: aws.String(d.Id()),
			VpcId:             aws.String(d.Get("vpc_id").(string)),
		})
		if err == nil {
			return nil
		}
		if ec2err, ok := err.(awserr.Error); ok {
			switch ec2err.Code() {
			case "InvalidInternetGatewayID.NotFound":
				return resource.RetryableError(err) // retry
			}
		}
		return resource.NonRetryableError(err)
	})
	if err != nil {
		return err
	}

	// A note on the states below: the AWS docs (as of July, 2014) say
	// that the states would be: attached, attaching, detached, detaching,
	// but when running, I noticed that the state is usually "available" when
	// it is attached.

	// Wait for it to be fully attached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending: []string{"detached", "attaching"},
		Target:  []string{"available"},
		Refresh: IGAttachStateRefreshFunc(conn, d.Id(), "available"),
		Timeout: 4 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to attach: %s",
			d.Id(), err)
	}

	return nil
}

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

	// Get the old VPC ID to detach from
	vpcID, _ := d.GetChange("vpc_id")

	if vpcID.(string) == "" {
		log.Printf(
			"[DEBUG] Not detaching Internet Gateway '%s' as no VPC ID is set",
			d.Id())
		return nil
	}

	log.Printf(
		"[INFO] Detaching Internet Gateway '%s' from VPC '%s'",
		d.Id(),
		vpcID.(string))

	// Wait for it to be fully detached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending:        []string{"detaching"},
		Target:         []string{"detached"},
		Refresh:        detachIGStateRefreshFunc(conn, d.Id(), vpcID.(string)),
		Timeout:        15 * time.Minute,
		Delay:          10 * time.Second,
		NotFoundChecks: 30,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to detach: %s",
			d.Id(), err)
	}

	return nil
}

// InstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an EC2 instance.
func detachIGStateRefreshFunc(conn *ec2.EC2, gatewayID, vpcID string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		_, err := conn.DetachInternetGateway(&ec2.DetachInternetGatewayInput{
			InternetGatewayId: aws.String(gatewayID),
			VpcId:             aws.String(vpcID),
		})
		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok {
				switch ec2err.Code() {
				case "InvalidInternetGatewayID.NotFound":
					log.Printf("[TRACE] Error detaching Internet Gateway '%s' from VPC '%s': %s", gatewayID, vpcID, err)
					return nil, "Not Found", nil

				case "Gateway.NotAttached":
					return "detached", "detached", nil

				case "DependencyViolation":
					return nil, "detaching", nil
				}
			}
		}

		// DetachInternetGateway only returns an error, so if it's nil, assume we're
		// detached
		return "detached", "detached", nil
	}
}

// IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an internet gateway.
func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysInput{
			InternetGatewayIds: []*string{aws.String(id)},
		})
		if err != nil {
			ec2err, ok := err.(awserr.Error)
			if ok && ec2err.Code() == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := resp.InternetGateways[0]
		return ig, "available", nil
	}
}

// IGAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used
// watch the state of an internet gateway's attachment.
func IGAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc {
	var start time.Time
	return func() (interface{}, string, error) {
		if start.IsZero() {
			start = time.Now()
		}

		resp, err := conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysInput{
			InternetGatewayIds: []*string{aws.String(id)},
		})
		if err != nil {
			ec2err, ok := err.(awserr.Error)
			if ok && ec2err.Code() == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := resp.InternetGateways[0]

		if time.Now().Sub(start) > 10*time.Second {
			return ig, expected, nil
		}

		if len(ig.Attachments) == 0 {
			// No attachments, we're detached
			return ig, "detached", nil
		}

		return ig, *ig.Attachments[0].State, nil
	}
}
<commit_msg>provider/aws: Increase timeout for deleting IGW (#14705)<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsInternetGateway() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsInternetGatewayCreate,
		Read:   resourceAwsInternetGatewayRead,
		Update: resourceAwsInternetGatewayUpdate,
		Delete: resourceAwsInternetGatewayDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"vpc_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"tags": tagsSchema(),
		},
	}
}

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

	// Create the gateway
	log.Printf("[DEBUG] Creating internet gateway")
	var err error
	resp, err := conn.CreateInternetGateway(nil)
	if err != nil {
		return fmt.Errorf("Error creating internet gateway: %s", err)
	}

	// Get the ID and store it
	ig := *resp.InternetGateway
	d.SetId(*ig.InternetGatewayId)
	log.Printf("[INFO] InternetGateway ID: %s", d.Id())

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		igRaw, _, err := IGStateRefreshFunc(conn, d.Id())()
		if igRaw != nil {
			return nil
		}
		if err == nil {
			return resource.RetryableError(err)
		} else {
			return resource.NonRetryableError(err)
		}
	})

	if err != nil {
		return errwrap.Wrapf("{{err}}", err)
	}

	err = setTags(conn, d)
	if err != nil {
		return err
	}

	// Attach the new gateway to the correct vpc
	return resourceAwsInternetGatewayAttach(d, meta)
}

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

	igRaw, _, err := IGStateRefreshFunc(conn, d.Id())()
	if err != nil {
		return err
	}
	if igRaw == nil {
		// Seems we have lost our internet gateway
		d.SetId("")
		return nil
	}

	ig := igRaw.(*ec2.InternetGateway)
	if len(ig.Attachments) == 0 {
		// Gateway exists but not attached to the VPC
		d.Set("vpc_id", "")
	} else {
		d.Set("vpc_id", ig.Attachments[0].VpcId)
	}

	d.Set("tags", tagsToMap(ig.Tags))

	return nil
}

func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
	if d.HasChange("vpc_id") {
		// If we're already attached, detach it first
		if err := resourceAwsInternetGatewayDetach(d, meta); err != nil {
			return err
		}

		// Attach the gateway to the new vpc
		if err := resourceAwsInternetGatewayAttach(d, meta); err != nil {
			return err
		}
	}

	conn := meta.(*AWSClient).ec2conn

	if err := setTags(conn, d); err != nil {
		return err
	}

	d.SetPartial("tags")

	return nil
}

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

	// Detach if it is attached
	if err := resourceAwsInternetGatewayDetach(d, meta); err != nil {
		return err
	}

	log.Printf("[INFO] Deleting Internet Gateway: %s", d.Id())

	return resource.Retry(10*time.Minute, func() *resource.RetryError {
		_, err := conn.DeleteInternetGateway(&ec2.DeleteInternetGatewayInput{
			InternetGatewayId: aws.String(d.Id()),
		})
		if err == nil {
			return nil
		}

		ec2err, ok := err.(awserr.Error)
		if !ok {
			return resource.RetryableError(err)
		}

		switch ec2err.Code() {
		case "InvalidInternetGatewayID.NotFound":
			return nil
		case "DependencyViolation":
			return resource.RetryableError(err) // retry
		}

		return resource.NonRetryableError(err)
	})
}

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

	if d.Get("vpc_id").(string) == "" {
		log.Printf(
			"[DEBUG] Not attaching Internet Gateway '%s' as no VPC ID is set",
			d.Id())
		return nil
	}

	log.Printf(
		"[INFO] Attaching Internet Gateway '%s' to VPC '%s'",
		d.Id(),
		d.Get("vpc_id").(string))

	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		_, err := conn.AttachInternetGateway(&ec2.AttachInternetGatewayInput{
			InternetGatewayId: aws.String(d.Id()),
			VpcId:             aws.String(d.Get("vpc_id").(string)),
		})
		if err == nil {
			return nil
		}
		if ec2err, ok := err.(awserr.Error); ok {
			switch ec2err.Code() {
			case "InvalidInternetGatewayID.NotFound":
				return resource.RetryableError(err) // retry
			}
		}
		return resource.NonRetryableError(err)
	})
	if err != nil {
		return err
	}

	// A note on the states below: the AWS docs (as of July, 2014) say
	// that the states would be: attached, attaching, detached, detaching,
	// but when running, I noticed that the state is usually "available" when
	// it is attached.

	// Wait for it to be fully attached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending: []string{"detached", "attaching"},
		Target:  []string{"available"},
		Refresh: IGAttachStateRefreshFunc(conn, d.Id(), "available"),
		Timeout: 4 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to attach: %s",
			d.Id(), err)
	}

	return nil
}

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

	// Get the old VPC ID to detach from
	vpcID, _ := d.GetChange("vpc_id")

	if vpcID.(string) == "" {
		log.Printf(
			"[DEBUG] Not detaching Internet Gateway '%s' as no VPC ID is set",
			d.Id())
		return nil
	}

	log.Printf(
		"[INFO] Detaching Internet Gateway '%s' from VPC '%s'",
		d.Id(),
		vpcID.(string))

	// Wait for it to be fully detached before continuing
	log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", d.Id())
	stateConf := &resource.StateChangeConf{
		Pending:        []string{"detaching"},
		Target:         []string{"detached"},
		Refresh:        detachIGStateRefreshFunc(conn, d.Id(), vpcID.(string)),
		Timeout:        15 * time.Minute,
		Delay:          10 * time.Second,
		NotFoundChecks: 30,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf(
			"Error waiting for internet gateway (%s) to detach: %s",
			d.Id(), err)
	}

	return nil
}

// InstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an EC2 instance.
func detachIGStateRefreshFunc(conn *ec2.EC2, gatewayID, vpcID string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		_, err := conn.DetachInternetGateway(&ec2.DetachInternetGatewayInput{
			InternetGatewayId: aws.String(gatewayID),
			VpcId:             aws.String(vpcID),
		})
		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok {
				switch ec2err.Code() {
				case "InvalidInternetGatewayID.NotFound":
					log.Printf("[TRACE] Error detaching Internet Gateway '%s' from VPC '%s': %s", gatewayID, vpcID, err)
					return nil, "Not Found", nil

				case "Gateway.NotAttached":
					return "detached", "detached", nil

				case "DependencyViolation":
					return nil, "detaching", nil
				}
			}
		}

		// DetachInternetGateway only returns an error, so if it's nil, assume we're
		// detached
		return "detached", "detached", nil
	}
}

// IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an internet gateway.
func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		resp, err := conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysInput{
			InternetGatewayIds: []*string{aws.String(id)},
		})
		if err != nil {
			ec2err, ok := err.(awserr.Error)
			if ok && ec2err.Code() == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := resp.InternetGateways[0]
		return ig, "available", nil
	}
}

// IGAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used
// watch the state of an internet gateway's attachment.
func IGAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc {
	var start time.Time
	return func() (interface{}, string, error) {
		if start.IsZero() {
			start = time.Now()
		}

		resp, err := conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysInput{
			InternetGatewayIds: []*string{aws.String(id)},
		})
		if err != nil {
			ec2err, ok := err.(awserr.Error)
			if ok && ec2err.Code() == "InvalidInternetGatewayID.NotFound" {
				resp = nil
			} else {
				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil {
			// Sometimes AWS just has consistency issues and doesn't see
			// our instance yet. Return an empty state.
			return nil, "", nil
		}

		ig := resp.InternetGateways[0]

		if time.Now().Sub(start) > 10*time.Second {
			return ig, expected, nil
		}

		if len(ig.Attachments) == 0 {
			// No attachments, we're detached
			return ig, "detached", nil
		}

		return ig, *ig.Attachments[0].State, nil
	}
}
<|endoftext|>


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

// +build unit

package strategy_test

import (
	"github.com/control-center/serviced/scheduler/strategy"
	"github.com/control-center/serviced/scheduler/strategy/mocks"
	"github.com/control-center/serviced/utils"
	. "gopkg.in/check.v1"
)

const (
	Gigabyte = 1024 * 1024 * 1024
)

func newHost(cores int, memgigs uint64) *mocks.Host {
	host := &mocks.Host{}
	host.On("TotalCores").Return(cores)
	host.On("TotalMemory").Return(memgigs * Gigabyte)
	return host
}

func newService(cores int, memgigs uint64) *mocks.ServiceConfig {
	id, _ := utils.NewUUID36()
	svc := &mocks.ServiceConfig{}
	svc.On("RequestedCorePercent").Return(cores * 100)
	svc.On("RequestedMemoryBytes").Return(memgigs * Gigabyte)
	svc.On("GetServiceID").Return(id)
	return svc
}

// Given two identical hosts, one of which has a service running on it, verify
// that the second host is deemed most appropriate
func (s *StrategySuite) TestSimpleScoring(c *C) {
	hostA := newHost(2, 2)
	hostB := newHost(2, 2)

	svc := newService(1, 1)
	svc2 := newService(1, 1)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{})

	under, over := strategy.ScoreHosts(svc2, []strategy.Host{hostA, hostB})
	c.Assert(under[0].Host, Equals, hostB)
	c.Assert(under[1].Host, Equals, hostA)
	c.Assert(over, HasLen, 0)
}

// Given two non-identical hosts, one of which has fewer overall resources but
// more free space, verify that the freer host is deemed most appropriate
func (s *StrategySuite) TestUnbalancedScoring(c *C) {
	hostA := newHost(3, 3)
	hostB := newHost(2, 2)

	svc := newService(2, 2)
	svc2 := newService(1, 1)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{})

	under, over := strategy.ScoreHosts(svc2, []strategy.Host{hostA, hostB})

	c.Assert(under[0].Host, Equals, hostB)
	c.Assert(under[1].Host, Equals, hostA)
	c.Assert(over, HasLen, 0)

}

// Given two identical hosts, one of which has plenty of memory but no
// available CPU, verify that it is not selected as the appropriate host
func (s *StrategySuite) TestNoCPUAvailable(c *C) {
	hostA := newHost(6, 6)
	hostB := newHost(6, 6)

	svc := newService(6, 1)
	svc2 := newService(4, 4)
	svc3 := newService(2, 2)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{svc2})

	under, over := strategy.ScoreHosts(svc3, []strategy.Host{hostA, hostB})

	c.Assert(under[0].Host, Equals, hostB)
	c.Assert(over[0].Host, Equals, hostA)
}

// Given two identical hosts, none of which has enough CPU free but one of
// which has enough memory, make sure the host with the memory is the one
// assigned
func (s *StrategySuite) TestNotEnoughResources(c *C) {
	hostA := newHost(5, 5)
	hostB := newHost(5, 5)

	svc := newService(4, 4)
	svc2 := newService(4, 3)
	svc3 := newService(2, 2)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{svc2})

	under, over := strategy.ScoreHosts(svc3, []strategy.Host{hostA, hostB})

	c.Assert(under, HasLen, 0)
	c.Assert(over[0].Host, Equals, hostB)
	c.Assert(over[1].Host, Equals, hostA)
}

// Given two identical hosts, all of which are overloaded both in terms of
// memory and CPU, make sure the one with the least memory spoken for is
// preferred over the one with the least CPU spoken for
func (s *StrategySuite) TestMemoryPreferred(c *C) {

	hostA := newHost(5, 5)
	hostB := newHost(5, 5)

	svc := newService(7, 6)
	svc2 := newService(6, 7)
	svc3 := newService(1, 1)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{svc2})

	under, over := strategy.ScoreHosts(svc3, []strategy.Host{hostA, hostB})

	c.Assert(under, HasLen, 0)
	c.Assert(over[0].Host, Equals, hostA)
	c.Assert(over[1].Host, Equals, hostB)
}
<commit_msg>fix tests<commit_after>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build unit

package strategy_test

import (
	"github.com/control-center/serviced/scheduler/strategy"
	"github.com/control-center/serviced/scheduler/strategy/mocks"
	"github.com/control-center/serviced/utils"
	. "gopkg.in/check.v1"
)

const (
	Gigabyte = 1024 * 1024 * 1024
)

func newHost(cores int, memgigs uint64) *mocks.Host {
	host := &mocks.Host{}
	host.On("TotalCores").Return(cores)
	host.On("TotalMemory").Return(memgigs * Gigabyte)
	id, _ := utils.NewUUID36()
	host.On("HostID").Return(id)
	return host
}

func newService(cores int, memgigs uint64) *mocks.ServiceConfig {
	id, _ := utils.NewUUID36()
	svc := &mocks.ServiceConfig{}
	svc.On("RequestedCorePercent").Return(cores * 100)
	svc.On("RequestedMemoryBytes").Return(memgigs * Gigabyte)
	svc.On("GetServiceID").Return(id)
	return svc
}

// Given two identical hosts, one of which has a service running on it, verify
// that the second host is deemed most appropriate
func (s *StrategySuite) TestSimpleScoring(c *C) {
	hostA := newHost(2, 2)
	hostB := newHost(2, 2)

	svc := newService(1, 1)
	svc2 := newService(1, 1)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{})

	under, over := strategy.ScoreHosts(svc2, []strategy.Host{hostA, hostB})
	c.Assert(under[0].Host, Equals, hostB)
	c.Assert(under[1].Host, Equals, hostA)
	c.Assert(over, HasLen, 0)
}

// Given two non-identical hosts, one of which has fewer overall resources but
// more free space, verify that the freer host is deemed most appropriate
func (s *StrategySuite) TestUnbalancedScoring(c *C) {
	hostA := newHost(3, 3)
	hostB := newHost(2, 2)

	svc := newService(2, 2)
	svc2 := newService(1, 1)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{})

	under, over := strategy.ScoreHosts(svc2, []strategy.Host{hostA, hostB})

	c.Assert(under[0].Host, Equals, hostB)
	c.Assert(under[1].Host, Equals, hostA)
	c.Assert(over, HasLen, 0)

}

// Given two identical hosts, one of which has plenty of memory but no
// available CPU, verify that it is not selected as the appropriate host
func (s *StrategySuite) TestNoCPUAvailable(c *C) {
	hostA := newHost(6, 6)
	hostB := newHost(6, 6)

	svc := newService(6, 1)
	svc2 := newService(4, 4)
	svc3 := newService(2, 2)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{svc2})

	under, over := strategy.ScoreHosts(svc3, []strategy.Host{hostA, hostB})

	c.Assert(under[0].Host, Equals, hostB)
	c.Assert(over[0].Host, Equals, hostA)
}

// Given two identical hosts, none of which has enough CPU free but one of
// which has enough memory, make sure the host with the memory is the one
// assigned
func (s *StrategySuite) TestNotEnoughResources(c *C) {
	hostA := newHost(5, 5)
	hostB := newHost(5, 5)

	svc := newService(4, 4)
	svc2 := newService(4, 3)
	svc3 := newService(2, 2)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{svc2})

	under, over := strategy.ScoreHosts(svc3, []strategy.Host{hostA, hostB})

	c.Assert(under, HasLen, 0)
	c.Assert(over[0].Host, Equals, hostB)
	c.Assert(over[1].Host, Equals, hostA)
}

// Given two identical hosts, all of which are overloaded both in terms of
// memory and CPU, make sure the one with the least memory spoken for is
// preferred over the one with the least CPU spoken for
func (s *StrategySuite) TestMemoryPreferred(c *C) {

	hostA := newHost(5, 5)
	hostB := newHost(5, 5)

	svc := newService(7, 6)
	svc2 := newService(6, 7)
	svc3 := newService(1, 1)

	hostA.On("RunningServices").Return([]strategy.ServiceConfig{svc})
	hostB.On("RunningServices").Return([]strategy.ServiceConfig{svc2})

	under, over := strategy.ScoreHosts(svc3, []strategy.Host{hostA, hostB})

	c.Assert(under, HasLen, 0)
	c.Assert(over[0].Host, Equals, hostA)
	c.Assert(over[1].Host, Equals, hostB)
}
<|endoftext|>


<commit_before>package terminal

import (
	"bytes"
	"io/ioutil"
	"strings"
	"testing"
)

func TestTerminal(t *testing.T) {
	var buf bytes.Buffer
	term := New(&buf)
	term.SetLastLine("-- last line --")
	term.Refresh("prompt ", []rune("aaa"), 2)
	if got := buf.String(); strings.Index(got, "prompt aaa") < 0 {
		t.Errorf("got %q, but should include %q", got, "prompt aaa")
	}
}

func BenchmarkTerminal(b *testing.B) {
	term := New(ioutil.Discard)
	term.SetLastLine("-- last line --")
	for i := 0; i < b.N; i++ {
		term.Refresh("prompt ", []rune("aaa"), 2)
	}
}
<commit_msg>Make it simple<commit_after>package terminal

import (
	"bytes"
	"io/ioutil"
	"strings"
	"testing"
)

func TestTerminal(t *testing.T) {
	var buf bytes.Buffer
	term := New(&buf)
	term.SetLastLine("-- last line --")
	term.Refresh("prompt ", []rune("aaa"), 2)
	if got := buf.String(); !strings.Contains(got, "prompt aaa") {
		t.Errorf("got %q, but should include %q", got, "prompt aaa")
	}
}

func BenchmarkTerminal(b *testing.B) {
	term := New(ioutil.Discard)
	term.SetLastLine("-- last line --")
	for i := 0; i < b.N; i++ {
		term.Refresh("prompt ", []rune("aaa"), 2)
	}
}
<|endoftext|>


<commit_before>package githook

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

	"github.com/bleenco/abstruse/server/core"
	"github.com/drone/go-scm/scm"
)

const emptyCommit = "0000000000000000000000000000000000000000"

// NewParser returns a new GitHookParser.
func NewParser(client *scm.Client) core.GitHookParser {
	return &parser{client}
}

type parser struct {
	client *scm.Client
}

func (p *parser) Parse(req *http.Request, secretFunc func(string) *core.Repository) (*core.GitHook, *core.Repository, error) {
	fn := func(webhook scm.Webhook) (string, error) {
		if webhook == nil {
			return "", scm.ErrUnknownEvent
		}
		repo := webhook.Repository()
		fullname := fmt.Sprintf("%s/%s", repo.Namespace, repo.Name)
		r := secretFunc(fullname)
		if r == nil || r.Provider.Secret == "" {
			return "", fmt.Errorf("cannot find repository")
		}
		return r.Provider.Secret, nil
	}

	payload, err := p.client.Webhooks.Parse(req, fn)
	if err == scm.ErrUnknownEvent {
		return nil, nil, nil
	}
	if err != nil {
		return nil, nil, err
	}

	switch h := payload.(type) {
	case *scm.PushHook:
		return parsePushHook(h)
	case *scm.TagHook:
		return nil, nil, nil
	case *scm.PullRequestHook:
		return nil, nil, nil
	case *scm.BranchHook:
		return nil, nil, nil
	default:
		return nil, nil, nil
	}
}

func parsePushHook(h *scm.PushHook) (*core.GitHook, *core.Repository, error) {
	repo := &core.Repository{}
	githook := &core.GitHook{}

	if h.Commit.Sha == emptyCommit {
		return nil, nil, nil
	}

	if strings.HasPrefix(h.Ref, "refs/tags/") {
		githook = &core.GitHook{
			Event:        core.EventTag,
			Action:       core.ActionCreate,
			Link:         h.Commit.Link,
			Timestamp:    h.Commit.Author.Date,
			Message:      h.Commit.Message,
			Before:       h.Before,
			After:        h.After,
			Source:       scm.TrimRef(h.BaseRef),
			Target:       scm.TrimRef(h.BaseRef),
			Ref:          h.Ref,
			AuthorEmail:  h.Commit.Author.Email,
			AuthorAvatar: h.Commit.Author.Avatar,
			AuthorName:   h.Commit.Author.Name,
			AuthorLogin:  h.Commit.Author.Login,
			SenderEmail:  h.Sender.Email,
			SenderAvatar: h.Sender.Avatar,
			SenderName:   h.Sender.Name,
			SenderLogin:  h.Sender.Login,
		}
	} else {
		githook = &core.GitHook{
			Event:        core.EventPush,
			Link:         h.Commit.Link,
			Timestamp:    h.Commit.Author.Date,
			Message:      h.Commit.Message,
			Before:       h.Before,
			After:        h.Commit.Sha,
			Ref:          h.Ref,
			Source:       strings.TrimPrefix(h.Ref, "refs/heads/"),
			Target:       strings.TrimPrefix(h.Ref, "refs/heads/"),
			AuthorEmail:  h.Commit.Author.Email,
			AuthorAvatar: h.Commit.Author.Avatar,
			AuthorName:   h.Commit.Author.Name,
			AuthorLogin:  h.Commit.Author.Login,
			SenderEmail:  h.Sender.Email,
			SenderAvatar: h.Sender.Avatar,
			SenderName:   h.Sender.Name,
			SenderLogin:  h.Sender.Login,
		}
	}

	// TODO: check branch field!
	repo = &core.Repository{
		UID:       h.Repo.ID,
		Namespace: h.Repo.Namespace,
		Name:      h.Repo.Name,
		FullName:  fmt.Sprintf("%s/%s", h.Repo.Namespace, h.Repo.Name),
		URL:       h.Repo.Link,
		Private:   h.Repo.Private,
		Clone:     h.Repo.Clone,
		CloneSSH:  h.Repo.CloneSSH,
	}

	if githook.AuthorAvatar == "" {
		githook.AuthorAvatar = h.Sender.Avatar
	}

	return githook, repo, nil
}
<commit_msg>feat(webhooks): PR, tag, branch parsers<commit_after>package githook

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

	"github.com/bleenco/abstruse/server/core"
	"github.com/drone/go-scm/scm"
)

const emptyCommit = "0000000000000000000000000000000000000000"

// NewParser returns a new GitHookParser.
func NewParser(client *scm.Client) core.GitHookParser {
	return &parser{client}
}

type parser struct {
	client *scm.Client
}

func (p *parser) Parse(req *http.Request, secretFunc func(string) *core.Repository) (*core.GitHook, *core.Repository, error) {
	fn := func(webhook scm.Webhook) (string, error) {
		if webhook == nil {
			return "", scm.ErrUnknownEvent
		}
		repo := webhook.Repository()
		fullname := fmt.Sprintf("%s/%s", repo.Namespace, repo.Name)
		r := secretFunc(fullname)
		if r == nil || r.Provider.Secret == "" {
			return "", fmt.Errorf("cannot find repository")
		}
		return r.Provider.Secret, nil
	}

	payload, err := p.client.Webhooks.Parse(req, fn)
	if err == scm.ErrUnknownEvent {
		return nil, nil, nil
	}
	if err != nil {
		return nil, nil, err
	}

	switch h := payload.(type) {
	case *scm.PushHook:
		return p.parsePushHook(h)
	case *scm.TagHook:
		return p.parseTagHook(h)
	case *scm.PullRequestHook:
		return p.parsePRHook(h)
	case *scm.BranchHook:
		return p.parseBranchHook(h)
	default:
		return nil, nil, nil
	}
}

func (p *parser) parsePushHook(h *scm.PushHook) (*core.GitHook, *core.Repository, error) {
	repo := &core.Repository{}
	githook := &core.GitHook{}

	if h.Commit.Sha == emptyCommit {
		return nil, nil, nil
	}

	if strings.HasPrefix(h.Ref, "refs/tags/") {
		githook = &core.GitHook{
			Event:        core.EventTag,
			Action:       core.ActionCreate,
			Link:         h.Commit.Link,
			Timestamp:    h.Commit.Author.Date,
			Message:      h.Commit.Message,
			Before:       h.Before,
			After:        h.After,
			Source:       scm.TrimRef(h.BaseRef),
			Target:       scm.TrimRef(h.BaseRef),
			Ref:          h.Ref,
			AuthorEmail:  h.Commit.Author.Email,
			AuthorAvatar: h.Commit.Author.Avatar,
			AuthorName:   h.Commit.Author.Name,
			AuthorLogin:  h.Commit.Author.Login,
			SenderEmail:  h.Sender.Email,
			SenderAvatar: h.Sender.Avatar,
			SenderName:   h.Sender.Name,
			SenderLogin:  h.Sender.Login,
		}
	} else {
		githook = &core.GitHook{
			Event:        core.EventPush,
			Link:         h.Commit.Link,
			Timestamp:    h.Commit.Author.Date,
			Message:      h.Commit.Message,
			Before:       h.Before,
			After:        h.Commit.Sha,
			Ref:          h.Ref,
			Source:       strings.TrimPrefix(h.Ref, "refs/heads/"),
			Target:       strings.TrimPrefix(h.Ref, "refs/heads/"),
			AuthorEmail:  h.Commit.Author.Email,
			AuthorAvatar: h.Commit.Author.Avatar,
			AuthorName:   h.Commit.Author.Name,
			AuthorLogin:  h.Commit.Author.Login,
			SenderEmail:  h.Sender.Email,
			SenderAvatar: h.Sender.Avatar,
			SenderName:   h.Sender.Name,
			SenderLogin:  h.Sender.Login,
		}
	}

	// TODO: check branch field!
	repo = &core.Repository{
		UID:       h.Repo.ID,
		Namespace: h.Repo.Namespace,
		Name:      h.Repo.Name,
		FullName:  fmt.Sprintf("%s/%s", h.Repo.Namespace, h.Repo.Name),
		URL:       h.Repo.Link,
		Private:   h.Repo.Private,
		Clone:     h.Repo.Clone,
		CloneSSH:  h.Repo.CloneSSH,
	}

	if githook.AuthorAvatar == "" {
		githook.AuthorAvatar = h.Sender.Avatar
	}

	return githook, repo, nil
}

func (p *parser) parseTagHook(h *scm.TagHook) (*core.GitHook, *core.Repository, error) {
	repo := &core.Repository{}
	githook := &core.GitHook{}

	if h.Action != scm.ActionCreate {
		return nil, nil, nil
	}

	// When tag is created github, gitea and gitlab sends both a push
	// and a tag hook. Push hook contains more info so we use that.
	if p.client.Driver == scm.DriverGithub ||
		p.client.Driver == scm.DriverGitea ||
		p.client.Driver == scm.DriverGitlab {
		return nil, nil, nil
	}

	githook = &core.GitHook{
		Event:        core.EventTag,
		Action:       core.ActionCreate,
		Link:         "",
		Message:      path.Base(h.Ref.Path),
		After:        h.Ref.Sha,
		Ref:          h.Ref.Name,
		Source:       h.Ref.Name,
		Target:       h.Ref.Name,
		AuthorLogin:  h.Sender.Login,
		AuthorName:   h.Sender.Name,
		AuthorEmail:  h.Sender.Email,
		AuthorAvatar: h.Sender.Avatar,
		SenderEmail:  h.Sender.Email,
		SenderAvatar: h.Sender.Avatar,
		SenderName:   h.Sender.Name,
		SenderLogin:  h.Sender.Login,
	}
	repo = &core.Repository{
		UID:       h.Repo.ID,
		Namespace: h.Repo.Namespace,
		Name:      h.Repo.Name,
		FullName:  fmt.Sprintf("%s/%s", h.Repo.Namespace, h.Repo.Name),
		URL:       h.Repo.Link,
		Private:   h.Repo.Private,
		Clone:     h.Repo.Clone,
		CloneSSH:  h.Repo.CloneSSH,
	}

	if !strings.HasPrefix(githook.Ref, "refs/tags/") {
		githook.Ref = fmt.Sprintf("refs/tags/%s", githook.Ref)
	}

	return githook, repo, nil
}

func (p *parser) parsePRHook(h *scm.PullRequestHook) (*core.GitHook, *core.Repository, error) {
	if h.Action == scm.ActionClose {
		return nil, nil, nil
	}

	githook := &core.GitHook{
		Event:        core.EventPullRequest,
		Action:       h.Action.String(),
		Link:         h.PullRequest.Link,
		Timestamp:    h.PullRequest.Created,
		Title:        h.PullRequest.Title,
		Message:      h.PullRequest.Body,
		Before:       h.PullRequest.Base.Sha,
		After:        h.PullRequest.Sha,
		Ref:          h.PullRequest.Ref,
		Fork:         h.PullRequest.Fork,
		Source:       h.PullRequest.Source,
		Target:       h.PullRequest.Target,
		AuthorLogin:  h.PullRequest.Author.Login,
		AuthorName:   h.PullRequest.Author.Name,
		AuthorEmail:  h.PullRequest.Author.Email,
		AuthorAvatar: h.PullRequest.Author.Avatar,
		SenderEmail:  h.Sender.Email,
		SenderAvatar: h.Sender.Avatar,
		SenderName:   h.Sender.Name,
		SenderLogin:  h.Sender.Login,
		PrNumber:     h.PullRequest.Number,
		PrTitle:      h.PullRequest.Title,
		PrBody:       h.PullRequest.Body,
	}
	repo := &core.Repository{
		UID:       h.Repo.ID,
		Namespace: h.Repo.Namespace,
		Name:      h.Repo.Name,
		FullName:  fmt.Sprintf("%s/%s", h.Repo.Namespace, h.Repo.Name),
		URL:       h.Repo.Link,
		Private:   h.Repo.Private,
		Clone:     h.Repo.Clone,
		CloneSSH:  h.Repo.CloneSSH,
	}

	return githook, repo, nil
}

func (p *parser) parseBranchHook(h *scm.BranchHook) (*core.GitHook, *core.Repository, error) {
	if h.Action != scm.ActionCreate {
		return nil, nil, nil
	}

	githook := &core.GitHook{
		Event:        core.EventPush,
		Link:         "",
		Message:      "",
		After:        h.Ref.Sha,
		Ref:          h.Ref.Name,
		Source:       h.Ref.Name,
		Target:       h.Ref.Name,
		AuthorLogin:  h.Sender.Login,
		AuthorName:   h.Sender.Name,
		AuthorEmail:  h.Sender.Email,
		AuthorAvatar: h.Sender.Avatar,
		SenderLogin:  h.Sender.Login,
		SenderName:   h.Sender.Name,
		SenderEmail:  h.Sender.Email,
		SenderAvatar: h.Sender.Avatar,
	}
	repo := &core.Repository{
		UID:       h.Repo.ID,
		Namespace: h.Repo.Namespace,
		Name:      h.Repo.Name,
		FullName:  fmt.Sprintf("%s/%s", h.Repo.Namespace, h.Repo.Name),
		URL:       h.Repo.Link,
		Private:   h.Repo.Private,
		Clone:     h.Repo.Clone,
		CloneSSH:  h.Repo.CloneSSH,
	}

	return githook, repo, nil
}
<|endoftext|>


<commit_before>package validate_test

import (
	"fmt"

	"github.com/lxc/lxd/shared/validate"
)

func ExampleIsNetworkMAC() {
	tests := []string{
		"00:00:5e:00:53:01",
		"02:00:5e:10:00:00:00:01", // too long
		"00-00-5e-00-53-01",       // invalid delimiter
		"0000.5e00.5301",          // invalid delimiter
		"invalid",
		"",
	}

	for _, v := range tests {
		err := validate.IsNetworkMAC(v)
		fmt.Printf("%s, %t\n", v, err == nil)
	}

	// Output: 00:00:5e:00:53:01, true
	// 02:00:5e:10:00:00:00:01, false
	// 00-00-5e-00-53-01, false
	// 0000.5e00.5301, false
	// invalid, false
	// , false
}

func ExampleIsPCIAddress() {
	tests := []string{
		"0000:12:ab.0", // valid
		"0010:12:ab.0", // valid
		"0000:12:CD.0", // valid
		"12:ab.0",      // valid
		"12:CD.0",      // valid
		"0000:12:gh.0", // invalid hex
		"0000:12:GH.0", // invalid hex
		"12:gh.0",      // invalid hex
		"12:GH.0",      // invalid hex
		"000:12:CD.0",  // wrong prefix
		"12.ab.0",      // invalid format
		"",
	}

	for _, v := range tests {
		err := validate.IsPCIAddress(v)
		fmt.Printf("%s, %t\n", v, err == nil)
	}

	// Output: 0000:12:ab.0, true
	// 0010:12:ab.0, true
	// 0000:12:CD.0, true
	// 12:ab.0, true
	// 12:CD.0, true
	// 0000:12:gh.0, false
	// 0000:12:GH.0, false
	// 12:gh.0, false
	// 12:GH.0, false
	// 000:12:CD.0, false
	// 12.ab.0, false
	// , false
}
<commit_msg>shared/validate/validate/test: Adds tests for Required and Optional<commit_after>package validate_test

import (
	"fmt"

	"github.com/lxc/lxd/shared/validate"
)

func ExampleIsNetworkMAC() {
	tests := []string{
		"00:00:5e:00:53:01",
		"02:00:5e:10:00:00:00:01", // too long
		"00-00-5e-00-53-01",       // invalid delimiter
		"0000.5e00.5301",          // invalid delimiter
		"invalid",
		"",
	}

	for _, v := range tests {
		err := validate.IsNetworkMAC(v)
		fmt.Printf("%s, %t\n", v, err == nil)
	}

	// Output: 00:00:5e:00:53:01, true
	// 02:00:5e:10:00:00:00:01, false
	// 00-00-5e-00-53-01, false
	// 0000.5e00.5301, false
	// invalid, false
	// , false
}

func ExampleIsPCIAddress() {
	tests := []string{
		"0000:12:ab.0", // valid
		"0010:12:ab.0", // valid
		"0000:12:CD.0", // valid
		"12:ab.0",      // valid
		"12:CD.0",      // valid
		"0000:12:gh.0", // invalid hex
		"0000:12:GH.0", // invalid hex
		"12:gh.0",      // invalid hex
		"12:GH.0",      // invalid hex
		"000:12:CD.0",  // wrong prefix
		"12.ab.0",      // invalid format
		"",
	}

	for _, v := range tests {
		err := validate.IsPCIAddress(v)
		fmt.Printf("%s, %t\n", v, err == nil)
	}

	// Output: 0000:12:ab.0, true
	// 0010:12:ab.0, true
	// 0000:12:CD.0, true
	// 12:ab.0, true
	// 12:CD.0, true
	// 0000:12:gh.0, false
	// 0000:12:GH.0, false
	// 12:gh.0, false
	// 12:GH.0, false
	// 000:12:CD.0, false
	// 12.ab.0, false
	// , false
}

func ExampleOptional() {
	tests := []string{
		"",
		"foo",
		"true",
	}

	for _, v := range tests {
		f := validate.Optional()
		fmt.Printf("%v ", f(v))

		f = validate.Optional(validate.IsBool)
		fmt.Printf("%v\n", f(v))
	}

	// Output: <nil> <nil>
	// <nil> Invalid value for a boolean "foo"
	// <nil> <nil>
}

func ExampleRequired() {
	tests := []string{
		"",
		"foo",
		"true",
	}

	for _, v := range tests {
		f := validate.Required()
		fmt.Printf("%v ", f(v))

		f = validate.Required(validate.IsBool)
		fmt.Printf("%v\n", f(v))
	}

	// Output: <nil> Invalid value for a boolean ""
	// <nil> Invalid value for a boolean "foo"
	// <nil> <nil>
}
<|endoftext|>