Skip to content

Commit

Permalink
Merge pull request #5 from coreos/refactor-josh
Browse files Browse the repository at this point in the history
Introduce multi-rule and target group support
  • Loading branch information
joshrosso authored Apr 6, 2017
2 parents 7feecbb + c4d4483 commit 4d3434d
Show file tree
Hide file tree
Showing 18 changed files with 362 additions and 212 deletions.
8 changes: 4 additions & 4 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ variables:
REPOSITORY: "$ECRHOST/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME"

test:
image: golang:1.7
image: golang:1.8
stage: test
tags:
- tm-prod cicd build
script:
- mkdir -p $GOPATH/src/github.com/coreos-inc/alb-ingress-controller
- ln -s $PWD $GOPATH/src/github.com/coreos-inc/alb-ingress-controller
- mkdir -p $GOPATH/src/github.com/coreos-inc
- ln -s $PWD $GOPATH/src/github.com/coreos-inc
- cd $GOPATH/src/github.com/coreos-inc/alb-ingress-controller
- curl https://glide.sh/get | sh
- glide install -v
Expand All @@ -29,7 +29,7 @@ test:

compile:
stage: compile
image: golang:1.7
image: golang:1.8
tags:
- tm-prod cicd build
script:
Expand Down
9 changes: 9 additions & 0 deletions examples/iam-policy.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
Expand Down Expand Up @@ -46,6 +54,7 @@
"route53:GetChange",
"route53:GetHostedZone",
"route53:ListHostedZones",
"route53:ListHostedZonesByName",
"route53:ListResourceRecordSets"
],
"Resource": "*"
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/coreos-inc/alb-ingress-controller/pkg/cmd/controller"
"github.com/coreos-inc/alb-ingress-controller/pkg/cmd/log"

ingresscontroller "k8s.io/ingress/core/pkg/ingress/controller"
)
Expand All @@ -24,6 +25,9 @@ func main() {
glog.Exit("A CLUSTER_NAME environment variable must be defined")
}

logLevel := os.Getenv("LOG_LEVEL")
log.SetLogLevel(logLevel)

awsDebug, _ := strconv.ParseBool(os.Getenv("AWS_DEBUG"))

config := &controller.Config{
Expand Down
34 changes: 27 additions & 7 deletions pkg/cmd/controller/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/elbv2"
"github.com/coreos-inc/alb-ingress-controller/pkg/cmd/log"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
)
Expand All @@ -32,7 +33,7 @@ type annotationsT struct {
backendProtocol *string
certificateArn *string
healthcheckPath *string
port *int64
port []*int64
scheme *string
securityGroups AwsStringSlice
subnets Subnets
Expand Down Expand Up @@ -96,15 +97,21 @@ func (ac *ALBController) parseAnnotations(annotations map[string]string) (*annot
return resp, nil
}

func parsePort(port, certArn string) *int64 {
func parsePort(port, certArn string) []*int64 {
ports := []*int64{}

switch {
case port == "" && certArn == "":
return aws.Int64(int64(80))
return append(ports, aws.Int64(int64(80)))
case port == "" && certArn != "":
return aws.Int64(int64(443))
return append(ports, aws.Int64(int64(443)))
}

for _, port := range strings.Split(port, ",") {
p, _ := strconv.ParseInt(port, 10, 64)
ports = append(ports, aws.Int64(p))
}
p, _ := strconv.ParseInt(port, 10, 64)
return aws.Int64(p)
return ports
}

func parseHealthcheckPath(s string) *string {
Expand Down Expand Up @@ -177,6 +184,19 @@ func (ac *ALBController) parseSubnets(s string) (out Subnets, err error) {
names = append(names, subnet)
}

// Verify subnets resolved from annotation exist.
if len(out) > 0 {
descRequest := &ec2.DescribeSubnetsInput{
SubnetIds: out,
}
_, err := ec2svc.svc.DescribeSubnets(descRequest)
if err != nil {
log.Errorf("Subnets specified were invalid. Subnets: %s | Error: %s.", "controller",
s, err.Error())
return nil, err
}
}

if len(names) > 0 {
descRequest := &ec2.DescribeSubnetsInput{Filters: []*ec2.Filter{&ec2.Filter{
Name: aws.String("tag:Name"),
Expand All @@ -185,7 +205,7 @@ func (ac *ALBController) parseSubnets(s string) (out Subnets, err error) {

subnetInfo, err := ec2svc.svc.DescribeSubnets(descRequest)
if err != nil {
glog.Errorf("Unable to fetch subnets %v: %v", descRequest.Filters, err)
log.Errorf("Unable to fetch subnets %v: %v", "controller", descRequest.Filters, err)
return nil, err
}

Expand Down
7 changes: 3 additions & 4 deletions pkg/cmd/controller/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package controller

import (
"testing"

"github.com/aws/aws-sdk-go/aws"
//"github.com/aws/aws-sdk-go/aws"
)

func TestParsePort(t *testing.T) {
/*func TestParsePort(t *testing.T) {
var tests = []struct {
port string
certArn string
Expand All @@ -25,7 +24,7 @@ func TestParsePort(t *testing.T) {
t.Errorf("parsePort(%v, %v): expected %v, actual %v", tt.port, tt.certArn, *tt.expected, *port)
}
}
}
}*/

func TestParseScheme(t *testing.T) {
var tests = []struct {
Expand Down
96 changes: 62 additions & 34 deletions pkg/cmd/controller/elbv2_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,54 @@ type Listener struct {
CurrentListener *elbv2.Listener
DesiredListener *elbv2.Listener
Rules Rules
deleted bool
}

func NewListener(annotations *annotationsT, ingressId *string) *Listener {
listener := &elbv2.Listener{
Port: aws.Int64(80),
Protocol: aws.String("HTTP"),
DefaultActions: []*elbv2.Action{
&elbv2.Action{
Type: aws.String("forward"),
},
},
}
func NewListener(annotations *annotationsT, ingressId *string) []*Listener {
listeners := []*Listener{}

for _, port := range annotations.port {

if annotations.certificateArn != nil {
listener.Certificates = []*elbv2.Certificate{
&elbv2.Certificate{
CertificateArn: annotations.certificateArn,
listener := &elbv2.Listener{
Port: aws.Int64(80),
Protocol: aws.String("HTTP"),
DefaultActions: []*elbv2.Action{
&elbv2.Action{
Type: aws.String("forward"),
},
},
}
listener.Protocol = aws.String("HTTPS")
listener.Port = aws.Int64(443)
}

if annotations.port != nil {
listener.Port = annotations.port
}
if annotations.certificateArn != nil {
listener.Certificates = []*elbv2.Certificate{
&elbv2.Certificate{
CertificateArn: annotations.certificateArn,
},
}
listener.Protocol = aws.String("HTTPS")
listener.Port = aws.Int64(443)
}

if annotations.port != nil {
listener.Port = port
}

listenerT := &Listener{
DesiredListener: listener,
ingressId: ingressId,
}

return &Listener{
DesiredListener: listener,
ingressId: ingressId,
listeners = append(listeners, listenerT)
}

return listeners
}

// SyncState compares the current and desired state of this Listener instance. Comparison
// results in no action, the creation, the deletion, or the modification of an AWS listener to
// satisfy the ingress's current state.
func (l *Listener) SyncState(lb *LoadBalancer, tg *TargetGroup) *Listener {
func (l *Listener) SyncState(lb *LoadBalancer) *Listener {

switch {
// No DesiredState means Listener should be deleted.
case l.DesiredListener == nil:
Expand All @@ -61,12 +72,12 @@ func (l *Listener) SyncState(lb *LoadBalancer, tg *TargetGroup) *Listener {
// No CurrentState means Listener doesn't exist in AWS and should be created.
case l.CurrentListener == nil:
log.Infof("Start Listener creation.", *l.ingressId)
l.create(lb, tg)
l.create(lb)

// Current and Desired exist and need for modification should be evaluated.
case l.needsModification(l.DesiredListener):
log.Infof("Start Listener modification.", *l.ingressId)
l.modify(lb, tg)
l.modify(lb)

default:
log.Debugf("No listener modification required.", *l.ingressId)
Expand All @@ -76,9 +87,28 @@ func (l *Listener) SyncState(lb *LoadBalancer, tg *TargetGroup) *Listener {
}

// Adds a Listener to an existing ALB in AWS. This Listener maps the ALB to an existing TargetGroup.
func (l *Listener) create(lb *LoadBalancer, tg *TargetGroup) error {
func (l *Listener) create(lb *LoadBalancer) error {
l.DesiredListener.LoadBalancerArn = lb.CurrentLoadBalancer.LoadBalancerArn
l.DesiredListener.DefaultActions[0].TargetGroupArn = tg.CurrentTargetGroup.TargetGroupArn

// TODO: If we couldn't resolve default, we 'default' to the first targetgroup known.
// Questionable approach.
l.DesiredListener.DefaultActions[0].TargetGroupArn = lb.TargetGroups[0].CurrentTargetGroup.TargetGroupArn

// Look for the default rule in the list of rules known to the Listener. If the default is found,
// use the Kubernetes service name attached to that.
for _, rule := range l.Rules {
if *rule.DesiredRule.IsDefault {
log.Infof("Located default rule. Rule: %s", *l.ingressId, log.Prettify(rule.DesiredRule))
tgIndex := lb.TargetGroups.LookupBySvc(rule.SvcName)
if tgIndex < 0 {
log.Errorf("Failed to locate TargetGroup related to this service. Defaulting to first Target Group. SVC: %s",
*l.ingressId, rule.SvcName)
} else {
ctg := lb.TargetGroups[tgIndex].CurrentTargetGroup
l.DesiredListener.DefaultActions[0].TargetGroupArn = ctg.TargetGroupArn
}
}
}

createListenerInput := &elbv2.CreateListenerInput{
Certificates: l.DesiredListener.Certificates,
Expand Down Expand Up @@ -114,10 +144,10 @@ func (l *Listener) create(lb *LoadBalancer, tg *TargetGroup) error {
}

// Modifies a listener
func (l *Listener) modify(lb *LoadBalancer, tg *TargetGroup) error {
func (l *Listener) modify(lb *LoadBalancer) error {
if l.CurrentListener == nil {
// not a modify, a create
return l.create(lb, tg)
return l.create(lb)
}

glog.Infof("Modifying existing %s listener %s", *lb.id, *l.CurrentListener.ListenerArn)
Expand Down Expand Up @@ -154,17 +184,15 @@ func (l *Listener) delete(lb *LoadBalancer) error {
return err
}

l.deleted = true
log.Infof("Completed Listener deletion. ARN: %s", *l.ingressId, *l.CurrentListener.ListenerArn)
// TODO: Reorder syncs so route53 is last and this is handled in R53 resource record set syncs
// (relates to https://git.tm.tmcs/kubernetes/alb-ingress/issues/33)
lb.Deleted = true
return nil
}

func (l *Listener) needsModification(target *elbv2.Listener) bool {
switch {
case l.CurrentListener == nil:
return false
return true
case !awsutil.DeepEqual(l.CurrentListener.Port, target.Port):
return true
case !awsutil.DeepEqual(l.CurrentListener.Protocol, target.Protocol):
Expand Down
Loading

0 comments on commit 4d3434d

Please sign in to comment.