diff --git a/docs/catalog.md b/docs/catalog.md index 0b34df51b..6c1ccd43f 100644 --- a/docs/catalog.md +++ b/docs/catalog.md @@ -2,4 +2,6 @@ This is a non-comprehensive list of existing ingress controllers. +* [Dummy controller backend](/examples/custom-controller) + diff --git a/docs/dev/devel.md b/docs/dev/devel.md index d472638e7..e3d7c94c2 100644 --- a/docs/dev/devel.md +++ b/docs/dev/devel.md @@ -1,3 +1,4 @@ # Writing Ingress controllers This doc outlines the basic steps needed to write an Ingress controller. +If you want the tl;dr version, skip straight to the [example](/examples/custom-controller). diff --git a/docs/faq/README.md b/docs/faq/README.md index 53810643d..f7e4de16e 100644 --- a/docs/faq/README.md +++ b/docs/faq/README.md @@ -85,7 +85,7 @@ as well as in [this](/examples/pipeline) example. First check the [catalog](#is-there-a-catalog-of-existing-ingress-controllers), to make sure you really need to write one. -1. Write a [generic backend](https://github.com/kubernetes/ingress/blob/master/core/pkg/ingress/doc.go) +1. Write a [generic backend](/examples/custom-controller) 2. Keep it in your own repo, make sure it passes the [conformance suite](https://github.com/kubernetes/kubernetes/blob/master/test/e2e/ingress_utils.go#L112) 3. Submit an example(s) in the appropriate subdirectories [here](/examples/README.md) 4. Add it to the catalog diff --git a/examples/README.md b/examples/README.md index 2a00a24d7..01d842eb2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -69,4 +69,10 @@ Websockets | websockets loadbalancing | nginx | Intermediate HTTP/2 | HTTP/2 loadbalancing | * | Intermediate Proxy protocol | leverage the proxy protocol for source IP | nginx | Advanced +## Custom controllers + +Name | Description | Platform | Complexity Level +-----| ----------- | ---------- | ---------------- +Dummy | A simple dummy controller that logs updates | * | Advanced + diff --git a/examples/custom-controller/Dockerfile b/examples/custom-controller/Dockerfile new file mode 100644 index 000000000..197ea92f7 --- /dev/null +++ b/examples/custom-controller/Dockerfile @@ -0,0 +1,20 @@ +# Copyright 2017[<0;55;12M The Kubernetes Authors. All rights reserved. +# +# 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. + +# TODO(ingress#191): Change this to something more appropriate, like busybox +From ubuntu:15.10 +MAINTAINER Prashanth B +RUN apt-get update && apt-get install ssl-cert -y +COPY server / +ENTRYPOINT ["/server"] diff --git a/examples/custom-controller/Makefile b/examples/custom-controller/Makefile new file mode 100644 index 000000000..a66cf01f6 --- /dev/null +++ b/examples/custom-controller/Makefile @@ -0,0 +1,39 @@ +# Copyright 2017 The Kubernetes Authors All rights reserved. +# +# 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 the default backend binary or image for amd64, arm, arm64 and ppc64le +# +# Usage: +# [PREFIX=gcr.io/google_containers/dummy-ingress-controller] [ARCH=amd64] [TAG=1.1] make (server|container|push) + +all: push + +TAG=0.1 +PREFIX?=bprashanth/dummy-ingress-controller +ARCH?=amd64 +GOLANG_VERSION=1.6 +TEMP_DIR:=$(shell mktemp -d) + +server: server.go + CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) GOARM=6 godep go build -a -installsuffix cgo -ldflags '-w' -o server ./server.go + +container: server + docker build --pull -t $(PREFIX)-$(ARCH):$(TAG) . + +push: container + gcloud docker push $(PREFIX)-$(ARCH):$(TAG) + +clean: + rm -f server + diff --git a/examples/custom-controller/README.md b/examples/custom-controller/README.md new file mode 100644 index 000000000..f420ae780 --- /dev/null +++ b/examples/custom-controller/README.md @@ -0,0 +1,29 @@ +# Dummy controller + +This example contains the source code of a simple dummy controller. If you want +more details on the interface, or what the generic controller is actually doing, +please read [this doc](/docs/dev/devel.md). You can deploy the controller as +follows: + +```console +$ kubectl create -f deployment.yaml +service "default-backend" created +deployment "dummy-ingress-controller" created + +$ kubectl get po +NAME READY STATUS RESTARTS AGE +dummy-ingress-controller-3685541482-082nl 1/1 Running 0 10m + +$ kubectl logs dummy-ingress-controller-3685541482-082nl +I0131 02:29:02.462123 1 launch.go:92] &{dummy 0.0.0 git-00000000 git://foo.bar.com} +I0131 02:29:02.462513 1 launch.go:221] Creating API server client for https://10.0.0.1:443 +I0131 02:29:02.494571 1 launch.go:111] validated default/default-backend as the default backend +I0131 02:29:02.503180 1 controller.go:1038] starting Ingress controller +I0131 02:29:02.513528 1 leaderelection.go:247] lock is held by dummy-ingress-controller-3685541482-50jh0 and has not yet expired +W0131 02:29:03.510699 1 queue.go:87] requeuing kube-system/kube-scheduler, err deferring sync till endpoints controller has synced +W0131 02:29:03.514445 1 queue.go:87] requeuing kube-system/node-controller-token-826dl, err deferring sync till endpoints controller has synced +2017/01/31 02:29:12 Received OnUpdate notification +2017/01/31 02:29:12 upstream-default-backend: 10.180.1.20 +``` + + diff --git a/examples/custom-controller/deployment.yaml b/examples/custom-controller/deployment.yaml new file mode 100644 index 000000000..cf924b7f7 --- /dev/null +++ b/examples/custom-controller/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: v1 +kind: Service +metadata: + name: default-backend + namespace: default + labels: + name: default-backend + app: dummy-ingress-controller +spec: + ports: + - port: 80 + targetPort: 10254 + selector: + # Point back the the dummy controller's + # healthz port + app: dummy-ingress-controller +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: dummy-ingress-controller + namespace: default + labels: + app: dummy-ingress-controller +spec: + selector: + matchLabels: + app: dummy-ingress-controller + template: + metadata: + labels: + app: dummy-ingress-controller + spec: + containers: + - name: server + image: bprashanth/dummy-ingress-controller-amd64:0.1 + imagePullPolicy: Always + ports: + - containerPort: 10254 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + args: + - /server + - --default-backend-service=$(POD_NAMESPACE)/default-backend diff --git a/examples/custom-controller/server.go b/examples/custom-controller/server.go new file mode 100644 index 000000000..6f1dcb840 --- /dev/null +++ b/examples/custom-controller/server.go @@ -0,0 +1,82 @@ +package main + +import ( + "log" + "net/http" + "os/exec" + "strings" + + nginxconfig "k8s.io/ingress/controllers/nginx/pkg/config" + "k8s.io/ingress/core/pkg/ingress" + "k8s.io/ingress/core/pkg/ingress/controller" + "k8s.io/ingress/core/pkg/ingress/defaults" + "k8s.io/kubernetes/pkg/api" +) + +func main() { + dc := newDummyController() + ic := controller.NewIngressController(dc) + defer func() { + log.Printf("Shutting down ingress controller...") + ic.Stop() + }() + ic.Start() +} + +func newDummyController() ingress.Controller { + return &DummyController{} +} + +type DummyController struct{} + +func (dc DummyController) SetConfig(cfgMap *api.ConfigMap) { + log.Printf("Config map %+v", cfgMap) +} + +func (dc DummyController) Reload(data []byte) ([]byte, bool, error) { + out, err := exec.Command("echo", string(data)).CombinedOutput() + if err != nil { + log.Printf("Reloaded new config %s", out) + } else { + return out, false, err + } + return out, true, err +} + +func (dc DummyController) Test(file string) *exec.Cmd { + return exec.Command("echo", file) +} + +func (dc DummyController) OnUpdate(updatePayload ingress.Configuration) ([]byte, error) { + log.Printf("Received OnUpdate notification") + for _, b := range updatePayload.Backends { + eps := []string{} + for _, e := range b.Endpoints { + eps = append(eps, e.Address) + } + log.Printf("%v: %v", b.Name, strings.Join(eps, ", ")) + } + return []byte(``), nil +} + +func (dc DummyController) BackendDefaults() defaults.Backend { + // Just adopt nginx's default backend config + return nginxconfig.NewDefault().Backend +} + +func (n DummyController) Name() string { + return "dummy Controller" +} + +func (n DummyController) Check(_ *http.Request) error { + return nil +} + +func (dc DummyController) Info() *ingress.BackendInfo { + return &ingress.BackendInfo{ + Name: "dummy", + Release: "0.0.0", + Build: "git-00000000", + Repository: "git://foo.bar.com", + } +}