Skip to content

Commit

Permalink
Use Default Backend pool and HTTP settings when Service not present (#59
Browse files Browse the repository at this point in the history
)
  • Loading branch information
akshaysngupta authored Oct 30, 2018
1 parent cda03a2 commit 774406b
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 91 deletions.
10 changes: 4 additions & 6 deletions cmd/appgw-ingress/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,18 @@ import (
"time"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/appgw"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/controller"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/k8scontext"

"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure/auth"

"github.com/golang/glog"
flag "github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/appgw"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/controller"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/k8scontext"
)

var (
Expand Down
32 changes: 16 additions & 16 deletions docs/build.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
## Building

This is a CMake-based project. Build targets include:

- `ALL_BUILD` (default target) builds `appgw-ingress` and `dockerize` target
- `devenv` builds a docker image with configured development environment
- `vendor` installs dependency using `glide` in a docker container with image from `devenv` target
- `appgw-ingress` builds the binary for this controller in a docker container with image from `devenv` target
- `dockerize` builds a docker image with the binary from `appgw-ingress` target
- `dockerpush` pushes the docker image to a container registry with prefix defined in CMake variable `<deployment_push_prefix>`

To run the CMake targets:

1. `mkdir build && cd build` creates and enters a build directory
2. `cmake ..` generates project configuration in the build directory
3. `cmake --build .` to build the default target,
## Building

This is a CMake-based project. Build targets include:

- `ALL_BUILD` (default target) builds `appgw-ingress` and `dockerize` target
- `devenv` builds a docker image with configured development environment
- `vendor` installs dependency using `glide` in a docker container with image from `devenv` target
- `appgw-ingress` builds the binary for this controller in a docker container with image from `devenv` target
- `dockerize` builds a docker image with the binary from `appgw-ingress` target
- `dockerpush` pushes the docker image to a container registry with prefix defined in CMake variable `<deployment_push_prefix>`

To run the CMake targets:

1. `mkdir build && cd build` creates and enters a build directory
2. `cmake ..` generates project configuration in the build directory
3. `cmake --build .` to build the default target,
or `cmake --build . --target <target_name>` to specify a target to run from above
114 changes: 110 additions & 4 deletions pkg/appgw/appgw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"
"time"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/k8scontext"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"github.com/Azure/go-autorest/autorest/to"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/api/core/v1"
Expand All @@ -13,10 +16,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
testclient "k8s.io/client-go/kubernetes/fake"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/k8scontext"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"github.com/Azure/go-autorest/autorest/to"
)

var _ = Describe("Tests `appgw.ConfigBuilder`", func() {
Expand Down Expand Up @@ -290,4 +289,111 @@ var _ = Describe("Tests `appgw.ConfigBuilder`", func() {
Expect((*pathRule.Paths)[0]).To(Equal("/hi"))
})
})

Context("Tests Ingress Controller when Service doesn't exists", func() {
It("Should be able to create Application Gateway Configuration from Ingress with empty backend pool.", func() {
// Delete the service
options := &metav1.DeleteOptions{}
err := k8sClient.CoreV1().Services(ingressNS).Delete(serviceName, options)
Expect(err).Should(BeNil(), "Unable to delete service resource due to: %v", err)

// Delete the Endpoint
err = k8sClient.CoreV1().Endpoints(ingressNS).Delete(serviceName, options)
Expect(err).Should(BeNil(), "Unable to delete endpoint resource due to: %v", err)

// Start the informers. This will sync the cache with the latest ingress.
ctxt.Run()

// Get all the ingresses
ingressList := ctxt.GetHTTPIngressList()
// There should be only one ingress
Expect(len(ingressList)).To(Equal(1), "Expected only one ingress resource but got: %d", len(ingressList))
// Make sure it is the ingress we stored.
Expect(ingressList[0]).To(Equal(ingress))

// Add HTTP settings.
configBuilder, err := configBuilder.BackendHTTPSettingsCollection(ingressList)
Expect(err).Should(BeNil(), "Error in generating the HTTP Settings: %v", err)

// Retrieve the implementation of the `ConfigBuilder` interface.
appGW := configBuilder.Build()
// We will have a default HTTP setting that gets added, and an HTTP setting corresponding to port `backendPort`
Expect(len(*appGW.BackendHTTPSettingsCollection)).To(Equal(2), "Expected two HTTP setting, but got: %d", len(*appGW.BackendHTTPSettingsCollection))

expectedBackend := &ingress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].Backend
httpSettingsName := generateHTTPSettingsName(generateBackendID(ingress, expectedBackend).serviceFullName(), fmt.Sprintf("%d", servicePort), servicePort)
httpSettings := &network.ApplicationGatewayBackendHTTPSettings{
Etag: to.StringPtr("*"),
Name: &httpSettingsName,
ApplicationGatewayBackendHTTPSettingsPropertiesFormat: &network.ApplicationGatewayBackendHTTPSettingsPropertiesFormat{
Protocol: network.HTTP,
Port: &servicePort,
},
}

// Test the default backend HTTP settings.
Expect((*appGW.BackendHTTPSettingsCollection)[0]).To(Equal(defaultBackendHTTPSettings()))
// Test the ingress backend HTTP setting that we installed.
Expect((*appGW.BackendHTTPSettingsCollection)[1]).To(Equal(*httpSettings))

// Add backend address pools. We need the HTTP settings before we can add the backend address pools.
configBuilder, err = configBuilder.BackendAddressPools(ingressList)
Expect(err).Should(BeNil(), "Error in generating the backend address pools: %v", err)

// Retrieve the implementation of the `ConfigBuilder` interface.
appGW = configBuilder.Build()
// We will have a default backend address pool that gets added, and a backend pool corresponding to our service.
Expect(len(*appGW.BackendAddressPools)).To(Equal(1), "Expected two backend address pools, but got: %d", len(*appGW.BackendAddressPools))

// Test the default backend address pool.
Expect((*appGW.BackendAddressPools)[0]).To(Equal(defaultBackendAddressPool()))

// Add the listeners. We need the backend address pools before we can add HTTP listeners.
configBuilder, err = configBuilder.HTTPListeners(ingressList)
Expect(err).Should(BeNil(), "Error in generating the HTTP listeners: %v", err)

// Retrieve the implementation of the `ConfigBuilder` interface.
appGW = configBuilder.Build()
// Ingress allows listners on port 80 or port 443. Therefore in this particular case we would have only a single listener
Expect(len(*appGW.HTTPListeners)).To(Equal(1), "Expected a single HTTP listener, but got: %d", len(*appGW.HTTPListeners))

// Test the listener.
appGwIdentifier := Identifier{}
frontendPortID := appGwIdentifier.frontendPortID(generateFrontendPortName(80))
httpListenerName := generateHTTPListenerName(frontendListenerIdentifier{80, domainName})
httpListener := &network.ApplicationGatewayHTTPListener{
Etag: to.StringPtr("*"),
Name: &httpListenerName,
ApplicationGatewayHTTPListenerPropertiesFormat: &network.ApplicationGatewayHTTPListenerPropertiesFormat{
FrontendIPConfiguration: resourceRef("*"),
FrontendPort: resourceRef(frontendPortID),
Protocol: network.HTTP,
HostName: &domainName,
},
}

Expect((*appGW.HTTPListeners)[0]).To(Equal(*httpListener))

// RequestRoutingRules depends on the previous operations
configBuilder, err = configBuilder.RequestRoutingRules(ingressList)
Expect(err).Should(BeNil(), "Error in generating the routing rules: %v", err)

// Retrieve the implementation of the `ConfigBuilder` interface.
appGW = configBuilder.Build()
Expect(len(*appGW.RequestRoutingRules)).To(Equal(1), "Expected one routing rule, but got: %d", len(*appGW.RequestRoutingRules))
Expect(*((*appGW.RequestRoutingRules)[0].Name)).To(Equal(generateRequestRoutingRuleName(frontendListenerIdentifier{80, domainName})))
Expect((*appGW.RequestRoutingRules)[0].RuleType).To(Equal(network.PathBasedRouting))

// Check the `urlPathMaps`
Expect(len(*appGW.URLPathMaps)).To(Equal(1), "Expected one URL path map routing, but got: %d", len(*appGW.URLPathMaps))
Expect(*((*appGW.URLPathMaps)[0].Name)).To(Equal(generateURLPathMapName(frontendListenerIdentifier{80, domainName})))
// Check the `pathRule` stored within the `urlPathMap`.
Expect(len(*((*appGW.URLPathMaps)[0].PathRules))).To(Equal(1), "Expected one path based rule, but got: %d", len(*((*appGW.URLPathMaps)[0].PathRules)))

pathRule := (*((*appGW.URLPathMaps)[0].PathRules))[0]
Expect(len(*(pathRule.Paths))).To(Equal(1), "Expected a single path in path-based rules, but got: %d", len(*(pathRule.Paths)))
// Check the exact path that was set.
Expect((*pathRule.Paths)[0]).To(Equal("/hi"))
})
})
})
12 changes: 6 additions & 6 deletions pkg/appgw/backendaddresspools.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@
package appgw

import (
"errors"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"github.com/Azure/go-autorest/autorest/to"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils"
)

func (builder *appGwConfigBuilder) BackendAddressPools(ingressList [](*v1beta1.Ingress)) (ConfigBuilder, error) {
addressPools := make([](network.ApplicationGatewayBackendAddressPool), 0)
addressPools = append(addressPools, defaultBackendAddressPool())
emptyPool := defaultBackendAddressPool()
addressPools = append(addressPools, emptyPool)

for backendID, serviceBackendPair := range builder.serviceBackendPairMap {
endpoints := builder.k8sContext.GetEndpointsByService(backendID.serviceKey())
if endpoints == nil {
glog.Warningf("unable to get endpoints for service key [%s]", backendID.serviceKey())
return builder, errors.New("unable to get endpoints for service")
builder.backendPoolMap[backendID] = &emptyPool
continue
}

for _, subset := range endpoints.Subsets {
endpointsPortsSet := utils.NewUnorderedSet()
for _, endpointsPort := range subset.Ports {
Expand Down
78 changes: 39 additions & 39 deletions pkg/appgw/backendhttpsettings.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ import (
"errors"
"fmt"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"github.com/Azure/go-autorest/autorest/to"
"github.com/golang/glog"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils"

"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/util/intstr"
Expand Down Expand Up @@ -44,56 +42,58 @@ func (builder *appGwConfigBuilder) BackendHTTPSettingsCollection(ingressList [](
unresolvedBackendID := make([]backendIdentifier, 0)
backendIDs.ForEach(func(backendIDInterface interface{}) {
backendID := backendIDInterface.(backendIdentifier)
resolvedBackendPorts := utils.NewUnorderedSet()

service := builder.k8sContext.GetService(backendID.serviceKey())
if service == nil {
glog.V(1).Infof("unable to get the service [%s]", backendID.serviceKey())
unresolvedBackendID = append(unresolvedBackendID, backendID)
return
}
resolvedBackendPorts.Insert(serviceBackendPortPair{
ServicePort: backendID.ServicePort.IntVal,
BackendPort: backendID.ServicePort.IntVal,
})
} else {
for _, sp := range service.Spec.Ports {
// find the backend port number
// check if any service ports matches the specified ports
if sp.Protocol != v1.ProtocolTCP {
// ignore UDP ports
continue
}
if fmt.Sprint(sp.Port) == backendID.ServicePort.String() ||
sp.Name == backendID.ServicePort.String() ||
sp.TargetPort.String() == backendID.ServicePort.String() {
// matched a service port with a port from the service

// find the backend port number
resolvedBackendPorts := utils.NewUnorderedSet()
for _, sp := range service.Spec.Ports {
// check if any service ports matches the specified ports
if sp.Protocol != v1.ProtocolTCP {
// ignore UDP ports
continue
}
if fmt.Sprint(sp.Port) == backendID.ServicePort.String() ||
sp.Name == backendID.ServicePort.String() ||
sp.TargetPort.String() == backendID.ServicePort.String() {
// matched a service port with a port from the service

if sp.TargetPort.String() == "" {
// targetPort is not defined, by default targetPort == port
resolvedBackendPorts.Insert(serviceBackendPortPair{
ServicePort: sp.Port,
BackendPort: sp.Port,
})
} else {
// target port is defined as name or port number
if sp.TargetPort.Type == intstr.Int {
// port is defined as port number
if sp.TargetPort.String() == "" {
// targetPort is not defined, by default targetPort == port
resolvedBackendPorts.Insert(serviceBackendPortPair{
ServicePort: sp.Port,
BackendPort: sp.TargetPort.IntVal,
BackendPort: sp.Port,
})
} else {
// if service port is defined by name, need to resolve
targetPortName := sp.TargetPort.StrVal
glog.V(1).Infof("resolving port name %s", targetPortName)
targetPortsResolved := builder.resolvePortName(targetPortName, &backendID)
targetPortsResolved.ForEach(func(targetPortInterface interface{}) {
targetPort := targetPortInterface.(int32)
// target port is defined as name or port number
if sp.TargetPort.Type == intstr.Int {
// port is defined as port number
resolvedBackendPorts.Insert(serviceBackendPortPair{
ServicePort: sp.Port,
BackendPort: targetPort,
BackendPort: sp.TargetPort.IntVal,
})
})
} else {
// if service port is defined by name, need to resolve
targetPortName := sp.TargetPort.StrVal
glog.V(1).Infof("resolving port name %s", targetPortName)
targetPortsResolved := builder.resolvePortName(targetPortName, &backendID)
targetPortsResolved.ForEach(func(targetPortInterface interface{}) {
targetPort := targetPortInterface.(int32)
resolvedBackendPorts.Insert(serviceBackendPortPair{
ServicePort: sp.Port,
BackendPort: targetPort,
})
})
}
}
break
}
break
}
}

Expand Down
3 changes: 1 addition & 2 deletions pkg/appgw/configbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ package appgw

import (
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"k8s.io/api/extensions/v1beta1"

"github.com/Azure/application-gateway-kubernetes-ingress/pkg/k8scontext"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils"

"k8s.io/api/extensions/v1beta1"
)

// ConfigBuilder is a builder for application gateway configuration
Expand Down
1 change: 0 additions & 1 deletion pkg/appgw/identifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"os"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"

"github.com/Azure/go-autorest/autorest/to"
"github.com/golang/glog"
)
Expand Down
8 changes: 1 addition & 7 deletions pkg/appgw/internaltypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ package appgw
import (
"fmt"

"github.com/Azure/go-autorest/autorest/to"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"k8s.io/apimachinery/pkg/util/intstr"
)
Expand Down Expand Up @@ -109,11 +107,7 @@ func defaultBackendAddressPool() network.ApplicationGatewayBackendAddressPool {
return network.ApplicationGatewayBackendAddressPool{
Name: &defBackendAddressPool,
ApplicationGatewayBackendAddressPoolPropertiesFormat: &network.ApplicationGatewayBackendAddressPoolPropertiesFormat{
BackendAddresses: &[]network.ApplicationGatewayBackendAddress{
network.ApplicationGatewayBackendAddress{
Fqdn: to.StringPtr("nonexistent.placeholder"),
},
},
BackendAddresses: &[]network.ApplicationGatewayBackendAddress{},
},
}
}
Expand Down
1 change: 0 additions & 1 deletion pkg/appgw/requestroutingrules.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-06-01/network"
"github.com/Azure/go-autorest/autorest/to"

"k8s.io/api/extensions/v1beta1"
)

Expand Down
Loading

0 comments on commit 774406b

Please sign in to comment.