Skip to content

Commit

Permalink
Create bypass entries in function namespace
Browse files Browse the repository at this point in the history
* Documents installation / approach with example for nodeinfo
* Adds feature and unit tests
* Remove armhf since it can be installed via the openfaas chart
using k3sup
* Update release version to latest
* Update examples
* Remove certifier from PR template

Tested e2e with Nginx on Civo's k3s service with NginxIngress
and traefik removed.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
  • Loading branch information
alexellis committed Feb 11, 2020
1 parent 82230a5 commit 6e523ca
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 66 deletions.
1 change: 0 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
- [ ] I have run the [certifier](https://github.com/openfaas/certifier)


## Types of changes
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017-2019 OpenFaaS Authors
Copyright (c) 2017-2019 OpenFaaS Author(s)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,35 @@ If you are confident in the configuration, switch over to the production issuer,

Save the file and apply.

### Bypass mode

The IngressOperator can be used to create Ingress records that bypass the OpenFaaS Gateway. This may be useful when you are running a non-standard workload such as a brownfields monolith to reduce hops, or with an unsupported protocol like gRPC or websockets.

Example:

```yaml
apiVersion: openfaas.com/v1alpha2
kind: FunctionIngress
metadata:
name: nodeinfo
namespace: openfaas-fn
spec:
domain: "nodeinfo.myfaas.club"
function: "nodeinfo"
ingressType: "nginx"
bypassGateway: true
```

Note that since Ingress records must be created in the same namespace as the backend service, `namespace` is changed to `openfaas-fn`.

By default, the OpenFaaS helm chart can deploy the first instance of the operator, if you need gateway bypass, then deploy a second operator using a customised version of `artifacts/operator-amd64.yaml`.

When deploying the operator, you will also need to:

* Set the `ingress_namespace` env-var to `openfaas-fn`
* Edit the deployment `namespace` to `openfaas-fn`
* Optionally: edit `artifacts/operator-rbac.yaml` to `openfaas-fn` and apply

### Run or deploy the IngressOperator

#### In-cluster:
Expand Down Expand Up @@ -328,9 +357,9 @@ This project follows the [OpenFaaS contributing guide](./CONTRIBUTING.md)
## Configuration via Environment Variable
| Option | Usage |
|---------------------|-------------------------------------------------------------------------------------------------|
| `openfaas_gateway_namespace` | Namespace for the OpenFaaS gateway, default: `openfaas` |
| Option | Usage |
|---------------------|----------------------------------------------------------------------------------------------------|
| `ingress_namespace` | Namespace to create Ingress within, if bypassing gateway, set to `openfaas-fn`. default: `openfaas`|
## LICENSE
Expand Down
11 changes: 11 additions & 0 deletions artifacts/nodeinfo-bypass-ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: openfaas.com/v1alpha2
kind: FunctionIngress
metadata:
name: nodeinfo
namespace: openfaas-fn
spec:
domain: "nodeinfo.myfaas.club"
function: "nodeinfo"
ingressType: "nginx"
bypassGateway: true
path: /
10 changes: 4 additions & 6 deletions artifacts/operator-amd64.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
apiVersion: apps/v1beta2
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-operator
Expand All @@ -14,21 +14,19 @@ spec:
labels:
app: ingress-operator
annotations:
prometheus.io.scrape: 'true'
prometheus.io.scrape: 'false'
spec:
serviceAccountName: ingress-operator
containers:
- name: operator
image: openfaas/ingress-operator:0.4.1
image: openfaas/ingress-operator:0.6.0
imagePullPolicy: Always
command:
- ./ingress-operator
- -logtostderr
- -v=2
env:
- name: core_namespace
value: openfaas
- name: functions_namespace
- name: ingress_namespace
value: openfaas-fn
resources:
limits:
Expand Down
37 changes: 0 additions & 37 deletions artifacts/operator-armhf.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion artifacts/operator-rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ rules:
- apiGroups: ["openfaas.com"]
resources: ["functioningresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["extensions"]
- apiGroups: ["extensions", "networking"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/emicklei/go-restful v2.11.1+incompatible // indirect
github.com/go-openapi/spec v0.19.6 // indirect
github.com/go-openapi/swag v0.19.7 // indirect
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
github.com/google/go-cmp v0.3.0
github.com/googleapis/gnostic v0.2.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ github.com/go-openapi/swag v0.19.7 h1:VRuXN2EnMSsZdauzdss6JBC29YotDqG59BZ+tdlIL1
github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down
5 changes: 4 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ var pullPolicyOptions = map[string]bool{
func init() {
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")

// TODO: remove
flag.Bool("logtostderr", false, "logtostderr legacy flag")
}

func main() {
// TODO: remove
flag.Set("logtostderr", "true")
flag.Parse()

Expand Down Expand Up @@ -67,7 +70,7 @@ func main() {
}

ingressNamespace := "openfaas"
if namespace, exists := os.LookupEnv("openfaas_gateway_namespace"); exists {
if namespace, exists := os.LookupEnv("ingress_namespace"); exists {
ingressNamespace = namespace
}

Expand Down
41 changes: 25 additions & 16 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (

const controllerAgentName = "ingress-operator"
const faasIngressKind = "FunctionIngress"
const openfaasWorkloadPort = 8080

const (
// SuccessSynced is used as part of the Event 'reason' when a Function is synced
Expand Down Expand Up @@ -419,6 +420,10 @@ func (c *Controller) handleObject(obj interface{}) {
func makeRules(fni *faasv1.FunctionIngress) []v1beta1.IngressRule {
path := "/(.*)"

if fni.Spec.BypassGateway {
path = "/"
}

if len(fni.Spec.Path) > 0 {
path = fni.Spec.Path
}
Expand All @@ -427,6 +432,11 @@ func makeRules(fni *faasv1.FunctionIngress) []v1beta1.IngressRule {
path = strings.TrimRight(path, "(.*)")
}

serviceHost := "gateway"
if fni.Spec.BypassGateway {
serviceHost = fni.Spec.Function
}

return []v1beta1.IngressRule{
v1beta1.IngressRule{
Host: fni.Spec.Domain,
Expand All @@ -436,9 +446,9 @@ func makeRules(fni *faasv1.FunctionIngress) []v1beta1.IngressRule {
v1beta1.HTTPIngressPath{
Path: path,
Backend: v1beta1.IngressBackend{
ServiceName: "gateway",
ServiceName: serviceHost,
ServicePort: intstr.IntOrString{
IntVal: 8080,
IntVal: openfaasWorkloadPort,
},
},
},
Expand Down Expand Up @@ -496,20 +506,19 @@ func makeAnnotations(function *faasv1.FunctionIngress) map[string]string {
annotations["kubernetes.io/ingress.class"] = class
annotations["com.openfaas.spec"] = string(specJSON)

switch class {

case "nginx":
annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/function/" + function.Spec.Function + "/$1"
break
case "skipper":
annotations["zalando.org/skipper-filter"] = `setPath("/function/` + function.Spec.Function + `")`
break

case "traefik":
annotations["traefik.ingress.kubernetes.io/rewrite-target"] = "/function/" + function.Spec.Function + "/$1"
annotations["traefik.ingress.kubernetes.io/rule-type"] = `PathPrefix`

break
if !function.Spec.BypassGateway {
switch class {
case "nginx":
annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/function/" + function.Spec.Function + "/$1"
break
case "skipper":
annotations["zalando.org/skipper-filter"] = `setPath("/function/` + function.Spec.Function + `")`
break
case "traefik":
annotations["traefik.ingress.kubernetes.io/rewrite-target"] = "/function/" + function.Spec.Function + "/$1"
annotations["traefik.ingress.kubernetes.io/rule-type"] = `PathPrefix`
break
}
}

if function.Spec.UseTLS() {
Expand Down
64 changes: 64 additions & 0 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ func TestMakeAnnotations_IngressClass(t *testing.T) {
}
}

func TestMakeAnnotations_ByPassRemovesRewriteTarget(t *testing.T) {
wantIngressType := "nginx"
ingress := faasv1.FunctionIngress{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"kubernetes.io/ingress.class": "nginx",
},
},
Spec: faasv1.FunctionIngressSpec{
IngressType: wantIngressType,
Function: "nodeinfo",
BypassGateway: true,
Domain: "nodeinfo.example.com",
},
}

result := makeAnnotations(&ingress)
t.Log(result)
if val, ok := result["nginx.ingress.kubernetes.io/rewrite-target"]; ok {
t.Errorf("No rewrite annotations should be given, but got: %s", val)
}
}

func TestMakeAnnotations_IngressClassAdditionalAnnotations(t *testing.T) {
defaultRewriteAnnotation := "nginx.ingress.kubernetes.io/rewrite-target"
ingress := faasv1.FunctionIngress{
Expand Down Expand Up @@ -89,6 +112,47 @@ func Test_makeRules_Nginx_RootPath_HasRegex(t *testing.T) {
if gotPath != wantPath {
t.Errorf("want path %s, but got %s", wantPath, gotPath)
}

gotPort := rules[0].HTTP.Paths[0].Backend.ServicePort

if gotPort.IntValue() != openfaasWorkloadPort {
t.Errorf("want port %d, but got %d", openfaasWorkloadPort, gotPort.IntValue())
}
}

func Test_makeRules_Nginx_RootPath_IsRootWithBypassMode(t *testing.T) {
wantFunction := "nodeinfo"
ingress := faasv1.FunctionIngress{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{},
},
Spec: faasv1.FunctionIngressSpec{
BypassGateway: true,
IngressType: "nginx",
Function: "nodeinfo",
// Path: "/",
},
}

rules := makeRules(&ingress)

if len(rules) == 0 {
t.Errorf("Ingress should give at least one rule")
t.Fail()
}

wantPath := "/"
gotPath := rules[0].HTTP.Paths[0].Path

if gotPath != wantPath {
t.Errorf("want path %s, but got %s", wantPath, gotPath)
}

gotHost := rules[0].HTTP.Paths[0].Backend.ServiceName

if gotHost != wantFunction {
t.Errorf("want host to be function: %s, but got %s", wantFunction, gotHost)
}
}

func Test_makeRules_Nginx_PathOverride(t *testing.T) {
Expand Down

0 comments on commit 6e523ca

Please sign in to comment.