diff --git a/.gitignore b/.gitignore index 31d3d3e3a5..bf36da9b5b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,10 @@ Session.vim # Ingress Controller binaries osx-nginx-ingress nginx-ingress +!nginx-ingress/ osx-nginx-plus-ingress nginx-plus-ingress -nginx-controller/nginx-controller +cmd/nginx-ingress/nginx-ingress # NGINX Plus license files *.crt @@ -37,3 +38,6 @@ nginx-controller/nginx-controller # Default certificate and key default.pem + +# Dockerfiles for building +Dockerfile diff --git a/.travis.yml b/.travis.yml index a0acd2bf51..811bf0b568 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,7 @@ go: - "1.10" script: - echo "Building ingress controller commit:${TRAVIS_COMMIT}" -- cd nginx-controller && - make BUILD_IN_CONTAINER=0 container; +- make BUILD_IN_CONTAINER=0 container; before_install: - echo "PR Slug:${TRAVIS_PULL_REQUEST_SLUG}" - if [[ "${TRAVIS_PULL_REQUEST_SLUG}" == "nginxinc/kubernetes-ingress" || "${TRAVIS_PULL_REQUEST}" == "false" ]]; then diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d905c537bd..7a43e2303e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,8 +30,12 @@ Read the [documentation](https://github.com/nginxinc/kubernetes-ingress/tree/mas ### Project Structure * This Ingress Controller is written in Go and supports both the open source NGINX software and NGINX Plus. -* The main code resides under `/nginx-controller` -* The project dependencies reside in the `/vendor`. We use [dep](https://github.com/golang/dep) for managing dependencies. +* The project follows a standard Go project layout + * The main code is found at `cmd/nginx-ingress/` + * The internal code is found at `internal/` + * Build files for Docker and CI are found under `build/` + * Deployment yaml files, and Helm files are found at `deployments/` + * The project dependencies are found at `vendor/`. We use [dep](https://github.com/golang/dep) for managing dependencies. ## Contributing diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..3ddcf2abda --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +all: push + +VERSION = edge +TAG = $(VERSION) +PREFIX = nginx/nginx-ingress + +DOCKER_RUN = docker run --rm -v $(shell pwd):/go/src/github.com/nginxinc/kubernetes-ingress +DOCKER_BUILD_RUN = docker run --rm -v $(shell pwd):/go/src/github.com/nginxinc/kubernetes-ingress -w /go/src/github.com/nginxinc/kubernetes-ingress/cmd/nginx-ingress/ +GOLANG_CONTAINER = golang:1.10 +DOCKERFILEPATH = build +DOCKERFILE = Dockerfile # note, this can be overwritten e.g. can be DOCKERFILE=DockerFileForPlus + +BUILD_IN_CONTAINER = 1 +PUSH_TO_GCR = +GENERATE_DEFAULT_CERT_AND_KEY = +DOCKER_BUILD_OPTIONS = + +GIT_COMMIT=$(shell git rev-parse --short HEAD) + +nginx-ingress: +ifeq ($(BUILD_IN_CONTAINER),1) + $(DOCKER_BUILD_RUN) -e CGO_ENABLED=0 $(GOLANG_CONTAINER) go build -a -installsuffix cgo -ldflags "-w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT}" -o /go/src/github.com/nginxinc/kubernetes-ingress/nginx-ingress +else + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags "-w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT}" -o nginx-ingress github.com/nginxinc/kubernetes-ingress/cmd/nginx-ingress +endif + +test: +ifeq ($(BUILD_IN_CONTAINER),1) + $(DOCKER_RUN) $(GOLANG_CONTAINER) go test ./... +else + go test ./... +endif + +certificate-and-key: +ifeq ($(GENERATE_DEFAULT_CERT_AND_KEY),1) + ./build/generate_default_cert_and_key.sh +endif + +container: test nginx-ingress certificate-and-key + cp $(DOCKERFILEPATH)/$(DOCKERFILE) ./Dockerfile + docker build $(DOCKER_BUILD_OPTIONS) -f Dockerfile -t $(PREFIX):$(TAG) . + +push: container +ifeq ($(PUSH_TO_GCR),1) + gcloud docker -- push $(PREFIX):$(TAG) +else + docker push $(PREFIX):$(TAG) +endif + +clean: + rm -f nginx-ingress + rm -f Dockerfile diff --git a/README.md b/README.md index 336a7e08eb..c8b70722dd 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ The table below summarizes the options regarding the images, manifests, helm cha | Version | Description | Image for NGINX | Image for NGINX Plus | Installation Manifests and Helm Chart | Documentation and Examples | | ------- | ----------- | --------------- | -------------------- | ---------------------------------------| -------------------------- | | Latest stable release | For production use | `nginx/nginx-ingress:1.3.0`, `nginx/nginx-ingress:1.3.0-alpine` from [DockerHub](https://hub.docker.com/r/nginx/nginx-ingress/) or [build your own image](https://github.com/nginxinc/kubernetes-ingress/tree/v1.3.0/nginx-controller). | [Build your own image](https://github.com/nginxinc/kubernetes-ingress/tree/v1.3.0/nginx-controller). | [Manifests](https://github.com/nginxinc/kubernetes-ingress/tree/v1.3.0/install). [Helm chart](https://github.com/nginxinc/kubernetes-ingress/tree/v1.3.0/helm-chart). | [Documentation](https://github.com/nginxinc/kubernetes-ingress/tree/v1.3.0/docs). [Examples](https://github.com/nginxinc/kubernetes-ingress/tree/v1.3.0/examples). | -| Edge | For testing and experimenting | `nginx/nginx-ingress:edge`, `nginx/nginx-ingress:edge-alpine` from [DockerHub](https://hub.docker.com/r/nginx/nginx-ingress/) or [build your own image](https://github.com/nginxinc/kubernetes-ingress/tree/master/nginx-controller). | [Build your own image](https://github.com/nginxinc/kubernetes-ingress/tree/master/nginx-controller). | [Manifests](https://github.com/nginxinc/kubernetes-ingress/tree/master/install). [Helm chart](https://github.com/nginxinc/kubernetes-ingress/tree/master/helm-chart). | [Documentation](https://github.com/nginxinc/kubernetes-ingress/tree/master/docs). [Examples](https://github.com/nginxinc/kubernetes-ingress/tree/master/examples). | +| Edge | For testing and experimenting | `nginx/nginx-ingress:edge`, `nginx/nginx-ingress:edge-alpine` from [DockerHub](https://hub.docker.com/r/nginx/nginx-ingress/) or [build your own image](https://github.com/nginxinc/kubernetes-ingress/tree/master/build). | [Build your own image](https://github.com/nginxinc/kubernetes-ingress/tree/master/build). | [Manifests](https://github.com/nginxinc/kubernetes-ingress/tree/master/deployments). [Helm chart](https://github.com/nginxinc/kubernetes-ingress/tree/master/deployments/helm-chart). | [Documentation](https://github.com/nginxinc/kubernetes-ingress/tree/master/docs). [Examples](https://github.com/nginxinc/kubernetes-ingress/tree/master/examples). | ## Benefits of Using the Ingress Controller with NGINX Plus @@ -71,7 +71,7 @@ NGINX Plus provides you with [advanced statistics](https://www.nginx.com/product * **JWTs** NGINX Plus can validate JSON Web Tokens (JWTs), providing a flexible authentication mechanism. * **Support** Support from NGINX Inc is available for NGINX Plus Ingress controller. -**Note**: Deployment of the Ingress controller for NGINX Plus requires you to do one extra step: build your own [Docker image](nginx-controller) using the certificate and key for your subscription. +**Note**: Deployment of the Ingress controller for NGINX Plus requires you to do one extra step: build your own [Docker image](build) using the certificate and key for your subscription. The Docker image of the Ingress controller for NGINX is [available on Docker Hub](https://hub.docker.com/r/nginx/nginx-ingress/). ## Using Multiple Ingress Controllers diff --git a/nginx-controller/Dockerfile b/build/Dockerfile similarity index 84% rename from nginx-controller/Dockerfile rename to build/Dockerfile index d3e5ffebf1..e640656675 100644 --- a/nginx-controller/Dockerfile +++ b/build/Dockerfile @@ -6,7 +6,7 @@ RUN ln -sf /proc/1/fd/1 /var/log/nginx/access.log \ && ln -sf /proc/1/fd/1 /var/log/nginx/stream-access.log \ && ln -sf /proc/1/fd/2 /var/log/nginx/error.log -COPY nginx-ingress nginx/templates/nginx.ingress.tmpl nginx/templates/nginx.tmpl / +COPY nginx-ingress internal/nginx/templates/nginx.ingress.tmpl internal/nginx/templates/nginx.tmpl / RUN rm /etc/nginx/conf.d/* diff --git a/nginx-controller/DockerfileForAlpine b/build/DockerfileForAlpine similarity index 84% rename from nginx-controller/DockerfileForAlpine rename to build/DockerfileForAlpine index 08e44b88c7..9f8a5be296 100644 --- a/nginx-controller/DockerfileForAlpine +++ b/build/DockerfileForAlpine @@ -6,7 +6,7 @@ RUN ln -sf /proc/1/fd/1 /var/log/nginx/access.log \ && ln -sf /proc/1/fd/1 /var/log/nginx/stream-access.log \ && ln -sf /proc/1/fd/2 /var/log/nginx/error.log -COPY nginx-ingress nginx/templates/nginx.ingress.tmpl nginx/templates/nginx.tmpl / +COPY nginx-ingress internal/nginx/templates/nginx.ingress.tmpl internal/nginx/templates/nginx.tmpl / RUN rm /etc/nginx/conf.d/* diff --git a/nginx-controller/DockerfileForPlus b/build/DockerfileForPlus similarity index 95% rename from nginx-controller/DockerfileForPlus rename to build/DockerfileForPlus index 3e8530bdce..aed325e074 100644 --- a/nginx-controller/DockerfileForPlus +++ b/build/DockerfileForPlus @@ -50,7 +50,7 @@ RUN ln -sf /proc/1/fd/1 /var/log/nginx/access.log \ EXPOSE 80 443 -COPY nginx-ingress nginx/templates/nginx-plus.ingress.tmpl nginx/templates/nginx-plus.tmpl / +COPY nginx-ingress internal/nginx/templates/nginx-plus.ingress.tmpl internal/nginx/templates/nginx-plus.tmpl / RUN rm /etc/nginx/conf.d/* \ && mkdir -p /etc/nginx/secrets diff --git a/nginx-controller/README.md b/build/README.md similarity index 94% rename from nginx-controller/README.md rename to build/README.md index f2069a94ef..907cd522ae 100644 --- a/nginx-controller/README.md +++ b/build/README.md @@ -23,19 +23,13 @@ Although the Ingress controller is written in golang, golang is not required, as ### Building the Image and Pushing It to the Private Registry -We build the image using the make utility and the provided `Makefile`. Let’s create the controller binary, build an image and push the image to the private registry. +We build the image using the make utility and the provided `Makefile`. Let’s create the controller binary, build an image and push the image to the private registry. 1. Make sure to run the `docker login` command first to login to the registry. If you’re using Google Container Registry, you don’t need to use the docker command to login -- make sure you’re logged into the gcloud tool (using the `gcloud auth login` command) and set the variable `PUSH_TO_GCR=1` when running the make command. -1. Clone the Ingress controller repo and change your folder to `nginx-controller`: +1. Clone the Ingress controller repo: ``` $ git clone https://github.com/nginxinc/kubernetes-ingress/ - $ cd kubernetes-ingress/nginx-controller - ``` - -1. If you're using a stable release, check out the corresponding tag. For release 1.3.0, run: - ``` - $ git checkout v1.3.0 ``` 1. Build the image: @@ -48,7 +42,7 @@ We build the image using the make utility and the provided `Makefile`. Let’s c As the result, the image **myregistry.example.com/nginx-ingress:edge** is built and pushed to the registry. Note that the tag `edge` comes from the `VERSION` variable, defined in the Makefile. - * For NGINX Plus, first, make sure that the certificate (`nginx-repo.crt`) and the key (`nginx-repo.key`) of your license are located in the `nginx-controller` folder: + * For NGINX Plus, first, make sure that the certificate (`nginx-repo.crt`) and the key (`nginx-repo.key`) of your license are located in the root of the project: ``` $ ls nginx-repo.* nginx-repo.crt nginx-repo.key @@ -84,10 +78,8 @@ The **Makefile** contains the following main variables for you to customize (eit 1. `Dockerfile`, for building a debian-based image with NGINX. It's used by default. 1. `DockerfileForAlpine`, for building an alpine-based image with NGINX. 1. `DockerfileForPlus`, for building an debian-based image with NGINX Plus. -* **GENERATE_DEFAULT_CERT_AND_KEY** - The Ingress controller requires a certificate and a key for the default HTTP/HTTPS server. You can reference them in a TLS Secret in a command-line argument to the Ingress controller. As an alternative, you can add a file in the PEM format with your certificate and key to the image as `/etc/nginx/secrets/default`. Optionally, you can generate a self-signed certificate and a key during the build process. Set `GENERATE_DEFAULT_CERT_AND_KEY` to `1` to generate a certificate and a key in the `default.pem` file. Note that you must add the `ADD` instruction in the Dockerfile to copy the cert and the key to the image. The default value of `GENERATE_DEFAULT_CERT_AND_KEY` is `0`. +* **GENERATE_DEFAULT_CERT_AND_KEY** - The Ingress controller requires a certificate and a key for the default HTTP/HTTPS server. You can reference them in a TLS Secret in a command-line argument to the Ingress controller. As an alternative, you can add a file in the PEM format with your certificate and key to the image as `/etc/nginx/secrets/default`. Optionally, you can generate a self-signed certificate and a key during the build process. Set `GENERATE_DEFAULT_CERT_AND_KEY` to `1` to generate a certificate and a key in the `default.pem` file. Note that you must add the `ADD` instruction in the Dockerfile to copy the cert and the key to the image. The default value of `GENERATE_DEFAULT_CERT_AND_KEY` is `0`. * **DOCKER_BUILD_OPTIONS** -- the [options](https://docs.docker.com/engine/reference/commandline/build/#options) for the `docker build` command. For example, `--pull`. * **BUILD_IN_CONTAINER** -- By default, to compile the controller we use the [golang](https://hub.docker.com/_/golang/) container that we run as part of the building process. If you want to compile the controller using your local golang environment: 1. Make sure that the Ingress controller repo is in your `$GOPATH`. 1. Specify `BUILD_IN_CONTAINER=0` when you run the make command. - - diff --git a/ci/Jenkinsfile b/build/ci/Jenkinsfile similarity index 100% rename from ci/Jenkinsfile rename to build/ci/Jenkinsfile diff --git a/nginx-controller/generate_default_cert_and_key.sh b/build/generate_default_cert_and_key.sh similarity index 100% rename from nginx-controller/generate_default_cert_and_key.sh rename to build/generate_default_cert_and_key.sh diff --git a/nginx-controller/main.go b/cmd/nginx-ingress/main.go similarity index 83% rename from nginx-controller/main.go rename to cmd/nginx-ingress/main.go index 90d3358fb3..5bbbafa191 100644 --- a/nginx-controller/main.go +++ b/cmd/nginx-ingress/main.go @@ -13,9 +13,11 @@ import ( "github.com/golang/glog" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/controller" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx/plus" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + "github.com/nginxinc/kubernetes-ingress/internal/handlers" + "github.com/nginxinc/kubernetes-ingress/internal/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/nginx/plus" + "github.com/nginxinc/kubernetes-ingress/internal/utils" api_v1 "k8s.io/api/core/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -151,7 +153,7 @@ func main() { ngxc := nginx.NewNginxController("/etc/nginx/", local) if *defaultServerSecret != "" { - ns, name, err := controller.ParseNamespaceName(*defaultServerSecret) + ns, name, err := utils.ParseNamespaceName(*defaultServerSecret) if err != nil { glog.Fatalf("Error parsing the default-server-tls-secret argument: %v", err) } @@ -175,7 +177,7 @@ func main() { cfg := nginx.NewDefaultConfig() if *nginxConfigMaps != "" { - ns, name, err := controller.ParseNamespaceName(*nginxConfigMaps) + ns, name, err := utils.ParseNamespaceName(*nginxConfigMaps) if err != nil { glog.Fatalf("Error parsing the nginx-configmaps argument: %v", err) } @@ -230,21 +232,49 @@ func main() { controllerNamespace := os.Getenv("POD_NAMESPACE") lbcInput := controller.NewLoadBalancerControllerInput{ - KubeClient: kubeClient, - ResyncPeriod: 30 * time.Second, - Namespace: *watchNamespace, - CNF: cnf, - NginxConfigMaps: *nginxConfigMaps, - DefaultServerSecret: *defaultServerSecret, - NginxPlus: *nginxPlus, - IngressClass: *ingressClass, - UseIngressClassOnly: *useIngressClassOnly, - ExternalServiceName: *externalService, - ControllerNamespace: controllerNamespace, - ReportIngressStatus: *reportIngressStatus, - LeaderElectionEnabled: *leaderElectionEnabled, + KubeClient: kubeClient, + ResyncPeriod: 30 * time.Second, + Namespace: *watchNamespace, + NginxConfigurator: cnf, + DefaultServerSecret: *defaultServerSecret, + IsNginxPlus: *nginxPlus, + IngressClass: *ingressClass, + UseIngressClassOnly: *useIngressClassOnly, + ExternalServiceName: *externalService, + ControllerNamespace: controllerNamespace, + ReportIngressStatus: *reportIngressStatus, + IsLeaderElectionEnabled: *leaderElectionEnabled, } + lbc := controller.NewLoadBalancerController(lbcInput) + + // create handlers for resources we care about + ingressHandlers := handlers.CreateIngressHandlers(lbc) + secretHandlers := handlers.CreateSecretHandlers(lbc) + serviceHandlers := handlers.CreateServiceHandlers(lbc) + endpointHandlers := handlers.CreateEndpointHandlers(lbc) + + lbc.AddSecretHandler(secretHandlers) + lbc.AddIngressHandler(ingressHandlers) + lbc.AddServiceHandler(serviceHandlers) + lbc.AddEndpointHandler(endpointHandlers) + + if *nginxConfigMaps != "" { + nginxConfigMapsNS, nginxConfigMapsName, err := utils.ParseNamespaceName(*nginxConfigMaps) + if err != nil { + glog.Warning(err) + } else { + lbc.WatchNginxConfigMaps() + configMapHandlers := handlers.CreateConfigMapHandlers(lbc, nginxConfigMapsName) + lbc.AddConfigMapHandler(configMapHandlers, nginxConfigMapsNS) + } + } + + if lbcInput.ReportIngressStatus && lbcInput.IsLeaderElectionEnabled { + leaderHandler := handlers.CreateLeaderHandler(lbc) + lbc.AddLeaderHandler(leaderHandler) + } + go handleTermination(lbc, ngxc, nginxDone) lbc.Run() @@ -254,7 +284,7 @@ func main() { } } -func handleTermination(lbc *controller.LoadBalancerController, ngxc *nginx.NginxController, nginxDone chan error) { +func handleTermination(lbc *controller.LoadBalancerController, ngxc *nginx.Controller, nginxDone chan error) { signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGTERM) diff --git a/nginx-controller/main_test.go b/cmd/nginx-ingress/main_test.go similarity index 100% rename from nginx-controller/main_test.go rename to cmd/nginx-ingress/main_test.go diff --git a/install/README.md b/deployments/README.md similarity index 100% rename from install/README.md rename to deployments/README.md diff --git a/install/common/default-server-secret.yaml b/deployments/common/default-server-secret.yaml similarity index 100% rename from install/common/default-server-secret.yaml rename to deployments/common/default-server-secret.yaml diff --git a/install/common/nginx-config.yaml b/deployments/common/nginx-config.yaml similarity index 100% rename from install/common/nginx-config.yaml rename to deployments/common/nginx-config.yaml diff --git a/install/common/ns-and-sa.yaml b/deployments/common/ns-and-sa.yaml similarity index 100% rename from install/common/ns-and-sa.yaml rename to deployments/common/ns-and-sa.yaml diff --git a/install/daemon-set/nginx-ingress-with-prometheus.yaml b/deployments/daemon-set/nginx-ingress-with-prometheus.yaml similarity index 99% rename from install/daemon-set/nginx-ingress-with-prometheus.yaml rename to deployments/daemon-set/nginx-ingress-with-prometheus.yaml index eabaf93a0a..92398cc9ff 100644 --- a/install/daemon-set/nginx-ingress-with-prometheus.yaml +++ b/deployments/daemon-set/nginx-ingress-with-prometheus.yaml @@ -52,4 +52,3 @@ spec: - :9113 - -nginx.scrape-uri - http://127.0.0.1:8080/stub_status - diff --git a/install/daemon-set/nginx-ingress.yaml b/deployments/daemon-set/nginx-ingress.yaml similarity index 100% rename from install/daemon-set/nginx-ingress.yaml rename to deployments/daemon-set/nginx-ingress.yaml diff --git a/install/daemon-set/nginx-plus-ingress-with-prometheus.yaml b/deployments/daemon-set/nginx-plus-ingress-with-prometheus.yaml similarity index 100% rename from install/daemon-set/nginx-plus-ingress-with-prometheus.yaml rename to deployments/daemon-set/nginx-plus-ingress-with-prometheus.yaml diff --git a/install/daemon-set/nginx-plus-ingress.yaml b/deployments/daemon-set/nginx-plus-ingress.yaml similarity index 100% rename from install/daemon-set/nginx-plus-ingress.yaml rename to deployments/daemon-set/nginx-plus-ingress.yaml diff --git a/install/deployment/nginx-ingress-with-prometheus.yaml b/deployments/deployment/nginx-ingress-with-prometheus.yaml similarity index 99% rename from install/deployment/nginx-ingress-with-prometheus.yaml rename to deployments/deployment/nginx-ingress-with-prometheus.yaml index 051d5b660c..a77d0d6ee9 100644 --- a/install/deployment/nginx-ingress-with-prometheus.yaml +++ b/deployments/deployment/nginx-ingress-with-prometheus.yaml @@ -51,4 +51,3 @@ spec: - :9113 - nginx.scrape-uri - http://127.0.0.1:8080/stub_status - diff --git a/install/deployment/nginx-ingress.yaml b/deployments/deployment/nginx-ingress.yaml similarity index 100% rename from install/deployment/nginx-ingress.yaml rename to deployments/deployment/nginx-ingress.yaml diff --git a/install/deployment/nginx-plus-ingress-with-prometheus.yaml b/deployments/deployment/nginx-plus-ingress-with-prometheus.yaml similarity index 100% rename from install/deployment/nginx-plus-ingress-with-prometheus.yaml rename to deployments/deployment/nginx-plus-ingress-with-prometheus.yaml diff --git a/install/deployment/nginx-plus-ingress.yaml b/deployments/deployment/nginx-plus-ingress.yaml similarity index 100% rename from install/deployment/nginx-plus-ingress.yaml rename to deployments/deployment/nginx-plus-ingress.yaml diff --git a/helm-chart/Chart.yaml b/deployments/helm-chart/Chart.yaml similarity index 75% rename from helm-chart/Chart.yaml rename to deployments/helm-chart/Chart.yaml index 05f8be4cfc..f62e788fed 100644 --- a/helm-chart/Chart.yaml +++ b/deployments/helm-chart/Chart.yaml @@ -3,7 +3,7 @@ version: 0.1.2 appVersion: edge description: NGINX Ingress Controller sources: - - https://github.com/nginxinc/kubernetes-ingress/tree/master/helm-chart + - https://github.com/nginxinc/kubernetes-ingress/tree/master/deployment/helm-chart keywords: - ingress - nginx diff --git a/helm-chart/README.md b/deployments/helm-chart/README.md similarity index 96% rename from helm-chart/README.md rename to deployments/helm-chart/README.md index 92f6dfd231..3baa696460 100644 --- a/helm-chart/README.md +++ b/deployments/helm-chart/README.md @@ -10,7 +10,7 @@ This chart deploys the NGINX Ingress controller in your Kubernetes cluster. - Helm 2.8.x+. - Git. - If you’d like to use NGINX Plus: - - Build an Ingress controller image with NGINX Plus and push it to your private registry by following the instructions from [here](../nginx-controller/README.md). + - Build an Ingress controller image with NGINX Plus and push it to your private registry by following the instructions from [here](../../build/README.md). - Update the `controller.image.repository` field of the `values-plus.yaml` accordingly. ## Installing the Chart @@ -19,13 +19,9 @@ This chart deploys the NGINX Ingress controller in your Kubernetes cluster. ``` $ git clone https://github.com/nginxinc/kubernetes-ingress/ ``` -1. If you're using a stable release, check out the corresponding tag. For release 1.3.0, run: +2. Change your working directory to /deployments/helm-chart: ``` - $ git checkout v1.3.0 - ``` -2. Change your working directory to /helm-chart: - ``` - $ cd kubernetes-ingress/helm-chart + $ cd kubernetes-ingress/deployments/helm-chart ``` 3. To install the chart with the release name my-release (my-release is the name that you choose): diff --git a/helm-chart/templates/NOTES.txt b/deployments/helm-chart/templates/NOTES.txt similarity index 100% rename from helm-chart/templates/NOTES.txt rename to deployments/helm-chart/templates/NOTES.txt diff --git a/helm-chart/templates/controller-configmap.yaml b/deployments/helm-chart/templates/controller-configmap.yaml similarity index 100% rename from helm-chart/templates/controller-configmap.yaml rename to deployments/helm-chart/templates/controller-configmap.yaml diff --git a/helm-chart/templates/controller-daemonset.yaml b/deployments/helm-chart/templates/controller-daemonset.yaml similarity index 100% rename from helm-chart/templates/controller-daemonset.yaml rename to deployments/helm-chart/templates/controller-daemonset.yaml diff --git a/helm-chart/templates/controller-deployment.yaml b/deployments/helm-chart/templates/controller-deployment.yaml similarity index 100% rename from helm-chart/templates/controller-deployment.yaml rename to deployments/helm-chart/templates/controller-deployment.yaml diff --git a/helm-chart/templates/controller-secret.yaml b/deployments/helm-chart/templates/controller-secret.yaml similarity index 100% rename from helm-chart/templates/controller-secret.yaml rename to deployments/helm-chart/templates/controller-secret.yaml diff --git a/helm-chart/templates/controller-service.yaml b/deployments/helm-chart/templates/controller-service.yaml similarity index 100% rename from helm-chart/templates/controller-service.yaml rename to deployments/helm-chart/templates/controller-service.yaml diff --git a/helm-chart/templates/controller-serviceaccount.yaml b/deployments/helm-chart/templates/controller-serviceaccount.yaml similarity index 100% rename from helm-chart/templates/controller-serviceaccount.yaml rename to deployments/helm-chart/templates/controller-serviceaccount.yaml diff --git a/helm-chart/templates/rbac.yaml b/deployments/helm-chart/templates/rbac.yaml similarity index 100% rename from helm-chart/templates/rbac.yaml rename to deployments/helm-chart/templates/rbac.yaml diff --git a/helm-chart/values-icp.yaml b/deployments/helm-chart/values-icp.yaml similarity index 100% rename from helm-chart/values-icp.yaml rename to deployments/helm-chart/values-icp.yaml diff --git a/helm-chart/values-plus.yaml b/deployments/helm-chart/values-plus.yaml similarity index 100% rename from helm-chart/values-plus.yaml rename to deployments/helm-chart/values-plus.yaml diff --git a/helm-chart/values.yaml b/deployments/helm-chart/values.yaml similarity index 100% rename from helm-chart/values.yaml rename to deployments/helm-chart/values.yaml diff --git a/install/rbac/rbac.yaml b/deployments/rbac/rbac.yaml similarity index 100% rename from install/rbac/rbac.yaml rename to deployments/rbac/rbac.yaml diff --git a/install/service/loadbalancer-aws-elb.yaml b/deployments/service/loadbalancer-aws-elb.yaml similarity index 100% rename from install/service/loadbalancer-aws-elb.yaml rename to deployments/service/loadbalancer-aws-elb.yaml diff --git a/install/service/loadbalancer.yaml b/deployments/service/loadbalancer.yaml similarity index 100% rename from install/service/loadbalancer.yaml rename to deployments/service/loadbalancer.yaml diff --git a/install/service/nodeport.yaml b/deployments/service/nodeport.yaml similarity index 100% rename from install/service/nodeport.yaml rename to deployments/service/nodeport.yaml diff --git a/docs/installation.md b/docs/installation.md index bdc8dfd9a0..9df112bee0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -5,9 +5,9 @@ Make sure you have access to the Ingress controller image: * For NGINX Ingress controller, use the image `nginx/nginx-ingress` from [DockerHub](https://hub.docker.com/r/nginx/nginx-ingress/). -* For NGINX Plus Ingress controller, build your own image and push it to your private Docker registry by following the instructions from [here](../nginx-controller). +* For NGINX Plus Ingress controller, build your own image and push it to your private Docker registry by following the instructions from [here](../build/README.md). -The installation manifests are located in the [install](../install) folder. In the steps below we assume that you will be running the commands from that folder. +The installation manifests are located in the [deployments](../deployments) folder. In the steps below we assume that you will be running the commands from that folder. ## 1. Create a Namespace, a SA and the Default Secret. diff --git a/docs/nginx-ingress-controllers.md b/docs/nginx-ingress-controllers.md index adf0b88fa4..f25a0ff6df 100644 --- a/docs/nginx-ingress-controllers.md +++ b/docs/nginx-ingress-controllers.md @@ -25,7 +25,7 @@ The table below summarizes the key difference between nginxinc/kubernetes-ingres | TCP SSL Passthrough | Supported via a ConfigMap | Not supported | Not supported | | JWT validation | Not supported | Not supported | Supported | | Session persistence | Supported via a third-party module | Not supported | Supported | -| Configuration templates *1 | See the [template](https://github.com/kubernetes/ingress-nginx/blob/master/rootfs/etc/nginx/template/nginx.tmpl) | See the [templates](https://github.com/nginxinc/kubernetes-ingress/tree/master/nginx-controller/nginx/templates) | See the [templates](https://github.com/nginxinc/kubernetes-ingress/tree/master/nginx-controller/nginx/templates) | +| Configuration templates *1 | See the [template](https://github.com/kubernetes/ingress-nginx/blob/master/rootfs/etc/nginx/template/nginx.tmpl) | See the [templates](https://github.com/nginxinc/kubernetes-ingress/tree/master/internal/nginx/templates) | See the [templates](https://github.com/nginxinc/kubernetes-ingress/tree/master/internal/nginx/templates) | | **Deployment** | | Command-line arguments *2 | See the [arguments](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/cli-arguments.md) | See the [arguments](cli-arguments.md) | See the [arguments](cli-arguments.md) | | TLS certificate and key for the default server | Required as a command-line argument/ auto-generated | Required as a command-line argument | Required as a command-line argument | diff --git a/examples/customization/README.md b/examples/customization/README.md index f204f9846c..e38a046389 100644 --- a/examples/customization/README.md +++ b/examples/customization/README.md @@ -24,7 +24,7 @@ The table below summarizes all of the options. For some of them, there are examp | `nginx.org/redirect-to-https` | `redirect-to-https` | Sets the 301 redirect rule based on the value of the `http_x_forwarded_proto` header on the server block to force incoming traffic to be over HTTPS. Useful when terminating SSL in a load balancer in front of the Ingress controller — see [115](https://github.com/nginxinc/kubernetes-ingress/issues/115) | `False` | | | `ingress.kubernetes.io/ssl-redirect` | `ssl-redirect` | Sets an unconditional 301 redirect rule for all incoming HTTP traffic to force incoming traffic over HTTPS. | `True` | | | N/A | `error-log-level` | Sets the global [error log level](http://nginx.org/en/docs/ngx_core_module.html#error_log) for NGINX. | `notice` | | -| N/A | `log-format` | Sets the custom [log format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format). | See the [template file](../../nginx-controller/nginx/nginx.conf.tmpl). | | +| N/A | `log-format` | Sets the custom [log format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format). | See the [template file](../../internal/nginx/templates/nginx.tmpl). | | | `nginx.org/hsts` | `hsts` | Enables [HTTP Strict Transport Security (HSTS)](https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/): the HSTS header is added to the responses from backends. The `preload` directive is included in the header. | `False` | | | `nginx.org/hsts-max-age` | `hsts-max-age` | Sets the value of the `max-age` directive of the HSTS header. | `2592000` (1 month) | | `nginx.org/hsts-include-subdomains` | `hsts-include-subdomains` | Adds the `includeSubDomains` directive to the HSTS header. | `False`| | @@ -69,7 +69,7 @@ The table below summarizes all of the options. For some of them, there are examp | `nginx.com/slow-start` | N/A | Sets the upstream server [slow-start period](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#server-slow-start). By default, slow-start is activated after a server becomes [available](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#passive-health-checks) or [healthy](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#active-health-checks). To enable slow-start for newly added servers, configure [mandatory active health checks](../health-checks). | `"0s"` | | | N/A | `external-status-address` | Sets the address to be reported in the status of Ingress resources. Requires the `-report-status` command-line argument. Overrides the `-external-service` argument. | N/A | [Report Ingress Status](../../docs/report-ingress-status.md). | | N/A | `stream-snippets` | Sets a custom snippet in stream context. | N/A | [Support for TCP/UDP Load Balancing](../tcp-udp). | -| N/A | `stream-log-format` | Sets the custom [log format](http://nginx.org/en/docs/stream/ngx_stream_log_module.html#log_format) for TCP/UDP load balancing. | See the [template file](../../nginx-controller/nginx/nginx.conf.tmpl). | | +| N/A | `stream-log-format` | Sets the custom [log format](http://nginx.org/en/docs/stream/ngx_stream_log_module.html#log_format) for TCP/UDP load balancing. | See the [template file](../../internal/nginx/templates/nginx.tmpl). | | ## Using ConfigMaps diff --git a/examples/openshift/README.md b/examples/openshift/README.md index 129c3b51cc..68172e5fce 100644 --- a/examples/openshift/README.md +++ b/examples/openshift/README.md @@ -2,17 +2,17 @@ ## Prerequisites -* A cluster with OpenShift Release 3.5 and above. +* A cluster with OpenShift Release 3.5 and above. * You must be the cluster administrator to deploy the Ingress controller. * For NGINX Plus: - * Build and make available in your cluster the [Ingress controller](../../nginx-controller) image. + * Build and make available in your cluster the [Ingress controller](../../build) image. * Update the container image field in the `nginx-plus-ingress-rc.yaml` file accordingly. ## Steps 1. Avoid conflicts with the OpenShift Router. - + NGINX Plus Ingress controller must be able to bind to ports 80 and 443 of the cluster node, where it is running, like the OpenShift Router. Thus, you need to make sure that the Ingress controller and the Router are running on separate nodes. Additionally, NGINX Plus binds to port 8080 to expose its API and the monitoring dashboard. To quickly disable the Router you can run: @@ -27,7 +27,7 @@ 1. Create a service account for the Ingress controller with the name *nginx-ingress*: ``` - $ oc create sa nginx-ingress + $ oc create sa nginx-ingress ``` 1. Create a cluster role for the Ingress controller: ``` @@ -62,7 +62,7 @@ $ oc create -f ingress-admin-role.yaml ``` - Add this role to the users. As an example, we add this role for the user *developer* from the project *myproject*. + Add this role to the users. As an example, we add this role for the user *developer* from the project *myproject*. ``` $ oc policy add-role-to-user ingress-admin developer -n myproject ``` @@ -71,4 +71,4 @@ $ oc adm policy add-scc-to-user anyuid -z default -n myproject ``` -1. Now you can login as the user *developer* to the project *myproject* and [deploy the Cafe application](../complete-example#2-deploy-the-cafe-application). \ No newline at end of file +1. Now you can login as the user *developer* to the project *myproject* and [deploy the Cafe application](../complete-example#2-deploy-the-cafe-application). diff --git a/nginx-controller/controller/controller.go b/internal/controller/controller.go similarity index 61% rename from nginx-controller/controller/controller.go rename to internal/controller/controller.go index d10c4165f0..8882679380 100644 --- a/nginx-controller/controller/controller.go +++ b/internal/controller/controller.go @@ -18,13 +18,14 @@ package controller import ( "fmt" - "reflect" - "strings" "time" "github.com/golang/glog" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/queue" + "github.com/nginxinc/kubernetes-ingress/internal/utils" + "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/intstr" @@ -49,63 +50,68 @@ const ( // LoadBalancerController watches Kubernetes API and // reconfigures NGINX via NginxController when needed type LoadBalancerController struct { - client kubernetes.Interface - ingController cache.Controller - svcController cache.Controller - endpController cache.Controller - cfgmController cache.Controller - secrController cache.Controller - ingLister StoreToIngressLister - svcLister cache.Store - endpLister StoreToEndpointLister - cfgmLister StoreToConfigMapLister - secrLister StoreToSecretLister - syncQueue *taskQueue - stopCh chan struct{} - cnf *nginx.Configurator - watchNginxConfigMaps bool - nginxPlus bool - recorder record.EventRecorder - defaultServerSecret string - ingressClass string - useIngressClassOnly bool - statusUpdater *StatusUpdater - leaderElector *leaderelection.LeaderElector - reportIngressStatus bool - leaderElectionEnabled bool + client kubernetes.Interface + ingressController cache.Controller + svcController cache.Controller + endpointController cache.Controller + configMapController cache.Controller + secretController cache.Controller + ingressLister utils.StoreToIngressLister + svcLister cache.Store + endpointLister utils.StoreToEndpointLister + configMapLister utils.StoreToConfigMapLister + secretLister utils.StoreToSecretLister + syncQueue *queue.TaskQueue + stop chan struct{} + configurator *nginx.Configurator + watchNginxConfigMaps bool + isNginxPlus bool + recorder record.EventRecorder + defaultServerSecret string + ingressClass string + useIngressClassOnly bool + statusUpdater *StatusUpdater + leaderElector *leaderelection.LeaderElector + reportIngressStatus bool + isLeaderElectionEnabled bool + resync time.Duration + namespace string + controllerNamespace string } var keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc // NewLoadBalancerControllerInput holds the input needed to call NewLoadBalancerController. type NewLoadBalancerControllerInput struct { - KubeClient kubernetes.Interface - ResyncPeriod time.Duration - Namespace string - CNF *nginx.Configurator - NginxConfigMaps string - DefaultServerSecret string - NginxPlus bool - IngressClass string - UseIngressClassOnly bool - ExternalServiceName string - ControllerNamespace string - ReportIngressStatus bool - LeaderElectionEnabled bool + KubeClient kubernetes.Interface + ResyncPeriod time.Duration + Namespace string + NginxConfigurator *nginx.Configurator + DefaultServerSecret string + IsNginxPlus bool + IngressClass string + UseIngressClassOnly bool + ExternalServiceName string + ControllerNamespace string + ReportIngressStatus bool + IsLeaderElectionEnabled bool } // NewLoadBalancerController creates a controller func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalancerController { lbc := LoadBalancerController{ - client: input.KubeClient, - stopCh: make(chan struct{}), - cnf: input.CNF, - defaultServerSecret: input.DefaultServerSecret, - nginxPlus: input.NginxPlus, - ingressClass: input.IngressClass, - useIngressClassOnly: input.UseIngressClassOnly, - reportIngressStatus: input.ReportIngressStatus, - leaderElectionEnabled: input.LeaderElectionEnabled, + client: input.KubeClient, + stop: make(chan struct{}), + configurator: input.NginxConfigurator, + defaultServerSecret: input.DefaultServerSecret, + isNginxPlus: input.IsNginxPlus, + ingressClass: input.IngressClass, + useIngressClassOnly: input.UseIngressClassOnly, + reportIngressStatus: input.ReportIngressStatus, + isLeaderElectionEnabled: input.IsLeaderElectionEnabled, + resync: input.ResyncPeriod, + namespace: input.Namespace, + controllerNamespace: input.ControllerNamespace, } eventBroadcaster := record.NewBroadcaster() @@ -116,289 +122,123 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc lbc.recorder = eventBroadcaster.NewRecorder(scheme.Scheme, api_v1.EventSource{Component: "nginx-ingress-controller"}) - lbc.syncQueue = NewTaskQueue(lbc.sync) + lbc.syncQueue = queue.NewTaskQueue(lbc.sync) glog.V(3).Infof("Nginx Ingress Controller has class: %v", input.IngressClass) - ingHandlers := cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - addIng := obj.(*extensions.Ingress) - if !lbc.isNginxIngress(addIng) { - glog.Infof("Ignoring Ingress %v based on Annotation %v", addIng.Name, ingressClassKey) - return - } - glog.V(3).Infof("Adding Ingress: %v", addIng.Name) - lbc.syncQueue.enqueue(obj) - }, - DeleteFunc: func(obj interface{}) { - remIng, isIng := obj.(*extensions.Ingress) - if !isIng { - deletedState, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - glog.V(3).Infof("Error received unexpected object: %v", obj) - return - } - remIng, ok = deletedState.Obj.(*extensions.Ingress) - if !ok { - glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Ingress object: %v", deletedState.Obj) - return - } - } - if !lbc.isNginxIngress(remIng) { - return - } - if isMinion(remIng) { - master, err := lbc.findMasterForMinion(remIng) - if err != nil { - glog.Infof("Ignoring Ingress %v(Minion): %v", remIng.Name, err) - return - } - glog.V(3).Infof("Removing Ingress: %v(Minion) for %v(Master)", remIng.Name, master.Name) - lbc.syncQueue.enqueue(master) - } else { - glog.V(3).Infof("Removing Ingress: %v", remIng.Name) - lbc.syncQueue.enqueue(obj) - } - }, - UpdateFunc: func(old, cur interface{}) { - curIng := cur.(*extensions.Ingress) - oldIng := old.(*extensions.Ingress) - if !lbc.isNginxIngress(curIng) { - return - } - if hasChanges(oldIng, curIng) { - glog.V(3).Infof("Ingress %v changed, syncing", curIng.Name) - lbc.syncQueue.enqueue(cur) - } - }, - } - lbc.ingLister.Store, lbc.ingController = cache.NewInformer( - cache.NewListWatchFromClient(lbc.client.Extensions().RESTClient(), "ingresses", input.Namespace, fields.Everything()), - &extensions.Ingress{}, input.ResyncPeriod, ingHandlers) - - // statusUpdater requires ingLister to be instantiated, above. lbc.statusUpdater = &StatusUpdater{ client: input.KubeClient, namespace: input.ControllerNamespace, externalServiceName: input.ExternalServiceName, - ingLister: &lbc.ingLister, + ingLister: &lbc.ingressLister, keyFunc: keyFunc, } - if input.ReportIngressStatus && input.LeaderElectionEnabled { - leaderCallbacks := leaderelection.LeaderCallbacks{ - OnStartedLeading: func(stop <-chan struct{}) { - glog.V(3).Info("started leading, updating ingress status") - ingresses, mergeableIngresses := lbc.getManagedIngresses() - err := lbc.statusUpdater.UpdateManagedAndMergeableIngresses(ingresses, mergeableIngresses) - if err != nil { - glog.V(3).Infof("error updating status when starting leading: %v", err) - } - }, - } + return &lbc +} - var err error - lbc.leaderElector, err = NewLeaderElector(input.KubeClient, leaderCallbacks, input.ControllerNamespace) - if err != nil { - glog.V(3).Infof("Error starting LeaderElection: %v", err) - } - } +// UpdateManagedAndMergeableIngresses invokes the UpdateManagedAndMergeableIngresses method on the Status Updater +func (lbc *LoadBalancerController) UpdateManagedAndMergeableIngresses(ingresses []v1beta1.Ingress, mergeableIngresses map[string]*nginx.MergeableIngresses) error { + return lbc.statusUpdater.UpdateManagedAndMergeableIngresses(ingresses, mergeableIngresses) +} - svcHandlers := cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - addSvc := obj.(*api_v1.Service) - if lbc.isExternalServiceForStatus(addSvc) { - lbc.syncQueue.enqueue(addSvc) - return - } - glog.V(3).Infof("Adding service: %v", addSvc.Name) - lbc.enqueueIngressForService(addSvc) - }, - DeleteFunc: func(obj interface{}) { - remSvc, isSvc := obj.(*api_v1.Service) - if !isSvc { - deletedState, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - glog.V(3).Infof("Error received unexpected object: %v", obj) - return - } - remSvc, ok = deletedState.Obj.(*api_v1.Service) - if !ok { - glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Service object: %v", deletedState.Obj) - return - } - } - if lbc.isExternalServiceForStatus(remSvc) { - lbc.syncQueue.enqueue(remSvc) - return - } +// AddLeaderHandler adds the handler for leader election to the controller +func (lbc *LoadBalancerController) AddLeaderHandler(leaderHandler leaderelection.LeaderCallbacks) { + var err error + lbc.leaderElector, err = NewLeaderElector(lbc.client, leaderHandler, lbc.controllerNamespace) + if err != nil { + glog.V(3).Infof("Error starting LeaderElection: %v", err) + } +} - glog.V(3).Infof("Removing service: %v", remSvc.Name) - lbc.enqueueIngressForService(remSvc) +// GetIngressClassKey returns the ingress class key +func (lbc *LoadBalancerController) GetIngressClassKey() string { + return ingressClassKey +} - }, - UpdateFunc: func(old, cur interface{}) { - if !reflect.DeepEqual(old, cur) { - curSvc := cur.(*api_v1.Service) - if lbc.isExternalServiceForStatus(curSvc) { - lbc.syncQueue.enqueue(curSvc) - return - } - glog.V(3).Infof("Service %v changed, syncing", curSvc.Name) - lbc.enqueueIngressForService(curSvc) - } - }, - } - lbc.svcLister, lbc.svcController = cache.NewInformer( - cache.NewListWatchFromClient(lbc.client.Core().RESTClient(), "services", input.Namespace, fields.Everything()), - &api_v1.Service{}, input.ResyncPeriod, svcHandlers) - - endpHandlers := cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - addEndp := obj.(*api_v1.Endpoints) - glog.V(3).Infof("Adding endpoints: %v", addEndp.Name) - lbc.syncQueue.enqueue(obj) - }, - DeleteFunc: func(obj interface{}) { - remEndp, isEndp := obj.(*api_v1.Endpoints) - if !isEndp { - deletedState, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - glog.V(3).Infof("Error received unexpected object: %v", obj) - return - } - remEndp, ok = deletedState.Obj.(*api_v1.Endpoints) - if !ok { - glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Endpoints object: %v", deletedState.Obj) - return - } - } - glog.V(3).Infof("Removing endpoints: %v", remEndp.Name) - lbc.syncQueue.enqueue(obj) - }, - UpdateFunc: func(old, cur interface{}) { - if !reflect.DeepEqual(old, cur) { - glog.V(3).Infof("Endpoints %v changed, syncing", - cur.(*api_v1.Endpoints).Name) - lbc.syncQueue.enqueue(cur) - } - }, - } - lbc.endpLister.Store, lbc.endpController = cache.NewInformer( - cache.NewListWatchFromClient(lbc.client.Core().RESTClient(), "endpoints", input.Namespace, fields.Everything()), - &api_v1.Endpoints{}, input.ResyncPeriod, endpHandlers) +// AddSyncQueue enqueues the provided item on the sync queue +func (lbc *LoadBalancerController) AddSyncQueue(item interface{}) { + lbc.syncQueue.Enqueue(item) +} - secrHandlers := cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - secr := obj.(*api_v1.Secret) - if err := lbc.ValidateSecret(secr); err != nil { - return - } - nsname := secr.Namespace + "/" + secr.Name - if nsname == lbc.defaultServerSecret { - glog.V(3).Infof("Adding default server Secret: %v", secr.Name) - lbc.syncQueue.enqueue(obj) - } - }, - DeleteFunc: func(obj interface{}) { - remSecr, isSecr := obj.(*api_v1.Secret) - if !isSecr { - deletedState, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - glog.V(3).Infof("Error received unexpected object: %v", obj) - return - } - remSecr, ok = deletedState.Obj.(*api_v1.Secret) - if !ok { - glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Secret object: %v", deletedState.Obj) - return - } - } - if err := lbc.ValidateSecret(remSecr); err != nil { - return - } +// WatchNginxConfigMaps sets the controller to watch config map changes +func (lbc *LoadBalancerController) WatchNginxConfigMaps() { + lbc.watchNginxConfigMaps = true +} - glog.V(3).Infof("Removing Secret: %v", remSecr.Name) - lbc.syncQueue.enqueue(obj) - }, - UpdateFunc: func(old, cur interface{}) { - errOld := lbc.ValidateSecret(old.(*api_v1.Secret)) - errCur := lbc.ValidateSecret(cur.(*api_v1.Secret)) - if errOld != nil && errCur != nil { - return - } +// AddSecretHandler adds the handler for secrets to the controller +func (lbc *LoadBalancerController) AddSecretHandler(handlers cache.ResourceEventHandlerFuncs) { + lbc.secretLister.Store, lbc.secretController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.client.Core().RESTClient(), + "secrets", + lbc.namespace, + fields.Everything()), + &api_v1.Secret{}, + lbc.resync, + handlers, + ) +} - if !reflect.DeepEqual(old, cur) { - glog.V(3).Infof("Secret %v changed, syncing", - cur.(*api_v1.Secret).Name) - lbc.syncQueue.enqueue(cur) - } - }, - } +// AddServiceHandler adds the handler for services to the controller +func (lbc *LoadBalancerController) AddServiceHandler(handlers cache.ResourceEventHandlerFuncs) { + lbc.svcLister, lbc.svcController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.client.Core().RESTClient(), + "services", + lbc.namespace, + fields.Everything()), + &api_v1.Service{}, + lbc.resync, + handlers, + ) +} - lbc.secrLister.Store, lbc.secrController = cache.NewInformer( - cache.NewListWatchFromClient(lbc.client.Core().RESTClient(), "secrets", input.Namespace, fields.Everything()), - &api_v1.Secret{}, input.ResyncPeriod, secrHandlers) +// AddIngressHandler adds the handler for ingresses to the controller +func (lbc *LoadBalancerController) AddIngressHandler(handlers cache.ResourceEventHandlerFuncs) { + lbc.ingressLister.Store, lbc.ingressController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.client.Extensions().RESTClient(), + "ingresses", + lbc.namespace, + fields.Everything()), + &extensions.Ingress{}, + lbc.resync, + handlers, + ) +} - if input.NginxConfigMaps != "" { - nginxConfigMapsNS, nginxConfigMapsName, err := ParseNamespaceName(input.NginxConfigMaps) - if err != nil { - glog.Warning(err) - } else { - lbc.watchNginxConfigMaps = true - - cfgmHandlers := cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - cfgm := obj.(*api_v1.ConfigMap) - if cfgm.Name == nginxConfigMapsName { - glog.V(3).Infof("Adding ConfigMap: %v", cfgm.Name) - lbc.syncQueue.enqueue(obj) - } - }, - DeleteFunc: func(obj interface{}) { - cfgm, isCfgm := obj.(*api_v1.ConfigMap) - if !isCfgm { - deletedState, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - glog.V(3).Infof("Error received unexpected object: %v", obj) - return - } - cfgm, ok = deletedState.Obj.(*api_v1.ConfigMap) - if !ok { - glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-ConfigMap object: %v", deletedState.Obj) - return - } - } - if cfgm.Name == nginxConfigMapsName { - glog.V(3).Infof("Removing ConfigMap: %v", cfgm.Name) - lbc.syncQueue.enqueue(obj) - } - }, - UpdateFunc: func(old, cur interface{}) { - if !reflect.DeepEqual(old, cur) { - cfgm := cur.(*api_v1.ConfigMap) - if cfgm.Name == nginxConfigMapsName { - glog.V(3).Infof("ConfigMap %v changed, syncing", - cur.(*api_v1.ConfigMap).Name) - lbc.syncQueue.enqueue(cur) - } - } - }, - } - lbc.cfgmLister.Store, lbc.cfgmController = cache.NewInformer( - cache.NewListWatchFromClient(lbc.client.Core().RESTClient(), "configmaps", nginxConfigMapsNS, fields.Everything()), - &api_v1.ConfigMap{}, input.ResyncPeriod, cfgmHandlers) - } - } +// AddEndpointHandler adds the handler for endpoints to the controller +func (lbc *LoadBalancerController) AddEndpointHandler(handlers cache.ResourceEventHandlerFuncs) { + lbc.endpointLister.Store, lbc.endpointController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.client.Core().RESTClient(), + "endpoints", + lbc.namespace, + fields.Everything()), + &api_v1.Endpoints{}, + lbc.resync, + handlers, + ) +} - return &lbc +// AddConfigMapHandler adds the handler for config maps to the controller +func (lbc *LoadBalancerController) AddConfigMapHandler(handlers cache.ResourceEventHandlerFuncs, namespace string) { + lbc.configMapLister.Store, lbc.configMapController = cache.NewInformer( + cache.NewListWatchFromClient( + lbc.client.Core().RESTClient(), + "configmaps", + namespace, + fields.Everything()), + &api_v1.ConfigMap{}, + lbc.resync, + handlers, + ) } -// hasChanges ignores Status or ResourceVersion changes -func hasChanges(oldIng *extensions.Ingress, curIng *extensions.Ingress) bool { - oldIng.Status.LoadBalancer.Ingress = curIng.Status.LoadBalancer.Ingress - oldIng.ResourceVersion = curIng.ResourceVersion - return !reflect.DeepEqual(oldIng, curIng) +// GetDefaultServerSecret returns the default server secret +func (lbc *LoadBalancerController) GetDefaultServerSecret() string { + return lbc.defaultServerSecret } // Run starts the loadbalancer controller @@ -406,31 +246,31 @@ func (lbc *LoadBalancerController) Run() { if lbc.leaderElector != nil { go lbc.leaderElector.Run() } - go lbc.svcController.Run(lbc.stopCh) - go lbc.endpController.Run(lbc.stopCh) - go lbc.secrController.Run(lbc.stopCh) + go lbc.svcController.Run(lbc.stop) + go lbc.endpointController.Run(lbc.stop) + go lbc.secretController.Run(lbc.stop) if lbc.watchNginxConfigMaps { - go lbc.cfgmController.Run(lbc.stopCh) + go lbc.configMapController.Run(lbc.stop) } - go lbc.ingController.Run(lbc.stopCh) - go lbc.syncQueue.run(time.Second, lbc.stopCh) - <-lbc.stopCh + go lbc.ingressController.Run(lbc.stop) + go lbc.syncQueue.Run(time.Second, lbc.stop) + <-lbc.stop } // Stop shutdowns the load balancer controller func (lbc *LoadBalancerController) Stop() { - close(lbc.stopCh) + close(lbc.stop) - lbc.syncQueue.shutdown() + lbc.syncQueue.Shutdown() } -func (lbc *LoadBalancerController) syncEndp(task Task) { +func (lbc *LoadBalancerController) syncEndpoint(task queue.Task) { key := task.Key glog.V(3).Infof("Syncing endpoints %v", key) - obj, endpExists, err := lbc.endpLister.GetByKey(key) + obj, endpExists, err := lbc.endpointLister.GetByKey(key) if err != nil { - lbc.syncQueue.requeue(task, err) + lbc.syncQueue.Requeue(task, err) return } @@ -438,16 +278,16 @@ func (lbc *LoadBalancerController) syncEndp(task Task) { ings := lbc.getIngressForEndpoints(obj) for _, ing := range ings { - if !lbc.isNginxIngress(&ing) { + if !lbc.IsNginxIngress(&ing) { continue } - if isMinion(&ing) { - master, err := lbc.findMasterForMinion(&ing) + if utils.IsMinion(&ing) { + master, err := lbc.FindMasterForMinion(&ing) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", ing.Name, err) continue } - if !lbc.cnf.HasIngress(master) { + if !lbc.configurator.HasIngress(master) { continue } mergeableIngresses, err := lbc.createMergableIngresses(master) @@ -457,13 +297,13 @@ func (lbc *LoadBalancerController) syncEndp(task Task) { } glog.V(3).Infof("Updating Endpoints for %v/%v", ing.Namespace, ing.Name) - err = lbc.cnf.UpdateEndpointsMergeableIngress(mergeableIngresses) + err = lbc.configurator.UpdateEndpointsMergeableIngress(mergeableIngresses) if err != nil { glog.Errorf("Error updating endpoints for %v/%v: %v", ing.Namespace, ing.Name, err) } continue } - if !lbc.cnf.HasIngress(&ing) { + if !lbc.configurator.HasIngress(&ing) { continue } ingEx, err := lbc.createIngress(&ing) @@ -472,7 +312,7 @@ func (lbc *LoadBalancerController) syncEndp(task Task) { continue } glog.V(3).Infof("Updating Endpoints for %v/%v", ing.Namespace, ing.Name) - err = lbc.cnf.UpdateEndpoints(ingEx) + err = lbc.configurator.UpdateEndpoints(ingEx) if err != nil { glog.Errorf("Error updating endpoints for %v/%v: %v", ing.Namespace, ing.Name, err) } @@ -480,25 +320,25 @@ func (lbc *LoadBalancerController) syncEndp(task Task) { } } -func (lbc *LoadBalancerController) syncCfgm(task Task) { +func (lbc *LoadBalancerController) syncConfig(task queue.Task) { key := task.Key glog.V(3).Infof("Syncing configmap %v", key) - obj, cfgmExists, err := lbc.cfgmLister.GetByKey(key) + obj, configExists, err := lbc.configMapLister.GetByKey(key) if err != nil { - lbc.syncQueue.requeue(task, err) + lbc.syncQueue.Requeue(task, err) return } cfg := nginx.NewDefaultConfig() - if cfgmExists { + if configExists { cfgm := obj.(*api_v1.ConfigMap) - cfg = nginx.ParseConfigMap(cfgm, lbc.nginxPlus) + cfg = nginx.ParseConfigMap(cfgm, lbc.isNginxPlus) lbc.statusUpdater.SaveStatusFromExternalStatus(cfgm.Data["external-status-address"]) } - ingresses, mergeableIngresses := lbc.getManagedIngresses() + ingresses, mergeableIngresses := lbc.GetManagedIngresses() ingExes := lbc.ingressesToIngressExes(ingresses) if lbc.reportStatusEnabled() { @@ -508,59 +348,52 @@ func (lbc *LoadBalancerController) syncCfgm(task Task) { } } - if err := lbc.cnf.UpdateConfig(cfg, ingExes, mergeableIngresses); err != nil { - if cfgmExists { - cfgm := obj.(*api_v1.ConfigMap) - lbc.recorder.Eventf(cfgm, api_v1.EventTypeWarning, "UpdatedWithError", "Configuration from %v was updated, but not applied: %v", key, err) - } - for _, ingEx := range ingExes { - lbc.recorder.Eventf(ingEx.Ingress, api_v1.EventTypeWarning, "UpdatedWithError", "Configuration for %v/%v was updated, but not applied: %v", - ingEx.Ingress.Namespace, ingEx.Ingress.Name, err) - } - for _, mergeableIng := range mergeableIngresses { - master := mergeableIng.Master - lbc.recorder.Eventf(master.Ingress, api_v1.EventTypeWarning, "UpdatedWithError", "Configuration for %v/%v(Master) was updated, but not applied: %v", - master.Ingress.Namespace, master.Ingress.Name, err) - for _, minion := range mergeableIng.Minions { - lbc.recorder.Eventf(minion.Ingress, api_v1.EventTypeWarning, "UpdatedWithError", "Configuration for %v/%v(Minion) was updated, but not applied: %v", - minion.Ingress.Namespace, minion.Ingress.Name, err) - } - } - } else { - if cfgmExists { - cfgm := obj.(*api_v1.ConfigMap) - lbc.recorder.Eventf(cfgm, api_v1.EventTypeNormal, "Updated", "Configuration from %v was updated", key) - } - for _, ingEx := range ingExes { - lbc.recorder.Eventf(ingEx.Ingress, api_v1.EventTypeNormal, "Updated", "Configuration for %v/%v was updated", ingEx.Ingress.Namespace, ingEx.Ingress.Name) - } - for _, mergeableIng := range mergeableIngresses { - master := mergeableIng.Master - lbc.recorder.Eventf(master.Ingress, api_v1.EventTypeWarning, "Updated", "Configuration for %v/%v(Master) was updated", master.Ingress.Namespace, master.Ingress.Name) - for _, minion := range mergeableIng.Minions { - lbc.recorder.Eventf(minion.Ingress, api_v1.EventTypeWarning, "Updated", "Configuration for %v/%v(Minion) was updated", minion.Ingress.Namespace, minion.Ingress.Name) - } + updateErr := lbc.configurator.UpdateConfig(cfg, ingExes, mergeableIngresses) + + eventTitle := "Updated" + eventType := api_v1.EventTypeNormal + eventWarningMessage := "" + + if updateErr != nil { + eventTitle = "UpdatedWithError" + eventType = api_v1.EventTypeWarning + eventWarningMessage = "but was not applied" + } + if configExists { + cfgm := obj.(*api_v1.ConfigMap) + lbc.recorder.Eventf(cfgm, eventType, eventTitle, "Configuration from %v was updated %s: %v", key, eventWarningMessage, err) + } + for _, ingEx := range ingExes { + lbc.recorder.Eventf(ingEx.Ingress, eventType, eventTitle, "Configuration for %v/%v was updated %s: %v", + ingEx.Ingress.Namespace, ingEx.Ingress.Name, eventWarningMessage, err) + } + for _, mergeableIng := range mergeableIngresses { + master := mergeableIng.Master + lbc.recorder.Eventf(master.Ingress, eventType, eventTitle, "Configuration for %v/%v(Master) was updated %s: %v", master.Ingress.Namespace, master.Ingress.Name, eventWarningMessage, err) + for _, minion := range mergeableIng.Minions { + lbc.recorder.Eventf(minion.Ingress, eventType, eventTitle, "Configuration for %v/%v(Minion) was updated %s: %v", + minion.Ingress.Namespace, minion.Ingress.Name, eventWarningMessage, err) } } } -// getManagedIngresses gets Ingress resources that the IC is currently responsible for -func (lbc *LoadBalancerController) getManagedIngresses() ([]extensions.Ingress, map[string]*nginx.MergeableIngresses) { +// GetManagedIngresses gets Ingress resources that the IC is currently responsible for +func (lbc *LoadBalancerController) GetManagedIngresses() ([]extensions.Ingress, map[string]*nginx.MergeableIngresses) { mergeableIngresses := make(map[string]*nginx.MergeableIngresses) var managedIngresses []extensions.Ingress - ings, _ := lbc.ingLister.List() + ings, _ := lbc.ingressLister.List() for i := range ings.Items { ing := ings.Items[i] - if !lbc.isNginxIngress(&ing) { + if !lbc.IsNginxIngress(&ing) { continue } - if isMinion(&ing) { - master, err := lbc.findMasterForMinion(&ing) + if utils.IsMinion(&ing) { + master, err := lbc.FindMasterForMinion(&ing) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", ing, err) continue } - if !lbc.cnf.HasIngress(master) { + if !lbc.configurator.HasIngress(master) { continue } if _, exists := mergeableIngresses[master.Name]; !exists { @@ -573,7 +406,7 @@ func (lbc *LoadBalancerController) getManagedIngresses() ([]extensions.Ingress, } continue } - if !lbc.cnf.HasIngress(&ing) { + if !lbc.configurator.HasIngress(&ing) { continue } managedIngresses = append(managedIngresses, ing) @@ -593,33 +426,33 @@ func (lbc *LoadBalancerController) ingressesToIngressExes(ings []extensions.Ingr return ingExes } -func (lbc *LoadBalancerController) sync(task Task) { +func (lbc *LoadBalancerController) sync(task queue.Task) { glog.V(3).Infof("Syncing %v", task.Key) switch task.Kind { - case Ingress: + case queue.Ingress: lbc.syncIng(task) - case IngressMinion: + case queue.IngressMinion: lbc.syncIngMinion(task) - case ConfigMap: - lbc.syncCfgm(task) + case queue.ConfigMap: + lbc.syncConfig(task) return - case Endpoints: - lbc.syncEndp(task) + case queue.Endpoints: + lbc.syncEndpoint(task) return - case Secret: + case queue.Secret: lbc.syncSecret(task) return - case Service: + case queue.Service: lbc.syncExternalService(task) } } -func (lbc *LoadBalancerController) syncIngMinion(task Task) { +func (lbc *LoadBalancerController) syncIngMinion(task queue.Task) { key := task.Key - obj, ingExists, err := lbc.ingLister.Store.GetByKey(key) + obj, ingExists, err := lbc.ingressLister.Store.GetByKey(key) if err != nil { - lbc.syncQueue.requeue(task, err) + lbc.syncQueue.Requeue(task, err) return } @@ -631,45 +464,45 @@ func (lbc *LoadBalancerController) syncIngMinion(task Task) { minion := obj.(*extensions.Ingress) - master, err := lbc.findMasterForMinion(minion) + master, err := lbc.FindMasterForMinion(minion) if err != nil { - lbc.syncQueue.requeueAfter(task, err, 5*time.Second) + lbc.syncQueue.RequeueAfter(task, err, 5*time.Second) return } _, err = lbc.createIngress(minion) if err != nil { - lbc.syncQueue.requeueAfter(task, err, 5*time.Second) - if !lbc.cnf.HasMinion(master, minion) { + lbc.syncQueue.RequeueAfter(task, err, 5*time.Second) + if !lbc.configurator.HasMinion(master, minion) { return } } - lbc.syncQueue.enqueue(master) + lbc.syncQueue.Enqueue(master) } -func (lbc *LoadBalancerController) syncIng(task Task) { +func (lbc *LoadBalancerController) syncIng(task queue.Task) { key := task.Key - ing, ingExists, err := lbc.ingLister.GetByKeySafe(key) + ing, ingExists, err := lbc.ingressLister.GetByKeySafe(key) if err != nil { - lbc.syncQueue.requeue(task, err) + lbc.syncQueue.Requeue(task, err) return } if !ingExists { glog.V(2).Infof("Deleting Ingress: %v\n", key) - err := lbc.cnf.DeleteIngress(key) + err := lbc.configurator.DeleteIngress(key) if err != nil { glog.Errorf("Error when deleting configuration for %v: %v", key, err) } } else { glog.V(2).Infof("Adding or Updating Ingress: %v\n", key) - if isMaster(ing) { + if utils.IsMaster(ing) { mergeableIngExs, err := lbc.createMergableIngresses(ing) if err != nil { - lbc.syncQueue.requeueAfter(task, err, 5*time.Second) + lbc.syncQueue.RequeueAfter(task, err, 5*time.Second) lbc.recorder.Eventf(ing, api_v1.EventTypeWarning, "Rejected", "%v was rejected: %v", key, err) if lbc.reportStatusEnabled() { err = lbc.statusUpdater.ClearIngressStatus(*ing) @@ -679,29 +512,34 @@ func (lbc *LoadBalancerController) syncIng(task Task) { } return } - err = lbc.cnf.AddOrUpdateMergeableIngress(mergeableIngExs) - if err != nil { - lbc.recorder.Eventf(ing, api_v1.EventTypeWarning, "AddedOrUpdatedWithError", "Configuration for %v(Master) was added or updated, but not applied: %v", key, err) - for _, minion := range mergeableIngExs.Minions { - lbc.recorder.Eventf(ing, api_v1.EventTypeWarning, "AddedOrUpdatedWithError", "Configuration for %v/%v(Minion) was added or updated, but not applied: %v", minion.Ingress.Namespace, minion.Ingress.Name, err) - } - } else { - lbc.recorder.Eventf(ing, api_v1.EventTypeNormal, "AddedOrUpdated", "Configuration for %v(Master) was added or updated", key) - for _, minion := range mergeableIngExs.Minions { - lbc.recorder.Eventf(ing, api_v1.EventTypeNormal, "AddedOrUpdated", "Configuration for %v/%v(Minion) was added or updated", minion.Ingress.Namespace, minion.Ingress.Name) - } + addErr := lbc.configurator.AddOrUpdateMergeableIngress(mergeableIngExs) + + // record correct eventType and message depending on the error + eventTitle := "AddedOrUpdated" + eventType := api_v1.EventTypeNormal + eventWarningMessage := "" + + if addErr != nil { + eventTitle = "AddedOrUpdatedWithError" + eventType = api_v1.EventTypeWarning + eventWarningMessage = "but was not applied" } + lbc.recorder.Eventf(ing, eventType, eventTitle, "Configuration for %v(Master) was added or updated %s: %v", key, eventWarningMessage, err) + for _, minion := range mergeableIngExs.Minions { + lbc.recorder.Eventf(ing, eventType, eventTitle, "Configuration for %v/%v(Minion) was added or updated %s: %v", minion.Ingress.Namespace, minion.Ingress.Name, eventWarningMessage, err) + } + if lbc.reportStatusEnabled() { err = lbc.statusUpdater.UpdateMergableIngresses(mergeableIngExs) if err != nil { - glog.V(3).Infof("error updating ing status: %v", err) + glog.V(3).Infof("error updating ingress status: %v", err) } } return } ingEx, err := lbc.createIngress(ing) if err != nil { - lbc.syncQueue.requeueAfter(task, err, 5*time.Second) + lbc.syncQueue.RequeueAfter(task, err, 5*time.Second) lbc.recorder.Eventf(ing, api_v1.EventTypeWarning, "Rejected", "%v was rejected: %v", key, err) if lbc.reportStatusEnabled() { err = lbc.statusUpdater.ClearIngressStatus(*ing) @@ -712,7 +550,7 @@ func (lbc *LoadBalancerController) syncIng(task Task) { return } - err = lbc.cnf.AddOrUpdateIngress(ingEx) + err = lbc.configurator.AddOrUpdateIngress(ingEx) if err != nil { lbc.recorder.Eventf(ing, api_v1.EventTypeWarning, "AddedOrUpdatedWithError", "Configuration for %v was added or updated, but not applied: %v", key, err) } else { @@ -729,14 +567,14 @@ func (lbc *LoadBalancerController) syncIng(task Task) { // syncExternalService does not sync all services. // We only watch the Service specified by the external-service flag. -func (lbc *LoadBalancerController) syncExternalService(task Task) { +func (lbc *LoadBalancerController) syncExternalService(task queue.Task) { key := task.Key obj, exists, err := lbc.svcLister.GetByKey(key) if err != nil { - lbc.syncQueue.requeue(task, err) + lbc.syncQueue.Requeue(task, err) return } - statusIngs, mergableIngs := lbc.getManagedIngresses() + statusIngs, mergableIngs := lbc.GetManagedIngresses() if !exists { // service got removed lbc.statusUpdater.ClearStatusFromExternalService() @@ -752,15 +590,15 @@ func (lbc *LoadBalancerController) syncExternalService(task Task) { } } -// isExternalServiceForStatus matches the service specified by the external-service arg -func (lbc *LoadBalancerController) isExternalServiceForStatus(svc *api_v1.Service) bool { +// IsExternalServiceForStatus matches the service specified by the external-service arg +func (lbc *LoadBalancerController) IsExternalServiceForStatus(svc *api_v1.Service) bool { return lbc.statusUpdater.namespace == svc.Namespace && lbc.statusUpdater.externalServiceName == svc.Name } // reportStatusEnabled determines if we should attempt to report status func (lbc *LoadBalancerController) reportStatusEnabled() bool { if lbc.reportIngressStatus { - if lbc.leaderElectionEnabled { + if lbc.isLeaderElectionEnabled { return lbc.leaderElector != nil && lbc.leaderElector.IsLeader() } return true @@ -768,15 +606,15 @@ func (lbc *LoadBalancerController) reportStatusEnabled() bool { return false } -func (lbc *LoadBalancerController) syncSecret(task Task) { +func (lbc *LoadBalancerController) syncSecret(task queue.Task) { key := task.Key - obj, secrExists, err := lbc.secrLister.Store.GetByKey(key) + obj, secrExists, err := lbc.secretLister.Store.GetByKey(key) if err != nil { - lbc.syncQueue.requeue(task, err) + lbc.syncQueue.Requeue(task, err) return } - namespace, name, err := ParseNamespaceName(key) + namespace, name, err := utils.ParseNamespaceName(key) if err != nil { glog.Warningf("Secret key %v is invalid: %v", key, err) return @@ -785,7 +623,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { nonMinions, minions, err := lbc.findIngressesForSecret(namespace, name) if err != nil { glog.Warningf("Failed to find Ingress resources for Secret %v: %v", key, err) - lbc.syncQueue.requeueAfter(task, err, 5*time.Second) + lbc.syncQueue.RequeueAfter(task, err, 5*time.Second) } glog.V(2).Infof("Found %v Non-Minion and %v Minion Ingress resources with Secret %v", len(nonMinions), len(minions), key) @@ -794,7 +632,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { glog.V(2).Infof("Deleting Secret: %v\n", key) for _, minion := range minions { - master, err := lbc.findMasterForMinion(&minion) + master, err := lbc.FindMasterForMinion(&minion) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", minion.Name, err) continue @@ -804,21 +642,21 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { glog.Errorf("Ignoring Ingress %v(Minion): %v", minion.Name, err) continue } - err = lbc.cnf.AddOrUpdateMergeableIngress(mergeableIngress) + err = lbc.configurator.AddOrUpdateMergeableIngress(mergeableIngress) if err != nil { glog.Errorf("Failed to update Ingress %v(Master) of %v(Minion): %v", master.Name, minion.Name, err) } lbc.recorder.Eventf(&minion, api_v1.EventTypeWarning, "Rejected", "%v/%v was rejected due to deleted Secret %v: %v", minion.Namespace, minion.Name, key) lbc.recorder.Eventf(master, api_v1.EventTypeWarning, "Rejected", "Minion %v/%v was rejected due to deleted Secret %v: %v", minion.Namespace, minion.Name, key) - lbc.syncQueue.enqueue(&minion) + lbc.syncQueue.Enqueue(&minion) } - if err := lbc.cnf.DeleteSecret(key, nonMinions); err != nil { + if err := lbc.configurator.DeleteSecret(key, nonMinions); err != nil { glog.Errorf("Error when deleting Secret: %v: %v", key, err) } for _, ing := range nonMinions { - lbc.syncQueue.enqueue(&ing) + lbc.syncQueue.Enqueue(&ing) lbc.recorder.Eventf(&ing, api_v1.EventTypeWarning, "Rejected", "%v/%v was rejected due to deleted Secret %v", ing.Namespace, ing.Name, key) } @@ -836,7 +674,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { glog.Errorf("Couldn't validate the default server Secret %v: %v", key, err) lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "Rejected", "the default server Secret %v was rejected, using the previous version: %v", key, err) } else { - err := lbc.cnf.AddOrUpdateDefaultServerTLSSecret(secret) + err := lbc.configurator.AddOrUpdateDefaultServerTLSSecret(secret) if err != nil { glog.Errorf("Error when updating the default server Secret %v: %v", key, err) lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "UpdatedWithError", "the default server Secret %v was updated, but not applied: %v", key, err) @@ -854,7 +692,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { glog.Errorf("Couldn't validate secret %v: %v", key, err) for _, minion := range minions { - master, err := lbc.findMasterForMinion(&minion) + master, err := lbc.FindMasterForMinion(&minion) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", minion.Name, err) continue @@ -864,34 +702,34 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { glog.Errorf("Ignoring Ingress %v(Minion): %v", minion.Name, err) continue } - err = lbc.cnf.AddOrUpdateMergeableIngress(mergeableIngress) + err = lbc.configurator.AddOrUpdateMergeableIngress(mergeableIngress) if err != nil { glog.Errorf("Failed to update Ingress %v(Master) of %v(Minion): %v", master.Name, minion.Name, err) } lbc.recorder.Eventf(&minion, api_v1.EventTypeWarning, "Rejected", "%v/%v was rejected due to invalid Secret %v: %v", minion.Namespace, minion.Name, key, err) lbc.recorder.Eventf(master, api_v1.EventTypeWarning, "Rejected", "Minion %v/%v was rejected due to invalid Secret %v: %v", minion.Namespace, minion.Name, key, err) - lbc.syncQueue.enqueue(&minion) + lbc.syncQueue.Enqueue(&minion) } - if err := lbc.cnf.DeleteSecret(key, nonMinions); err != nil { + if err := lbc.configurator.DeleteSecret(key, nonMinions); err != nil { glog.Errorf("Error when deleting Secret: %v: %v", key, err) } for _, ing := range nonMinions { - lbc.syncQueue.enqueue(&ing) + lbc.syncQueue.Enqueue(&ing) lbc.recorder.Eventf(&ing, api_v1.EventTypeWarning, "Rejected", "%v/%v was rejected due to invalid Secret %v: %v", ing.Namespace, ing.Name, key, err) } lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "Rejected", "%v was rejected: %v", key, err) return } - if err := lbc.cnf.AddOrUpdateSecret(secret); err != nil { + if err := lbc.configurator.AddOrUpdateSecret(secret); err != nil { glog.Errorf("Error when updating Secret %v: %v", key, err) lbc.recorder.Eventf(secret, api_v1.EventTypeWarning, "UpdatedWithError", "%v was updated, but not applied: %v", key, err) for _, ing := range nonMinions { lbc.recorder.Eventf(&ing, api_v1.EventTypeWarning, "UpdatedWithError", "Configuration for %v/%v was updated, but not applied: %v", ing.Namespace, ing.Name, err) } for _, minion := range minions { - master, err := lbc.findMasterForMinion(&minion) + master, err := lbc.FindMasterForMinion(&minion) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", minion.Name, err) continue @@ -905,7 +743,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { lbc.recorder.Eventf(&ing, api_v1.EventTypeNormal, "Updated", "Configuration for %v/%v was updated", ing.Namespace, ing.Name) } for _, minion := range minions { - master, err := lbc.findMasterForMinion(&minion) + master, err := lbc.FindMasterForMinion(&minion) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", minion.Name, err) continue @@ -919,7 +757,7 @@ func (lbc *LoadBalancerController) syncSecret(task Task) { } func (lbc *LoadBalancerController) findIngressesForSecret(secretNamespace string, secretName string) (nonMinions []extensions.Ingress, minions []extensions.Ingress, error error) { - ings, err := lbc.ingLister.List() + ings, err := lbc.ingressLister.List() if err != nil { return nil, nil, fmt.Errorf("Couldn't get the list of Ingress resources: %v", err) } @@ -930,12 +768,12 @@ items: continue } - if !lbc.isNginxIngress(&ing) { + if !lbc.IsNginxIngress(&ing) { continue } - if !isMinion(&ing) { - if !lbc.cnf.HasIngress(&ing) { + if !utils.IsMinion(&ing) { + if !lbc.configurator.HasIngress(&ing) { continue } for _, tls := range ing.Spec.TLS { @@ -944,7 +782,7 @@ items: continue items } } - if lbc.nginxPlus { + if lbc.isNginxPlus { if jwtKey, exists := ing.Annotations[nginx.JWTKeyAnnotation]; exists { if jwtKey == secretName { nonMinions = append(nonMinions, ing) @@ -956,14 +794,14 @@ items: // we're dealing with a minion // minions can only have JWT secrets - if lbc.nginxPlus { - master, err := lbc.findMasterForMinion(&ing) + if lbc.isNginxPlus { + master, err := lbc.FindMasterForMinion(&ing) if err != nil { glog.Infof("Ignoring Ingress %v(Minion): %v", ing.Name, err) continue } - if !lbc.cnf.HasMinion(master, &ing) { + if !lbc.configurator.HasMinion(master, &ing) { continue } @@ -978,30 +816,31 @@ items: return nonMinions, minions, nil } -func (lbc *LoadBalancerController) enqueueIngressForService(svc *api_v1.Service) { +// EnqueueIngressForService enqueues the ingress for the given service +func (lbc *LoadBalancerController) EnqueueIngressForService(svc *api_v1.Service) { ings := lbc.getIngressesForService(svc) for _, ing := range ings { - if !lbc.isNginxIngress(&ing) { + if !lbc.IsNginxIngress(&ing) { continue } - if isMinion(&ing) { - master, err := lbc.findMasterForMinion(&ing) + if utils.IsMinion(&ing) { + master, err := lbc.FindMasterForMinion(&ing) if err != nil { glog.Errorf("Ignoring Ingress %v(Minion): %v", ing.Name, err) continue } ing = *master } - if !lbc.cnf.HasIngress(&ing) { + if !lbc.configurator.HasIngress(&ing) { continue } - lbc.syncQueue.enqueue(&ing) + lbc.syncQueue.Enqueue(&ing) } } func (lbc *LoadBalancerController) getIngressesForService(svc *api_v1.Service) []extensions.Ingress { - ings, err := lbc.ingLister.GetServiceIngress(svc) + ings, err := lbc.ingressLister.GetServiceIngress(svc) if err != nil { glog.V(3).Infof("ignoring service %v: %v", svc.Name, err) return nil @@ -1043,7 +882,7 @@ func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) (*ngin ingEx.TLSSecrets[secretName] = secret } - if lbc.nginxPlus { + if lbc.isNginxPlus { if jwtKey, exists := ingEx.Ingress.Annotations[nginx.JWTKeyAnnotation]; exists { secretName := jwtKey @@ -1072,7 +911,7 @@ func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) (*ngin } else { ingEx.Endpoints[ing.Spec.Backend.ServiceName+ing.Spec.Backend.ServicePort.String()] = endps } - if lbc.nginxPlus && lbc.isHealthCheckEnabled(ing) { + if lbc.isNginxPlus && lbc.isHealthCheckEnabled(ing) { healthCheck := lbc.getHealthChecksForIngressBackend(ing.Spec.Backend, ing.Namespace) if healthCheck != nil { ingEx.HealthChecks[ing.Spec.Backend.ServiceName+ing.Spec.Backend.ServicePort.String()] = healthCheck @@ -1098,7 +937,7 @@ func (lbc *LoadBalancerController) createIngress(ing *extensions.Ingress) (*ngin } else { ingEx.Endpoints[path.Backend.ServiceName+path.Backend.ServicePort.String()] = endps } - if lbc.nginxPlus && lbc.isHealthCheckEnabled(ing) { + if lbc.isNginxPlus && lbc.isHealthCheckEnabled(ing) { // Pull active health checks from k8 api healthCheck := lbc.getHealthChecksForIngressBackend(&path.Backend, ing.Namespace) if healthCheck != nil { @@ -1180,7 +1019,7 @@ func (lbc *LoadBalancerController) getEndpointsForIngressBackend(backend *extens return nil, err } - endps, err := lbc.endpLister.GetServiceEndpoints(svc) + endps, err := lbc.endpointLister.GetServiceEndpoints(svc) if err != nil { glog.V(3).Infof("Error getting endpoints for service %s from the cache: %v", svc.Name, err) return nil, err @@ -1259,7 +1098,7 @@ func (lbc *LoadBalancerController) getTargetPort(svcPort *api_v1.ServicePort, sv pod := &pods.Items[0] - portNum, err := FindPort(pod, svcPort) + portNum, err := utils.FindPort(pod, svcPort) if err != nil { return 0, fmt.Errorf("Error finding named port %v in pod %s: %v", svcPort, pod.Name, err) } @@ -1281,19 +1120,9 @@ func (lbc *LoadBalancerController) getServiceForIngressBackend(backend *extensio return nil, fmt.Errorf("service %s doesn't exists", svcKey) } -// ParseNamespaceName parses the string in the / format and returns the name and the namespace. -// It returns an error in case the string does not follow the / format. -func ParseNamespaceName(value string) (ns string, name string, err error) { - res := strings.Split(value, "/") - if len(res) != 2 { - return "", "", fmt.Errorf("%q must follow the format /", value) - } - return res[0], res[1], nil -} - -// Check if resource ingress class annotation (if exists) is matching with ingress controller class +// IsNginxIngress checks if resource ingress class annotation (if exists) is matching with ingress controller class // If annotation is absent and use-ingress-class-only enabled - ingress resource would ignore -func (lbc *LoadBalancerController) isNginxIngress(ing *extensions.Ingress) bool { +func (lbc *LoadBalancerController) IsNginxIngress(ing *extensions.Ingress) bool { if class, exists := ing.Annotations[ingressClassKey]; exists { if lbc.useIngressClassOnly { return class == lbc.ingressClass @@ -1319,7 +1148,7 @@ func (lbc *LoadBalancerController) isHealthCheckEnabled(ing *extensions.Ingress) // For NGINX Plus, it also checks if the secret follows the JWK Secret format. func (lbc *LoadBalancerController) ValidateSecret(secret *api_v1.Secret) error { err1 := nginx.ValidateTLSSecret(secret) - if !lbc.nginxPlus { + if !lbc.isNginxPlus { return err1 } @@ -1334,7 +1163,7 @@ func (lbc *LoadBalancerController) ValidateSecret(secret *api_v1.Secret) error { // getMinionsForHost returns a list of all minion ingress resources for a given master func (lbc *LoadBalancerController) getMinionsForMaster(master *nginx.IngressEx) ([]*nginx.IngressEx, error) { - ings, err := lbc.ingLister.List() + ings, err := lbc.ingressLister.List() if err != nil { return []*nginx.IngressEx{}, err } @@ -1347,11 +1176,11 @@ func (lbc *LoadBalancerController) getMinionsForMaster(master *nginx.IngressEx) var minions []*nginx.IngressEx var minionPaths = make(map[string]*extensions.Ingress) - for i, _ := range ings.Items { - if !lbc.isNginxIngress(&ings.Items[i]) { + for i := range ings.Items { + if !lbc.IsNginxIngress(&ings.Items[i]) { continue } - if !isMinion(&ings.Items[i]) { + if !utils.IsMinion(&ings.Items[i]) { continue } if ings.Items[i].Spec.Rules[0].Host != master.Ingress.Spec.Rules[0].Host { @@ -1394,21 +1223,21 @@ func (lbc *LoadBalancerController) getMinionsForMaster(master *nginx.IngressEx) return minions, nil } -// findMasterForHost returns a master for a given minion -func (lbc *LoadBalancerController) findMasterForMinion(minion *extensions.Ingress) (*extensions.Ingress, error) { - ings, err := lbc.ingLister.List() +// FindMasterForMinion returns a master for a given minion +func (lbc *LoadBalancerController) FindMasterForMinion(minion *extensions.Ingress) (*extensions.Ingress, error) { + ings, err := lbc.ingressLister.List() if err != nil { return &extensions.Ingress{}, err } - for i, _ := range ings.Items { - if !lbc.isNginxIngress(&ings.Items[i]) { + for i := range ings.Items { + if !lbc.IsNginxIngress(&ings.Items[i]) { continue } - if !lbc.cnf.HasIngress(&ings.Items[i]) { + if !lbc.configurator.HasIngress(&ings.Items[i]) { continue } - if !isMaster(&ings.Items[i]) { + if !utils.IsMaster(&ings.Items[i]) { continue } if ings.Items[i].Spec.Rules[0].Host != minion.Spec.Rules[0].Host { @@ -1460,17 +1289,3 @@ func (lbc *LoadBalancerController) createMergableIngresses(master *extensions.In return &mergeableIngresses, nil } - -func isMinion(ing *extensions.Ingress) bool { - if ing.Annotations["nginx.org/mergeable-ingress-type"] == "minion" { - return true - } - return false -} - -func isMaster(ing *extensions.Ingress) bool { - if ing.Annotations["nginx.org/mergeable-ingress-type"] == "master" { - return true - } - return false -} diff --git a/nginx-controller/controller/controller_test.go b/internal/controller/controller_test.go similarity index 92% rename from nginx-controller/controller/controller_test.go rename to internal/controller/controller_test.go index 604cf01e67..e406af7a19 100644 --- a/nginx-controller/controller/controller_test.go +++ b/internal/controller/controller_test.go @@ -8,8 +8,8 @@ import ( "time" "unsafe" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx/plus" + "github.com/nginxinc/kubernetes-ingress/internal/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/nginx/plus" "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -133,23 +133,23 @@ func TestIsNginxIngress(t *testing.T) { } for _, test := range testsWithoutIngressClassOnly { - if result := test.lbc.isNginxIngress(test.ing); result != test.expected { + if result := test.lbc.IsNginxIngress(test.ing); result != test.expected { classAnnotation := "N/A" if class, exists := test.ing.Annotations[ingressClassKey]; exists { classAnnotation = class } - t.Errorf("lbc.isNginxIngress(ing), lbc.ingressClass=%v, lbc.useIngressClassOnly=%v, ing.Annotations['%v']=%v; got %v, expected %v", + t.Errorf("lbc.IsNginxIngress(ing), lbc.ingressClass=%v, lbc.useIngressClassOnly=%v, ing.Annotations['%v']=%v; got %v, expected %v", test.lbc.ingressClass, test.lbc.useIngressClassOnly, ingressClassKey, classAnnotation, result, test.expected) } } for _, test := range testsWithIngressClassOnly { - if result := test.lbc.isNginxIngress(test.ing); result != test.expected { + if result := test.lbc.IsNginxIngress(test.ing); result != test.expected { classAnnotation := "N/A" if class, exists := test.ing.Annotations[ingressClassKey]; exists { classAnnotation = class } - t.Errorf("lbc.isNginxIngress(ing), lbc.ingressClass=%v, lbc.useIngressClassOnly=%v, ing.Annotations['%v']=%v; got %v, expected %v", + t.Errorf("lbc.IsNginxIngress(ing), lbc.ingressClass=%v, lbc.useIngressClassOnly=%v, ing.Annotations['%v']=%v; got %v, expected %v", test.lbc.ingressClass, test.lbc.useIngressClassOnly, ingressClassKey, classAnnotation, result, test.expected) } } @@ -159,9 +159,9 @@ func TestIsNginxIngress(t *testing.T) { func TestCreateMergableIngresses(t *testing.T) { cafeMaster, coffeeMinion, teaMinion, lbc := getMergableDefaults() - lbc.ingLister.Add(&cafeMaster) - lbc.ingLister.Add(&coffeeMinion) - lbc.ingLister.Add(&teaMinion) + lbc.ingressLister.Add(&cafeMaster) + lbc.ingressLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&teaMinion) mergeableIngresses, err := lbc.createMergableIngresses(&cafeMaster) if err != nil { @@ -220,7 +220,7 @@ func TestCreateMergableIngressesInvalidMaster(t *testing.T) { }, }, } - lbc.ingLister.Add(&cafeMaster) + lbc.ingressLister.Add(&cafeMaster) expected := fmt.Errorf("Ingress Resource %v/%v with the 'nginx.org/mergeable-ingress-type' annotation set to 'master' cannot contain Paths", cafeMaster.Namespace, cafeMaster.Name) _, err := lbc.createMergableIngresses(&cafeMaster) @@ -237,11 +237,11 @@ func TestFindMasterForMinion(t *testing.T) { Paths: []extensions.HTTPIngressPath{}, } - lbc.ingLister.Add(&cafeMaster) - lbc.ingLister.Add(&coffeeMinion) - lbc.ingLister.Add(&teaMinion) + lbc.ingressLister.Add(&cafeMaster) + lbc.ingressLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&teaMinion) - master, err := lbc.findMasterForMinion(&coffeeMinion) + master, err := lbc.FindMasterForMinion(&coffeeMinion) if err != nil { t.Errorf("Error finding master for %s(Minion): %v", coffeeMinion.Name, err) } @@ -249,7 +249,7 @@ func TestFindMasterForMinion(t *testing.T) { t.Errorf("Invalid Master found. Obtained %+v, Expected %+v", master, cafeMaster) } - master, err = lbc.findMasterForMinion(&teaMinion) + master, err = lbc.FindMasterForMinion(&teaMinion) if err != nil { t.Errorf("Error finding master for %s(Minion): %v", teaMinion.Name, err) } @@ -261,17 +261,17 @@ func TestFindMasterForMinion(t *testing.T) { func TestFindMasterForMinionNoMaster(t *testing.T) { _, coffeeMinion, teaMinion, lbc := getMergableDefaults() - lbc.ingLister.Add(&coffeeMinion) - lbc.ingLister.Add(&teaMinion) + lbc.ingressLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&teaMinion) expected := fmt.Errorf("Could not find a Master for Minion: '%v/%v'", coffeeMinion.Namespace, coffeeMinion.Name) - _, err := lbc.findMasterForMinion(&coffeeMinion) + _, err := lbc.FindMasterForMinion(&coffeeMinion) if !reflect.DeepEqual(err, expected) { t.Errorf("Expected: %s \nObtained: %s", expected, err) } expected = fmt.Errorf("Could not find a Master for Minion: '%v/%v'", teaMinion.Namespace, teaMinion.Name) - _, err = lbc.findMasterForMinion(&teaMinion) + _, err = lbc.FindMasterForMinion(&teaMinion) if !reflect.DeepEqual(err, expected) { t.Errorf("Error master found for %s(Minion): %v", teaMinion.Name, err) } @@ -291,10 +291,10 @@ func TestFindMasterForMinionInvalidMinion(t *testing.T) { }, } - lbc.ingLister.Add(&cafeMaster) - lbc.ingLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&cafeMaster) + lbc.ingressLister.Add(&coffeeMinion) - master, err := lbc.findMasterForMinion(&coffeeMinion) + master, err := lbc.FindMasterForMinion(&coffeeMinion) if err != nil { t.Errorf("Error finding master for %s(Minion): %v", coffeeMinion.Name, err) } @@ -311,9 +311,9 @@ func TestGetMinionsForMaster(t *testing.T) { Paths: []extensions.HTTPIngressPath{}, } - lbc.ingLister.Add(&cafeMaster) - lbc.ingLister.Add(&coffeeMinion) - lbc.ingLister.Add(&teaMinion) + lbc.ingressLister.Add(&cafeMaster) + lbc.ingressLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&teaMinion) cafeMasterIngEx, err := lbc.createIngress(&cafeMaster) if err != nil { @@ -364,9 +364,9 @@ func TestGetMinionsForMasterInvalidMinion(t *testing.T) { }, } - lbc.ingLister.Add(&cafeMaster) - lbc.ingLister.Add(&coffeeMinion) - lbc.ingLister.Add(&teaMinion) + lbc.ingressLister.Add(&cafeMaster) + lbc.ingressLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&teaMinion) cafeMasterIngEx, err := lbc.createIngress(&cafeMaster) if err != nil { @@ -421,9 +421,9 @@ func TestGetMinionsForMasterConflictingPaths(t *testing.T) { }, }) - lbc.ingLister.Add(&cafeMaster) - lbc.ingLister.Add(&coffeeMinion) - lbc.ingLister.Add(&teaMinion) + lbc.ingressLister.Add(&cafeMaster) + lbc.ingressLister.Add(&coffeeMinion) + lbc.ingressLister.Add(&teaMinion) cafeMasterIngEx, err := lbc.createIngress(&cafeMaster) if err != nil { @@ -549,7 +549,7 @@ func getMergableDefaults() (cafeMaster, coffeeMinion, teaMinion extensions.Ingre cafeMasterIngEx, _ := lbc.createIngress(&cafeMaster) ingExMap["default-cafe-master"] = cafeMasterIngEx - cnf := nginx.NewConfigurator(&nginx.NginxController{}, &nginx.Config{}, &plus.NginxAPIController{}, &nginx.TemplateExecutor{}) + cnf := nginx.NewConfigurator(&nginx.Controller{}, &nginx.Config{}, &plus.NginxAPIController{}, &nginx.TemplateExecutor{}) // edit private field ingresses to use in testing pointerVal := reflect.ValueOf(cnf) @@ -564,12 +564,12 @@ func getMergableDefaults() (cafeMaster, coffeeMinion, teaMinion extensions.Ingre lbc = LoadBalancerController{ client: fakeClient, ingressClass: "nginx", - cnf: cnf, + configurator: cnf, } lbc.svcLister, _ = cache.NewInformer( cache.NewListWatchFromClient(lbc.client.ExtensionsV1beta1().RESTClient(), "services", "default", fields.Everything()), &extensions.Ingress{}, time.Duration(1), nil) - lbc.ingLister.Store, _ = cache.NewInformer( + lbc.ingressLister.Store, _ = cache.NewInformer( cache.NewListWatchFromClient(lbc.client.ExtensionsV1beta1().RESTClient(), "ingresses", "default", fields.Everything()), &extensions.Ingress{}, time.Duration(1), nil) coffeeService := v1.Service{ @@ -782,11 +782,11 @@ func TestFindProbeForPods(t *testing.T) { func TestGetServicePortForIngressPort(t *testing.T) { fakeClient := fake.NewSimpleClientset() - cnf := nginx.NewConfigurator(&nginx.NginxController{}, &nginx.Config{}, &plus.NginxAPIController{}, &nginx.TemplateExecutor{}) + cnf := nginx.NewConfigurator(&nginx.Controller{}, &nginx.Config{}, &plus.NginxAPIController{}, &nginx.TemplateExecutor{}) lbc := LoadBalancerController{ client: fakeClient, ingressClass: "nginx", - cnf: cnf, + configurator: cnf, } svc := v1.Service{ TypeMeta: meta_v1.TypeMeta{}, @@ -940,15 +940,15 @@ func TestFindIngressesForSecret(t *testing.T) { lbc := LoadBalancerController{ client: fakeClient, ingressClass: "nginx", - cnf: cnf, - nginxPlus: true, + configurator: cnf, + isNginxPlus: true, } - lbc.ingLister.Store, _ = cache.NewInformer( + lbc.ingressLister.Store, _ = cache.NewInformer( cache.NewListWatchFromClient(lbc.client.ExtensionsV1beta1().RESTClient(), "ingresses", "default", fields.Everything()), &extensions.Ingress{}, time.Duration(1), nil) - lbc.secrLister.Store, lbc.secrController = cache.NewInformer( + lbc.secretLister.Store, lbc.secretController = cache.NewInformer( cache.NewListWatchFromClient(lbc.client.Core().RESTClient(), "secrets", "default", fields.Everything()), &v1.Secret{}, time.Duration(1), nil) @@ -964,8 +964,8 @@ func TestFindIngressesForSecret(t *testing.T) { t.Fatalf("Ingress was not added: %v", err) } - lbc.ingLister.Add(&test.ingress) - lbc.secrLister.Add(&test.secret) + lbc.ingressLister.Add(&test.ingress) + lbc.secretLister.Add(&test.secret) nonMinions, minions, err := lbc.findIngressesForSecret(test.secret.ObjectMeta.Namespace, test.secret.ObjectMeta.Name) if err != nil { diff --git a/nginx-controller/controller/leader.go b/internal/controller/leader.go similarity index 100% rename from nginx-controller/controller/leader.go rename to internal/controller/leader.go diff --git a/nginx-controller/controller/status.go b/internal/controller/status.go similarity index 97% rename from nginx-controller/controller/status.go rename to internal/controller/status.go index 1df796d7c8..81488da414 100644 --- a/nginx-controller/controller/status.go +++ b/internal/controller/status.go @@ -6,7 +6,8 @@ import ( "reflect" "github.com/golang/glog" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/utils" api_v1 "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,7 +26,7 @@ type StatusUpdater struct { externalServiceAddresses []string status []api_v1.LoadBalancerIngress keyFunc func(obj interface{}) (string, error) - ingLister *StoreToIngressLister + ingLister *utils.StoreToIngressLister } // UpdateManagedAndMergeableIngresses handles the full return format of LoadBalancerController.getManagedIngresses diff --git a/nginx-controller/controller/status_test.go b/internal/controller/status_test.go similarity index 97% rename from nginx-controller/controller/status_test.go rename to internal/controller/status_test.go index defee77b89..f6583503da 100644 --- a/nginx-controller/controller/status_test.go +++ b/internal/controller/status_test.go @@ -3,6 +3,7 @@ package controller import ( "testing" + "github.com/nginxinc/kubernetes-ingress/internal/utils" "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -32,7 +33,7 @@ func TestStatusUpdate(t *testing.T) { ing, }}, ) - ingLister := StoreToIngressLister{} + ingLister := utils.StoreToIngressLister{} ingLister.Store, _ = cache.NewInformer( cache.NewListWatchFromClient(fakeClient.Extensions().RESTClient(), "ingresses", "nginx-ingress", fields.Everything()), &extensions.Ingress{}, 2, nil) diff --git a/internal/handlers/configMap.go b/internal/handlers/configMap.go new file mode 100644 index 0000000000..9a61672848 --- /dev/null +++ b/internal/handlers/configMap.go @@ -0,0 +1,51 @@ +package handlers + +import ( + "reflect" + + "github.com/golang/glog" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + api_v1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/cache" +) + +// CreateConfigMapHandlers builds the handler funcs for config maps +func CreateConfigMapHandlers(lbc *controller.LoadBalancerController, name string) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + configMap := obj.(*api_v1.ConfigMap) + if configMap.Name == name { + glog.V(3).Infof("Adding ConfigMap: %v", configMap.Name) + lbc.AddSyncQueue(obj) + } + }, + DeleteFunc: func(obj interface{}) { + configMap, isConfigMap := obj.(*api_v1.ConfigMap) + if !isConfigMap { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + configMap, ok = deletedState.Obj.(*api_v1.ConfigMap) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-ConfigMap object: %v", deletedState.Obj) + return + } + } + if configMap.Name == name { + glog.V(3).Infof("Removing ConfigMap: %v", configMap.Name) + lbc.AddSyncQueue(obj) + } + }, + UpdateFunc: func(old, cur interface{}) { + if !reflect.DeepEqual(old, cur) { + configMap := cur.(*api_v1.ConfigMap) + if configMap.Name == name { + glog.V(3).Infof("ConfigMap %v changed, syncing", cur.(*api_v1.ConfigMap).Name) + lbc.AddSyncQueue(cur) + } + } + }, + } +} diff --git a/internal/handlers/endpoint.go b/internal/handlers/endpoint.go new file mode 100644 index 0000000000..12e8d5aa18 --- /dev/null +++ b/internal/handlers/endpoint.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "reflect" + + "github.com/golang/glog" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + api_v1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/cache" +) + +// CreateEndpointHandlers builds the handler funcs for endpoints +func CreateEndpointHandlers(lbc *controller.LoadBalancerController) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + endpoint := obj.(*api_v1.Endpoints) + glog.V(3).Infof("Adding endpoints: %v", endpoint.Name) + lbc.AddSyncQueue(obj) + }, + DeleteFunc: func(obj interface{}) { + endpoint, isEndpoint := obj.(*api_v1.Endpoints) + if !isEndpoint { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + endpoint, ok = deletedState.Obj.(*api_v1.Endpoints) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Endpoints object: %v", deletedState.Obj) + return + } + } + glog.V(3).Infof("Removing endpoints: %v", endpoint.Name) + lbc.AddSyncQueue(obj) + }, + UpdateFunc: func(old, cur interface{}) { + if !reflect.DeepEqual(old, cur) { + glog.V(3).Infof("Endpoints %v changed, syncing", cur.(*api_v1.Endpoints).Name) + lbc.AddSyncQueue(cur) + } + }, + } +} diff --git a/internal/handlers/ingress.go b/internal/handlers/ingress.go new file mode 100644 index 0000000000..6c2ec394e7 --- /dev/null +++ b/internal/handlers/ingress.go @@ -0,0 +1,65 @@ +package handlers + +import ( + "github.com/golang/glog" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + "github.com/nginxinc/kubernetes-ingress/internal/utils" + extensions "k8s.io/api/extensions/v1beta1" + "k8s.io/client-go/tools/cache" +) + +// CreateIngressHandlers builds the handler funcs for ingresses +func CreateIngressHandlers(lbc *controller.LoadBalancerController) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + ingress := obj.(*extensions.Ingress) + if !lbc.IsNginxIngress(ingress) { + glog.Infof("Ignoring Ingress %v based on Annotation %v", ingress.Name, lbc.GetIngressClassKey()) + return + } + glog.V(3).Infof("Adding Ingress: %v", ingress.Name) + lbc.AddSyncQueue(obj) + }, + DeleteFunc: func(obj interface{}) { + ingress, isIng := obj.(*extensions.Ingress) + if !isIng { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + ingress, ok = deletedState.Obj.(*extensions.Ingress) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Ingress object: %v", deletedState.Obj) + return + } + } + if !lbc.IsNginxIngress(ingress) { + return + } + if utils.IsMinion(ingress) { + master, err := lbc.FindMasterForMinion(ingress) + if err != nil { + glog.Infof("Ignoring Ingress %v(Minion): %v", ingress.Name, err) + return + } + glog.V(3).Infof("Removing Ingress: %v(Minion) for %v(Master)", ingress.Name, master.Name) + lbc.AddSyncQueue(master) + } else { + glog.V(3).Infof("Removing Ingress: %v", ingress.Name) + lbc.AddSyncQueue(obj) + } + }, + UpdateFunc: func(old, current interface{}) { + c := current.(*extensions.Ingress) + o := old.(*extensions.Ingress) + if !lbc.IsNginxIngress(c) { + return + } + if utils.HasChanges(o, c) { + glog.V(3).Infof("Ingress %v changed, syncing", c.Name) + lbc.AddSyncQueue(c) + } + }, + } +} diff --git a/internal/handlers/leader.go b/internal/handlers/leader.go new file mode 100644 index 0000000000..b60e1ba5ea --- /dev/null +++ b/internal/handlers/leader.go @@ -0,0 +1,21 @@ +package handlers + +import ( + "github.com/golang/glog" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + "k8s.io/client-go/tools/leaderelection" +) + +// CreateLeaderHandler builds the handler funcs for leader handling +func CreateLeaderHandler(lbc *controller.LoadBalancerController) leaderelection.LeaderCallbacks { + return leaderelection.LeaderCallbacks{ + OnStartedLeading: func(stop <-chan struct{}) { + glog.V(3).Info("started leading, updating ingress status") + ingresses, mergeableIngresses := lbc.GetManagedIngresses() + err := lbc.UpdateManagedAndMergeableIngresses(ingresses, mergeableIngresses) + if err != nil { + glog.V(3).Infof("error updating status when starting leading: %v", err) + } + }, + } +} diff --git a/internal/handlers/secret.go b/internal/handlers/secret.go new file mode 100644 index 0000000000..b0972ff727 --- /dev/null +++ b/internal/handlers/secret.go @@ -0,0 +1,60 @@ +package handlers + +import ( + "reflect" + + "github.com/golang/glog" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + api_v1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/cache" +) + +// CreateSecretHandlers builds the handler funcs for secrets +func CreateSecretHandlers(lbc *controller.LoadBalancerController) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + secret := obj.(*api_v1.Secret) + if err := lbc.ValidateSecret(secret); err != nil { + return + } + nsname := secret.Namespace + "/" + secret.Name + if nsname == lbc.GetDefaultServerSecret() { + glog.V(3).Infof("Adding default server Secret: %v", secret.Name) + lbc.AddSyncQueue(obj) + } + }, + DeleteFunc: func(obj interface{}) { + secret, isSecr := obj.(*api_v1.Secret) + if !isSecr { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + secret, ok = deletedState.Obj.(*api_v1.Secret) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Secret object: %v", deletedState.Obj) + return + } + } + if err := lbc.ValidateSecret(secret); err != nil { + return + } + + glog.V(3).Infof("Removing Secret: %v", secret.Name) + lbc.AddSyncQueue(obj) + }, + UpdateFunc: func(old, cur interface{}) { + errOld := lbc.ValidateSecret(old.(*api_v1.Secret)) + errCur := lbc.ValidateSecret(cur.(*api_v1.Secret)) + if errOld != nil && errCur != nil { + return + } + + if !reflect.DeepEqual(old, cur) { + glog.V(3).Infof("Secret %v changed, syncing", cur.(*api_v1.Secret).Name) + lbc.AddSyncQueue(cur) + } + }, + } +} diff --git a/internal/handlers/service.go b/internal/handlers/service.go new file mode 100644 index 0000000000..1e43e9890e --- /dev/null +++ b/internal/handlers/service.go @@ -0,0 +1,60 @@ +package handlers + +import ( + "reflect" + + "github.com/golang/glog" + "github.com/nginxinc/kubernetes-ingress/internal/controller" + "k8s.io/client-go/tools/cache" + + api_v1 "k8s.io/api/core/v1" +) + +// CreateServiceHandlers builds the handler funcs for services +func CreateServiceHandlers(lbc *controller.LoadBalancerController) cache.ResourceEventHandlerFuncs { + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + svc := obj.(*api_v1.Service) + if lbc.IsExternalServiceForStatus(svc) { + lbc.AddSyncQueue(svc) + return + } + glog.V(3).Infof("Adding service: %v", svc.Name) + lbc.EnqueueIngressForService(svc) + }, + DeleteFunc: func(obj interface{}) { + svc, isSvc := obj.(*api_v1.Service) + if !isSvc { + deletedState, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.V(3).Infof("Error received unexpected object: %v", obj) + return + } + svc, ok = deletedState.Obj.(*api_v1.Service) + if !ok { + glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Service object: %v", deletedState.Obj) + return + } + } + if lbc.IsExternalServiceForStatus(svc) { + lbc.AddSyncQueue(svc) + return + } + + glog.V(3).Infof("Removing service: %v", svc.Name) + lbc.EnqueueIngressForService(svc) + + }, + UpdateFunc: func(old, cur interface{}) { + if !reflect.DeepEqual(old, cur) { + svc := cur.(*api_v1.Service) + if lbc.IsExternalServiceForStatus(svc) { + lbc.AddSyncQueue(svc) + return + } + glog.V(3).Infof("Service %v changed, syncing", svc.Name) + lbc.EnqueueIngressForService(svc) + } + }, + } +} diff --git a/nginx-controller/nginx/config.go b/internal/nginx/config.go similarity index 100% rename from nginx-controller/nginx/config.go rename to internal/nginx/config.go diff --git a/nginx-controller/nginx/configurator.go b/internal/nginx/configurator.go similarity index 98% rename from nginx-controller/nginx/configurator.go rename to internal/nginx/configurator.go index 1f002a7297..c53fe18002 100644 --- a/nginx-controller/nginx/configurator.go +++ b/internal/nginx/configurator.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/golang/glog" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx/plus" + "github.com/nginxinc/kubernetes-ingress/internal/nginx/plus" api_v1 "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,7 +28,7 @@ const JWTKeyAnnotation = "nginx.com/jwt-key" // Configurator transforms an Ingress resource into NGINX Configuration type Configurator struct { - nginx *NginxController + nginx *Controller config *Config nginxAPI *plus.NginxAPIController templateExecutor *TemplateExecutor @@ -37,7 +37,7 @@ type Configurator struct { } // NewConfigurator creates a new Configurator -func NewConfigurator(nginx *NginxController, config *Config, nginxAPI *plus.NginxAPIController, templateExecutor *TemplateExecutor) *Configurator { +func NewConfigurator(nginx *Controller, config *Config, nginxAPI *plus.NginxAPIController, templateExecutor *TemplateExecutor) *Configurator { cnf := Configurator{ nginx: nginx, config: config, @@ -738,7 +738,7 @@ func parsePort(value string) (int, error) { port, err := strconv.ParseInt(value, 10, 16) if err != nil { return 0, fmt.Errorf( - "Unable to parse port as integer: %s\n", + "Unable to parse port as integer: %s", err, ) } @@ -1051,7 +1051,7 @@ func (cnf *Configurator) updatePlusEndpoints(ingEx *IngressEx) error { func filterMasterAnnotations(annotations map[string]string) []string { var removedAnnotations []string - for key, _ := range annotations { + for key := range annotations { if _, notAllowed := masterBlacklist[key]; notAllowed { removedAnnotations = append(removedAnnotations, key) delete(annotations, key) @@ -1064,7 +1064,7 @@ func filterMasterAnnotations(annotations map[string]string) []string { func filterMinionAnnotations(annotations map[string]string) []string { var removedAnnotations []string - for key, _ := range annotations { + for key := range annotations { if _, notAllowed := minionBlacklist[key]; notAllowed { removedAnnotations = append(removedAnnotations, key) delete(annotations, key) @@ -1085,8 +1085,8 @@ func mergeMasterAnnotationsIntoMinion(minionAnnotations map[string]string, maste } // GenerateNginxMainConfig generate NginxMainConfig from Config -func GenerateNginxMainConfig(config *Config) *NginxMainConfig { - nginxCfg := &NginxMainConfig{ +func GenerateNginxMainConfig(config *Config) *MainConfig { + nginxCfg := &MainConfig{ MainSnippets: config.MainMainSnippets, HTTPSnippets: config.MainHTTPSnippets, StreamSnippets: config.MainStreamSnippets, diff --git a/nginx-controller/nginx/configurator_test.go b/internal/nginx/configurator_test.go similarity index 99% rename from nginx-controller/nginx/configurator_test.go rename to internal/nginx/configurator_test.go index f63f4688f1..f0ae5a66cb 100644 --- a/nginx-controller/nginx/configurator_test.go +++ b/internal/nginx/configurator_test.go @@ -6,7 +6,7 @@ import ( "sort" "testing" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx/plus" + "github.com/nginxinc/kubernetes-ingress/internal/nginx/plus" api_v1 "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" diff --git a/nginx-controller/nginx/convert.go b/internal/nginx/convert.go similarity index 100% rename from nginx-controller/nginx/convert.go rename to internal/nginx/convert.go diff --git a/nginx-controller/nginx/convert_test.go b/internal/nginx/convert_test.go similarity index 100% rename from nginx-controller/nginx/convert_test.go rename to internal/nginx/convert_test.go diff --git a/nginx-controller/nginx/extensions.go b/internal/nginx/extensions.go similarity index 100% rename from nginx-controller/nginx/extensions.go rename to internal/nginx/extensions.go diff --git a/nginx-controller/nginx/extensions_test.go b/internal/nginx/extensions_test.go similarity index 100% rename from nginx-controller/nginx/extensions_test.go rename to internal/nginx/extensions_test.go diff --git a/nginx-controller/nginx/ingress.go b/internal/nginx/ingress.go similarity index 96% rename from nginx-controller/nginx/ingress.go rename to internal/nginx/ingress.go index 1df62bcc7b..12c4bb2c8e 100644 --- a/nginx-controller/nginx/ingress.go +++ b/internal/nginx/ingress.go @@ -15,6 +15,7 @@ type IngressEx struct { HealthChecks map[string]*api_v1.Probe } +// MergeableIngresses is a mergeable ingress of a master and minions type MergeableIngresses struct { Master *IngressEx Minions []*IngressEx diff --git a/nginx-controller/nginx/nginx.go b/internal/nginx/nginx.go similarity index 89% rename from nginx-controller/nginx/nginx.go rename to internal/nginx/nginx.go index 4fd6a2af08..8bc642c722 100644 --- a/nginx-controller/nginx/nginx.go +++ b/internal/nginx/nginx.go @@ -17,8 +17,8 @@ const dhparamFilename = "dhparam.pem" const TLSSecretFileMode = 0600 const jwkSecretFileMode = 0644 -// NginxController updates NGINX configuration, starts and reloads NGINX -type NginxController struct { +// Controller updates NGINX configuration, starts and reloads NGINX +type Controller struct { nginxConfdPath string nginxSecretsPath string local bool @@ -137,8 +137,8 @@ type Location struct { IngressResource string } -// NginxMainConfig describe the main NGINX configuration file -type NginxMainConfig struct { +// MainConfig describe the main NGINX configuration file +type MainConfig struct { ServerNamesHashBucketSize string ServerNamesHashMaxSize string LogFormat string @@ -182,8 +182,8 @@ func NewUpstreamWithDefaultServer(name string) Upstream { } // NewNginxController creates a NGINX controller -func NewNginxController(nginxConfPath string, local bool) *NginxController { - ngxc := NginxController{ +func NewNginxController(nginxConfPath string, local bool) *Controller { + ngxc := Controller{ nginxConfdPath: path.Join(nginxConfPath, "conf.d"), nginxSecretsPath: path.Join(nginxConfPath, "secrets"), local: local, @@ -194,7 +194,7 @@ func NewNginxController(nginxConfPath string, local bool) *NginxController { // DeleteIngress deletes the configuration file, which corresponds for the // specified ingress from NGINX conf directory -func (nginx *NginxController) DeleteIngress(name string) { +func (nginx *Controller) DeleteIngress(name string) { filename := nginx.getIngressNginxConfigFileName(name) glog.V(3).Infof("deleting %v", filename) @@ -206,7 +206,7 @@ func (nginx *NginxController) DeleteIngress(name string) { } // AddOrUpdateDHParam creates the servers dhparam.pem file -func (nginx *NginxController) AddOrUpdateDHParam(dhparam string) (string, error) { +func (nginx *Controller) AddOrUpdateDHParam(dhparam string) (string, error) { fileName := nginx.nginxSecretsPath + "/" + dhparamFilename if !nginx.local { pem, err := os.Create(fileName) @@ -224,7 +224,7 @@ func (nginx *NginxController) AddOrUpdateDHParam(dhparam string) (string, error) } // AddOrUpdateSecretFile creates a file with the specified name, content and mode. -func (nginx *NginxController) AddOrUpdateSecretFile(name string, content []byte, mode os.FileMode) string { +func (nginx *Controller) AddOrUpdateSecretFile(name string, content []byte, mode os.FileMode) string { filename := nginx.getSecretFileName(name) if !nginx.local { @@ -258,7 +258,7 @@ func (nginx *NginxController) AddOrUpdateSecretFile(name string, content []byte, } // DeleteSecretFile the file with a Secret -func (nginx *NginxController) DeleteSecretFile(name string) { +func (nginx *Controller) DeleteSecretFile(name string) { filename := nginx.getSecretFileName(name) glog.V(3).Infof("deleting %v", filename) @@ -270,16 +270,16 @@ func (nginx *NginxController) DeleteSecretFile(name string) { } -func (nginx *NginxController) getIngressNginxConfigFileName(name string) string { +func (nginx *Controller) getIngressNginxConfigFileName(name string) string { return path.Join(nginx.nginxConfdPath, name+".conf") } -func (nginx *NginxController) getSecretFileName(name string) string { +func (nginx *Controller) getSecretFileName(name string) string { return path.Join(nginx.nginxSecretsPath, name) } // Reload reloads NGINX -func (nginx *NginxController) Reload() error { +func (nginx *Controller) Reload() error { if !nginx.local { if err := shellOut("nginx -t"); err != nil { return fmt.Errorf("Invalid nginx configuration detected, not reloading: %s", err) @@ -294,7 +294,7 @@ func (nginx *NginxController) Reload() error { } // Start starts NGINX -func (nginx *NginxController) Start(done chan error) { +func (nginx *Controller) Start(done chan error) { if !nginx.local { cmd := exec.Command("nginx") cmd.Stdout = os.Stdout @@ -311,7 +311,7 @@ func (nginx *NginxController) Start(done chan error) { } // Quit shutdowns NGINX gracefully -func (nginx *NginxController) Quit() { +func (nginx *Controller) Quit() { if !nginx.local { if err := shellOut("nginx -s quit"); err != nil { glog.Fatalf("Failed to quit nginx: %v", err) @@ -351,7 +351,7 @@ func shellOut(cmd string) (err error) { } // UpdateMainConfigFile writes the main NGINX configuration file to the filesystem -func (nginx *NginxController) UpdateMainConfigFile(cfg []byte) { +func (nginx *Controller) UpdateMainConfigFile(cfg []byte) { filename := "/etc/nginx/nginx.conf" glog.V(3).Infof("Writing NGINX conf to %v", filename) @@ -374,7 +374,7 @@ func (nginx *NginxController) UpdateMainConfigFile(cfg []byte) { } // UpdateIngressConfigFile writes the Ingress configuration file to the filesystem -func (nginx *NginxController) UpdateIngressConfigFile(name string, cfg []byte) { +func (nginx *Controller) UpdateIngressConfigFile(name string, cfg []byte) { filename := nginx.getIngressNginxConfigFileName(name) glog.V(3).Infof("Writing Ingress conf to %v", filename) diff --git a/nginx-controller/nginx/plus/nginx_api.go b/internal/nginx/plus/nginx_api.go similarity index 86% rename from nginx-controller/nginx/plus/nginx_api.go rename to internal/nginx/plus/nginx_api.go index a887e5e7e8..69c7d7119d 100644 --- a/nginx-controller/nginx/plus/nginx_api.go +++ b/internal/nginx/plus/nginx_api.go @@ -6,17 +6,20 @@ import ( "github.com/golang/glog" ) +// NginxAPIController works with the NGINX API type NginxAPIController struct { client *NginxClient local bool } +// ServerConfig holds the config data type ServerConfig struct { MaxFails int64 FailTimeout string SlowStart string } +// NewNginxAPIController creates an instance of NginxAPIController func NewNginxAPIController(httpClient *http.Client, endpoint string, local bool) (*NginxAPIController, error) { client, err := NewNginxClient(httpClient, endpoint) if !local && err != nil { @@ -26,6 +29,7 @@ func NewNginxAPIController(httpClient *http.Client, endpoint string, local bool) return nginx, nil } +// UpdateServers updates upstream servers func (nginx *NginxAPIController) UpdateServers(upstream string, servers []string, config ServerConfig) error { if nginx.local { glog.V(3).Infof("Updating endpoints of %v: %v\n", upstream, servers) diff --git a/nginx-controller/nginx/plus/nginx_client.go b/internal/nginx/plus/nginx_client.go similarity index 99% rename from nginx-controller/nginx/plus/nginx_client.go rename to internal/nginx/plus/nginx_client.go index bde2a0fb5d..d902d8eaa5 100644 --- a/nginx-controller/nginx/plus/nginx_client.go +++ b/internal/nginx/plus/nginx_client.go @@ -20,6 +20,7 @@ type NginxClient struct { type versions []int +// UpstreamServer holds the fields for an upstream server type UpstreamServer struct { ID int64 `json:"id,omitempty"` Server string `json:"server"` diff --git a/nginx-controller/nginx/secret.go b/internal/nginx/secret.go similarity index 100% rename from nginx-controller/nginx/secret.go rename to internal/nginx/secret.go diff --git a/nginx-controller/nginx/template_executor.go b/internal/nginx/template_executor.go similarity index 96% rename from nginx-controller/nginx/template_executor.go rename to internal/nginx/template_executor.go index 66055c2d32..306f5a05a1 100644 --- a/nginx-controller/nginx/template_executor.go +++ b/internal/nginx/template_executor.go @@ -60,7 +60,7 @@ func (te *TemplateExecutor) UpdateIngressTemplate(templateString *string) error } // ExecuteMainConfigTemplate generates the content of the main NGINX configuration file -func (te *TemplateExecutor) ExecuteMainConfigTemplate(cfg *NginxMainConfig) ([]byte, error) { +func (te *TemplateExecutor) ExecuteMainConfigTemplate(cfg *MainConfig) ([]byte, error) { cfg.HealthStatus = te.HealthStatus cfg.NginxStatus = te.NginxStatus cfg.NginxStatusPort = te.NginxStatusPort diff --git a/nginx-controller/nginx/templates/nginx-plus.ingress.tmpl b/internal/nginx/templates/nginx-plus.ingress.tmpl similarity index 100% rename from nginx-controller/nginx/templates/nginx-plus.ingress.tmpl rename to internal/nginx/templates/nginx-plus.ingress.tmpl diff --git a/nginx-controller/nginx/templates/nginx-plus.tmpl b/internal/nginx/templates/nginx-plus.tmpl similarity index 100% rename from nginx-controller/nginx/templates/nginx-plus.tmpl rename to internal/nginx/templates/nginx-plus.tmpl diff --git a/nginx-controller/nginx/templates/nginx.ingress.tmpl b/internal/nginx/templates/nginx.ingress.tmpl similarity index 100% rename from nginx-controller/nginx/templates/nginx.ingress.tmpl rename to internal/nginx/templates/nginx.ingress.tmpl diff --git a/nginx-controller/nginx/templates/nginx.tmpl b/internal/nginx/templates/nginx.tmpl similarity index 100% rename from nginx-controller/nginx/templates/nginx.tmpl rename to internal/nginx/templates/nginx.tmpl diff --git a/nginx-controller/nginx/templates/templates_test.go b/internal/nginx/templates/templates_test.go similarity index 97% rename from nginx-controller/nginx/templates/templates_test.go rename to internal/nginx/templates/templates_test.go index 27796d538e..221b64040f 100644 --- a/nginx-controller/nginx/templates/templates_test.go +++ b/internal/nginx/templates/templates_test.go @@ -5,7 +5,7 @@ import ( "testing" "text/template" - "github.com/nginxinc/kubernetes-ingress/nginx-controller/nginx" + "github.com/nginxinc/kubernetes-ingress/internal/nginx" ) const nginxIngressTmpl = "nginx.ingress.tmpl" @@ -80,7 +80,7 @@ var ingCfg = nginx.IngressNginxConfig{ Keepalive: "16", } -var mainCfg = nginx.NginxMainConfig{ +var mainCfg = nginx.MainConfig{ ServerNamesHashMaxSize: "512", ServerTokens: "off", WorkerProcesses: "auto", diff --git a/internal/queue/queue.go b/internal/queue/queue.go new file mode 100644 index 0000000000..d7013a5ff8 --- /dev/null +++ b/internal/queue/queue.go @@ -0,0 +1,92 @@ +package queue + +import ( + "time" + + "github.com/golang/glog" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" +) + +var keyFunc = cache.DeletionHandlingMetaNamespaceKeyFunc + +// TaskQueue manages a work queue through an independent worker that +// invokes the given sync function for every work item inserted. +type TaskQueue struct { + // queue is the work queue the worker polls + queue *workqueue.Type + // sync is called for each item in the queue + sync func(Task) + // workerDone is closed when the worker exits + workerDone chan struct{} +} + +// NewTaskQueue creates a new task queue with the given sync function. +// The sync function is called for every element inserted into the queue. +func NewTaskQueue(syncFn func(Task)) *TaskQueue { + return &TaskQueue{ + queue: workqueue.New(), + sync: syncFn, + workerDone: make(chan struct{}), + } +} + +// Run begins running the worker for the given duration +func (t *TaskQueue) Run(period time.Duration, stopCh <-chan struct{}) { + wait.Until(t.worker, period, stopCh) +} + +// Enqueue enqueues ns/name of the given api object in the task queue. +func (t *TaskQueue) Enqueue(obj interface{}) { + key, err := keyFunc(obj) + if err != nil { + glog.V(3).Infof("Couldn't get key for object %v: %v", obj, err) + return + } + + task, err := NewTask(key, obj) + if err != nil { + glog.V(3).Infof("Couldn't create a task for object %v: %v", obj, err) + return + } + + glog.V(3).Infof("Adding an element with a key: %v", task.Key) + + t.queue.Add(task) +} + +// Requeue adds the task to the queue again and logs the given error +func (t *TaskQueue) Requeue(task Task, err error) { + glog.Errorf("Requeuing %v, err %v", task.Key, err) + t.queue.Add(task) +} + +// RequeueAfter adds the task to the queue after the given duration +func (t *TaskQueue) RequeueAfter(task Task, err error, after time.Duration) { + glog.Errorf("Requeuing %v after %s, err %v", task.Key, after.String(), err) + go func(task Task, after time.Duration) { + time.Sleep(after) + t.queue.Add(task) + }(task, after) +} + +// Worker processes work in the queue through sync. +func (t *TaskQueue) worker() { + for { + task, quit := t.queue.Get() + if quit { + close(t.workerDone) + return + } + glog.V(3).Infof("Syncing %v", task.(Task).Key) + t.sync(task.(Task)) + t.queue.Done(task) + } +} + +// Shutdown shuts down the work queue and waits for the worker to ACK +func (t *TaskQueue) Shutdown() { + t.queue.ShutDown() + <-t.workerDone +} diff --git a/internal/queue/task.go b/internal/queue/task.go new file mode 100644 index 0000000000..7cf65931ce --- /dev/null +++ b/internal/queue/task.go @@ -0,0 +1,59 @@ +package queue + +import ( + "fmt" + + "github.com/nginxinc/kubernetes-ingress/internal/utils" + api_v1 "k8s.io/api/core/v1" + extensions "k8s.io/api/extensions/v1beta1" +) + +// Kind represents the kind of the Kubernetes resources of a task +type Kind int + +const ( + // Ingress resource + Ingress = iota + // IngressMinion resource, which is a Minion Ingress resource + IngressMinion + // Endpoints resource + Endpoints + // ConfigMap resource + ConfigMap + // Secret resource + Secret + // Service resource + Service +) + +// Task is an element of a TaskQueue +type Task struct { + Kind Kind + Key string +} + +// NewTask creates a new task +func NewTask(key string, obj interface{}) (Task, error) { + var k Kind + switch t := obj.(type) { + case *extensions.Ingress: + ing := obj.(*extensions.Ingress) + if utils.IsMinion(ing) { + k = IngressMinion + } else { + k = Ingress + } + case *api_v1.Endpoints: + k = Endpoints + case *api_v1.ConfigMap: + k = ConfigMap + case *api_v1.Secret: + k = Secret + case *api_v1.Service: + k = Service + default: + return Task{}, fmt.Errorf("Unknow type: %v", t) + } + + return Task{k, key}, nil +} diff --git a/nginx-controller/controller/utils.go b/internal/utils/utils.go similarity index 58% rename from nginx-controller/controller/utils.go rename to internal/utils/utils.go index 90ad817190..a85eb23b1d 100644 --- a/nginx-controller/controller/utils.go +++ b/internal/utils/utils.go @@ -14,150 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -package controller +package utils import ( "fmt" - "time" + "reflect" + "strings" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" api_v1 "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" - - "github.com/golang/glog" -) - -// taskQueue manages a work queue through an independent worker that -// invokes the given sync function for every work item inserted. -type taskQueue struct { - // queue is the work queue the worker polls - queue *workqueue.Type - // sync is called for each item in the queue - sync func(Task) - // workerDone is closed when the worker exits - workerDone chan struct{} -} - -func (t *taskQueue) run(period time.Duration, stopCh <-chan struct{}) { - wait.Until(t.worker, period, stopCh) -} - -// enqueue enqueues ns/name of the given api object in the task queue. -func (t *taskQueue) enqueue(obj interface{}) { - key, err := keyFunc(obj) - if err != nil { - glog.V(3).Infof("Couldn't get key for object %v: %v", obj, err) - return - } - - task, err := NewTask(key, obj) - if err != nil { - glog.V(3).Infof("Couldn't create a task for object %v: %v", obj, err) - return - } - - glog.V(3).Infof("Adding an element with a key: %v", task.Key) - - t.queue.Add(task) -} - -func (t *taskQueue) requeue(task Task, err error) { - glog.Errorf("Requeuing %v, err %v", task.Key, err) - t.queue.Add(task) -} - -func (t *taskQueue) requeueAfter(task Task, err error, after time.Duration) { - glog.Errorf("Requeuing %v after %s, err %v", task.Key, after.String(), err) - go func(task Task, after time.Duration) { - time.Sleep(after) - t.queue.Add(task) - }(task, after) -} - -// worker processes work in the queue through sync. -func (t *taskQueue) worker() { - for { - task, quit := t.queue.Get() - if quit { - close(t.workerDone) - return - } - glog.V(3).Infof("Syncing %v", task.(Task).Key) - t.sync(task.(Task)) - t.queue.Done(task) - } -} - -// shutdown shuts down the work queue and waits for the worker to ACK -func (t *taskQueue) shutdown() { - t.queue.ShutDown() - <-t.workerDone -} - -// NewTaskQueue creates a new task queue with the given sync function. -// The sync function is called for every element inserted into the queue. -func NewTaskQueue(syncFn func(Task)) *taskQueue { - return &taskQueue{ - queue: workqueue.New(), - sync: syncFn, - workerDone: make(chan struct{}), - } -} - -// Kind represents the kind of the Kubernetes resources of a task -type Kind int - -const ( - // Ingress resource - Ingress = iota - // IngressMinion resource, which is a Minion Ingress resource - IngressMinion - // Endpoints resource - Endpoints - // ConfigMap resource - ConfigMap - // Secret resource - Secret - // Service resource - Service ) -// Task is an element of a taskQueue -type Task struct { - Kind Kind - Key string -} - -// NewTask creates a new task -func NewTask(key string, obj interface{}) (Task, error) { - var k Kind - switch t := obj.(type) { - case *extensions.Ingress: - ing := obj.(*extensions.Ingress) - if isMinion(ing) { - k = IngressMinion - } else { - k = Ingress - } - case *api_v1.Endpoints: - k = Endpoints - case *api_v1.ConfigMap: - k = ConfigMap - case *api_v1.Secret: - k = Secret - case *api_v1.Service: - k = Service - default: - return Task{}, fmt.Errorf("Unknow type: %v", t) - } - - return Task{k, key}, nil -} - // compareLinks returns true if the 2 self links are equal. func compareLinks(l1, l2 string) bool { // TODO: These can be partial links @@ -276,3 +146,36 @@ func FindPort(pod *api_v1.Pod, svcPort *api_v1.ServicePort) (int32, error) { type StoreToSecretLister struct { cache.Store } + +// IsMinion determines is an ingress is a minion or not +func IsMinion(ing *extensions.Ingress) bool { + if ing.Annotations["nginx.org/mergeable-ingress-type"] == "minion" { + return true + } + return false +} + +// IsMaster determines is an ingress is a master or not +func IsMaster(ing *extensions.Ingress) bool { + if ing.Annotations["nginx.org/mergeable-ingress-type"] == "master" { + return true + } + return false +} + +// HasChanges determines if current ingress has changes compared to old ingress +func HasChanges(old *extensions.Ingress, current *extensions.Ingress) bool { + old.Status.LoadBalancer.Ingress = current.Status.LoadBalancer.Ingress + old.ResourceVersion = current.ResourceVersion + return !reflect.DeepEqual(old, current) +} + +// ParseNamespaceName parses the string in the / format and returns the name and the namespace. +// It returns an error in case the string does not follow the / format. +func ParseNamespaceName(value string) (ns string, name string, err error) { + res := strings.Split(value, "/") + if len(res) != 2 { + return "", "", fmt.Errorf("%q must follow the format /", value) + } + return res[0], res[1], nil +} diff --git a/nginx-controller/Makefile b/nginx-controller/Makefile deleted file mode 100644 index d2c6e77241..0000000000 --- a/nginx-controller/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -all: push - -VERSION = edge -TAG = $(VERSION) -PREFIX = nginx/nginx-ingress - -DOCKER_RUN = docker run --rm -v $(shell pwd)/../:/go/src/github.com/nginxinc/kubernetes-ingress -w /go/src/github.com/nginxinc/kubernetes-ingress/nginx-controller/ -GOLANG_CONTAINER = golang:1.10 -DOCKERFILE = Dockerfile - -BUILD_IN_CONTAINER = 1 -PUSH_TO_GCR = -GENERATE_DEFAULT_CERT_AND_KEY = -DOCKER_BUILD_OPTIONS = - -GIT_COMMIT=$(shell git rev-parse --short HEAD) - -nginx-ingress: -ifeq ($(BUILD_IN_CONTAINER),1) - $(DOCKER_RUN) -e CGO_ENABLED=0 $(GOLANG_CONTAINER) go build -a -installsuffix cgo -ldflags "-w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT}" -o nginx-ingress *.go -else - CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags "-w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT}" -o nginx-ingress *.go -endif - -test: -ifeq ($(BUILD_IN_CONTAINER),1) - $(DOCKER_RUN) $(GOLANG_CONTAINER) go test ./... -else - go test ./... -endif - -certificate-and-key: -ifeq ($(GENERATE_DEFAULT_CERT_AND_KEY),1) - ./generate_default_cert_and_key.sh -endif - -container: test nginx-ingress certificate-and-key - docker build $(DOCKER_BUILD_OPTIONS) -f $(DOCKERFILE) -t $(PREFIX):$(TAG) . - -push: container -ifeq ($(PUSH_TO_GCR),1) - gcloud docker -- push $(PREFIX):$(TAG) -else - docker push $(PREFIX):$(TAG) -endif - -clean: - rm -f nginx-ingress