func (a *Adapter) S3Bucket() string {
	return a.albLogsS3Bucket
}
S3Bucket returns the S3 Bucket to be logged to
package aws
import (
	"errors"
	"fmt"
	"strings"
	"time"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/ec2metadata"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/acm"
	"github.com/aws/aws-sdk-go/service/acm/acmiface"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/autoscaling/autoscalingiface"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/aws/aws-sdk-go/service/elbv2/elbv2iface"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/iam/iamiface"
	"github.com/linki/instrumented_http"
	log "github.com/sirupsen/logrus"
	"github.com/zalando-incubator/kube-ingress-aws-controller/certs"
	"github.com/zalando-incubator/kube-ingress-aws-controller/problem"
)
type Adapter struct {
	ec2metadata    *ec2metadata.EC2Metadata
	ec2            ec2iface.EC2API
	elbv2          elbv2iface.ELBV2API
	autoscaling    autoscalingiface.AutoScalingAPI
	acm            acmiface.ACMAPI
	iam            iamiface.IAMAPI
	cloudformation cloudformationiface.CloudFormationAPI
	manifest                    *manifest
	healthCheckPath             string
	healthCheckPort             uint
	healthCheckInterval         time.Duration
	healthCheckTimeout          time.Duration
	targetPort                  uint
	albHTTPTargetPort           uint
	nlbHTTPTargetPort           uint
	targetHTTPS                 bool
	creationTimeout             time.Duration
	idleConnectionTimeout       time.Duration
	deregistrationDelayTimeout  time.Duration
	TargetedAutoScalingGroups   map[string]*autoScalingGroupDetails
	OwnedAutoScalingGroups      map[string]*autoScalingGroupDetails
	ec2Details                  map[string]*instanceDetails
	singleInstances             map[string]*instanceDetails
	obsoleteInstances           []string
	stackTerminationProtection  bool
	stackTags                   map[string]string
	controllerID                string
	sslPolicy                   string
	ipAddressType               string
	albLogsS3Bucket             string
	albLogsS3Prefix             string
	httpRedirectToHTTPS         bool
	nlbCrossZone                bool
	nlbHTTPEnabled              bool
	customFilter                string
	internalDomains             []string
	denyInternalDomains         bool
	denyInternalRespBody        string
	denyInternalRespContentType string
	denyInternalRespStatusCode  int
}
type manifest struct {
	securityGroup *securityGroupDetails
	instance      *instanceDetails
	subnets       []*subnetDetails
	filters       []*ec2.Filter
	asgFilters    map[string][]string
	clusterID     string
	vpcID         string
}
type configProviderFunc func() client.ConfigProvider
const (
	DefaultHealthCheckPath           = "/kube-system/healthz"
	DefaultHealthCheckPort           = 9999
	DefaultTargetPort                = 9999
	DefaultHealthCheckInterval       = 10 * time.Second
	DefaultHealthCheckTimeout        = 5 * time.Second
	DefaultCertificateUpdateInterval = 30 * time.Minute
	DefaultCreationTimeout           = 5 * time.Minute
	DefaultIdleConnectionTimeout     = 1 * time.Minute
	DefaultDeregistrationTimeout     = 5 * time.Minute
	DefaultControllerID              = "kube-ingress-aws-controller"
	DefaultMaxCertsPerALB = 24
	DefaultSslPolicy = "ELBSecurityPolicy-2016-08"
	DefaultIpAddressType = "ipv4"
	DefaultAlbS3LogsBucket = ""
	DefaultAlbS3LogsPrefix = ""
	DefaultCustomFilter = ""
	DefaultNLBCrossZone   = false
	DefaultNLBHTTPEnabled = false
	nameTag                     = "Name"
	LoadBalancerTypeApplication = "application"
	LoadBalancerTypeNetwork     = "network"
	IPAddressTypeIPV4           = "ipv4"
	IPAddressTypeDualstack      = "dualstack"
)
var (
	ErrLoadBalancerStackNotFound = errors.New("load balancer stack not found")
	ErrLoadBalancerStackNotReady = errors.New("existing load balancer stack not ready")
	ErrMissingNameTag = errors.New("Name tag not found")
	ErrMissingTag = errors.New("missing tag")
	ErrNoSubnets = errors.New("unable to find VPC subnets")
	ErrMissingAutoScalingGroupTag = errors.New(`instance is missing the "` + autoScalingGroupNameTag + `" tag`)
	ErrNoRunningInstances = errors.New("no reservations or instances in the running state")
	SSLPolicies = map[string]bool{
		"ELBSecurityPolicy-2016-08":             true,
		"ELBSecurityPolicy-FS-2018-06":          true,
		"ELBSecurityPolicy-TLS-1-2-2017-01":     true,
		"ELBSecurityPolicy-TLS-1-2-Ext-2018-06": true,
		"ELBSecurityPolicy-TLS-1-1-2017-01":     true,
		"ELBSecurityPolicy-2015-05":             true,
		"ELBSecurityPolicy-TLS-1-0-2015-04":     true,
		"ELBSecurityPolicy-FS-1-1-2019-08":      true,
		"ELBSecurityPolicy-FS-1-2-2019-08":      true,
		"ELBSecurityPolicy-FS-1-2-Res-2019-08":  true,
		"ELBSecurityPolicy-FS-1-2-Res-2020-10":  true,
	}
	SSLPoliciesList = []string{
		"ELBSecurityPolicy-2016-08",
		"ELBSecurityPolicy-FS-2018-06",
		"ELBSecurityPolicy-TLS-1-2-2017-01",
		"ELBSecurityPolicy-TLS-1-2-Ext-2018-06",
		"ELBSecurityPolicy-TLS-1-1-2017-01",
		"ELBSecurityPolicy-2015-05",
		"ELBSecurityPolicy-TLS-1-0-2015-04",
		"ELBSecurityPolicy-FS-1-1-2019-08",
		"ELBSecurityPolicy-FS-1-2-2019-08",
		"ELBSecurityPolicy-FS-1-2-Res-2019-08",
		"ELBSecurityPolicy-FS-1-2-Res-2020-10",
	}
)
func newConfigProvider(debug, disableInstrumentedHttpClient bool) client.ConfigProvider {
	cfg := aws.NewConfig().WithMaxRetries(3)
	if debug {
		cfg = cfg.WithLogLevel(aws.LogDebugWithRequestErrors)
	}
	if !disableInstrumentedHttpClient {
		cfg = cfg.WithHTTPClient(instrumented_http.NewClient(cfg.HTTPClient, nil))
	}
	opts := session.Options{
		SharedConfigState: session.SharedConfigEnable,
		Config:            *cfg,
	}
	return session.Must(session.NewSessionWithOptions(opts))
}
func NewAdapter(clusterID, newControllerID, vpcID string, debug, disableInstrumentedHttpClient bool) (adapter *Adapter, err error) {
	p := newConfigProvider(debug, disableInstrumentedHttpClient)
	adapter = &Adapter{
		ec2:                 ec2.New(p),
		elbv2:               elbv2.New(p),
		ec2metadata:         ec2metadata.New(p),
		autoscaling:         autoscaling.New(p),
		acm:                 acm.New(p),
		iam:                 iam.New(p),
		cloudformation:      cloudformation.New(p),
		healthCheckPath:     DefaultHealthCheckPath,
		healthCheckPort:     DefaultHealthCheckPort,
		targetPort:          DefaultTargetPort,
		healthCheckInterval: DefaultHealthCheckInterval,
		healthCheckTimeout:  DefaultHealthCheckTimeout,
		creationTimeout:     DefaultCreationTimeout,
		ec2Details:          make(map[string]*instanceDetails),
		singleInstances:     make(map[string]*instanceDetails),
		obsoleteInstances:   make([]string, 0),
		controllerID:        newControllerID,
		sslPolicy:           DefaultSslPolicy,
		ipAddressType:       DefaultIpAddressType,
		albLogsS3Bucket:     DefaultAlbS3LogsBucket,
		albLogsS3Prefix:     DefaultAlbS3LogsPrefix,
		nlbCrossZone:        DefaultNLBCrossZone,
		nlbHTTPEnabled:      DefaultNLBHTTPEnabled,
		customFilter:        DefaultCustomFilter,
	}
	adapter.manifest, err = buildManifest(adapter, clusterID, vpcID)
	if err != nil {
		return nil, err
	}
	return
}
func (a *Adapter) NewACMCertificateProvider() certs.CertificatesProvider {
	return newACMCertProvider(a.acm)
}
func (a *Adapter) NewIAMCertificateProvider() certs.CertificatesProvider {
	return newIAMCertProvider(a.iam)
}
func (a *Adapter) WithHealthCheckPath(path string) *Adapter {
	a.healthCheckPath = path
	return a
}
func (a *Adapter) WithHealthCheckPort(port uint) *Adapter {
	a.healthCheckPort = port
	return a
}
func (a *Adapter) WithTargetPort(port uint) *Adapter {
	a.targetPort = port
	return a
}
func (a *Adapter) WithALBHTTPTargetPort(port uint) *Adapter {
	a.albHTTPTargetPort = port
	return a
}
func (a *Adapter) WithNLBHTTPTargetPort(port uint) *Adapter {
	a.nlbHTTPTargetPort = port
	return a
}
func (a *Adapter) WithTargetHTTPS(https bool) *Adapter {
	a.targetHTTPS = https
	return a
}
func (a *Adapter) WithHealthCheckInterval(interval time.Duration) *Adapter {
	a.healthCheckInterval = interval
	return a
}
func (a *Adapter) WithHealthCheckTimeout(timeout time.Duration) *Adapter {
	a.healthCheckTimeout = timeout
	return a
}
func (a *Adapter) WithCreationTimeout(interval time.Duration) *Adapter {
	a.creationTimeout = interval
	return a
}
func (a *Adapter) WithIdleConnectionTimeout(interval time.Duration) *Adapter {
	if 1*time.Second <= interval && interval <= 4000*time.Second {
		a.idleConnectionTimeout = interval
	}
	return a
}
func (a *Adapter) WithDeregistrationDelayTimeout(interval time.Duration) *Adapter {
	if 1*time.Second <= interval && interval <= 3600*time.Second {
		a.deregistrationDelayTimeout = interval
	}
	return a
}
func (a *Adapter) WithControllerID(id string) *Adapter {
	a.controllerID = id
	return a
}
func (a *Adapter) WithSslPolicy(policy string) *Adapter {
	a.sslPolicy = policy
	return a
}
func (a *Adapter) WithStackTerminationProtection(terminationProtection bool) *Adapter {
	a.stackTerminationProtection = terminationProtection
	return a
}
func (a *Adapter) WithStackTags(tags map[string]string) *Adapter {
	a.stackTags = tags
	return a
}
func (a *Adapter) WithIpAddressType(ipAddressType string) *Adapter {
	if ipAddressType == IPAddressTypeDualstack {
		a.ipAddressType = ipAddressType
	}
	return a
}
func (a *Adapter) WithAlbLogsS3Bucket(bucket string) *Adapter {
	a.albLogsS3Bucket = bucket
	return a
}
func (a *Adapter) WithAlbLogsS3Prefix(prefix string) *Adapter {
	a.albLogsS3Prefix = prefix
	return a
}
func (a *Adapter) WithHTTPRedirectToHTTPS(httpRedirectToHTTPS bool) *Adapter {
	a.httpRedirectToHTTPS = httpRedirectToHTTPS
	return a
}
func (a *Adapter) WithNLBCrossZone(nlbCrossZone bool) *Adapter {
	a.nlbCrossZone = nlbCrossZone
	return a
}
func (a *Adapter) WithNLBHTTPEnabled(nlbHTTPEnabled bool) *Adapter {
	a.nlbHTTPEnabled = nlbHTTPEnabled
	return a
}
func (a *Adapter) WithCustomFilter(customFilter string) *Adapter {
	a.customFilter = customFilter
	a.manifest.filters = a.parseFilters(a.ClusterID())
	a.manifest.asgFilters = a.parseAutoscaleFilterTags(a.ClusterID())
	return a
}
func (a *Adapter) WithInternalDomains(domains []string) *Adapter {
	a.internalDomains = domains
	return a
}
func (a *Adapter) WithDenyInternalDomains(deny bool) *Adapter {
	a.denyInternalDomains = deny
	return a
}
func (a *Adapter) WithInternalDomainsDenyResponse(body string) *Adapter {
	a.denyInternalRespBody = body
	return a
}
func (a *Adapter) WithInternalDomainsDenyResponseStatusCode(code int) *Adapter {
	a.denyInternalRespStatusCode = code
	return a
}
func (a *Adapter) WithInternalDomainsDenyResponseContenType(contentType string) *Adapter {
	a.denyInternalRespContentType = contentType
	return a
}
func (a *Adapter) ClusterID() string {
	return a.manifest.clusterID
}
func (a *Adapter) VpcID() string {
	return a.manifest.vpcID
}
func (a *Adapter) InstanceID() string {
	if a.manifest != nil && a.manifest.instance != nil {
		return a.manifest.instance.id
	}
	return "<none>"
}