Skip to content

Commit

Permalink
add local deployment configuration
Browse files Browse the repository at this point in the history
Add Makefile command and controller option that allows to run the
controller outside of a container. These options are specially useful
to develop and test locally.
  • Loading branch information
jcmoraisjr committed Dec 28, 2021
1 parent a6a2cc1 commit c0d3c0c
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode
bin
rootfs/haproxy-ingress-controller

# IntelliJ
Expand Down
49 changes: 37 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,53 @@ GOARCH?=amd64
GIT_REPO=$(shell git config --get remote.origin.url)
GIT_COMMIT=git-$(shell git rev-parse --short HEAD)
VERSION_PKG=github.com/jcmoraisjr/haproxy-ingress/pkg/version
CONTROLLER_FLAGS=-X $(VERSION_PKG).RELEASE=local -X $(VERSION_PKG).COMMIT=$(GIT_COMMIT) -X $(VERSION_PKG).REPO=$(GIT_REPO)
CONTROLLER_TAG?=localhost/haproxy-ingress:latest
LOCAL_FS_PREFIX?=/tmp/haproxy-ingress
KUBECONFIG?=$(HOME)/.kube/config
CONTROLLER_CONFIGMAP?=
CONTROLLER_ARGS?=

.PHONY: build
build:
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build \
-installsuffix cgo \
-ldflags "-s -w -X $(VERSION_PKG).RELEASE=local -X $(VERSION_PKG).COMMIT=$(GIT_COMMIT) -X $(VERSION_PKG).REPO=$(GIT_REPO)" \
-o rootfs/haproxy-ingress-controller pkg/main.go
CGO_ENABLED=0 go build \
-v -installsuffix cgo \
-ldflags "-s -w $(CONTROLLER_FLAGS)" \
-o bin/controller pkg/main.go

.PHONY: run
run: build
@rm -rf $(LOCAL_FS_PREFIX)/var/run/haproxy/
@mkdir -p $(LOCAL_FS_PREFIX)/etc/haproxy/lua/
@cp rootfs/etc/lua/* $(LOCAL_FS_PREFIX)/etc/haproxy/lua/
./bin/controller \
--kubeconfig=$(KUBECONFIG)\
--local-filesystem-prefix=$(LOCAL_FS_PREFIX)\
--update-status=false\
--configmap=$(CONTROLLER_CONFIGMAP)\
$(CONTROLLER_ARGS)

.PHONY: lint
lint:
golangci-lint run

.PHONY: test
test:
test: lint
## fix race and add -race param
go test -tags cgo ./...

.PHONY: install
install:
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go install \
-v -installsuffix cgo pkg/main.go
.PHONY: linux-build
linux-build:
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build \
-v -installsuffix cgo \
-ldflags "-s -w $(CONTROLLER_FLAGS)" \
-o rootfs/haproxy-ingress-controller pkg/main.go

.PHONY: image
image:
docker build -t localhost/haproxy-ingress:latest rootfs
image: linux-build
docker build -t $(CONTROLLER_TAG) rootfs

.PHONY: docker-builder
docker-builder:
docker build -t localhost/haproxy-ingress:latest . -f builder/Dockerfile
@rm -f rootfs/haproxy-ingress-controller
docker build -t $(CONTROLLER_TAG) . -f builder/Dockerfile
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,65 @@ Updates made to the cluster are applied on the fly to the HAProxy instance.

## Develop HAProxy Ingress

Building:
**Building and running locally:**

```
mkdir -p $GOPATH/src/github.com/jcmoraisjr
cd $GOPATH/src/github.com/jcmoraisjr
git clone https://github.com/jcmoraisjr/haproxy-ingress.git
cd haproxy-ingress
make
make run
```

The following `make` targets are currently supported:
Dependencies to run locally:

* `install`: run `go install` which saves some building time.
* `build` (default): compiles HAProxy Ingress and generates an ELF (Linux) executable at `rootfs/haproxy-ingress-controller` despite the source platform.
* `test`: run unit tests
* `image`: generates a Docker image tagged `localhost/haproxy-ingress:latest`
* Golang
* HAProxy compiled with `USE_OPENSSL=1` and `USE_LUA=1`
* [golangci-lint](https://golangci-lint.run/) is used when running `make lint` or `make test` targets
* Lua with `lua-json` (`luarocks install lua-json`) if using Auth External or OAuth
* Kubernetes network should be reachable from the local machine for a proper e2e test

**Building container image:**

Fast build - cross compile for linux/amd64 (locally) and generate `localhost/haproxy-ingress:latest`:

```
make image
```

Official image - build in a multi-stage Dockerfile and generate `localhost/haproxy-ingress:latest`:

```
make docker-build
```

Deploy local image using Helm:

```
helm repo add haproxy-ingress https://haproxy-ingress.github.io/charts
helm install haproxy-ingress haproxy-ingress/haproxy-ingress\
--create-namespace --namespace=ingress-controller\
--set controller.image.repository=localhost/haproxy-ingress\
--set controller.image.tag=latest\
--set controller.image.pullPolicy=Never
```

**make options:**

The following `make` variables are supported:

* `CONTROLLER_TAG` (defaults to `localhost/haproxy-ingress:latest`): tag name for `make image` and `make docker-build`.
* `LOCAL_FS_PREFIX` (defaults to `/tmp/haproxy-ingress`): temporary directory for `make run`.
* `KUBECONFIG` (defaults to `$KUBECONFIG`, or `$(HOME)/.kube/config` if the former is empty): Kubernetes from where to read Ingress configurations.
* `CONTROLLER_CONFIGMAP`: `<namespace>/<name>` of the ConfigMap with global configurations.
* `CONTROLLER_ARGS`: space separated list of additional command-line arguments.

The following `make` targets are supported:

* `build` (default): Compiles HAProxy Ingress using the default OS and arch, and generates an executable at `bin/controller`.
* `run`: Runs HAProxy Ingress locally.
* `lint`: Runs [`golangci-lint`](https://golangci-lint.run/).
* `test`: Runs unit tests.
* `linux-build`: Compiles HAProxy Ingress and generates an ELF (Linux) executable despite the source platform at `rootfs/haproxy-ingress-controller`. Used by `image` step.
* `image`: Compiles HAProxy Ingress locally and generates a Docker image.
* `docker-build`: Compiles HAProxy Ingress and generates a Docker image using a multi-stage Dockerfile.
12 changes: 12 additions & 0 deletions docs/content/en/docs/configuration/command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The following command-line options are supported:
| [`--ingress-class`](#ingress-class) | name | `haproxy` | |
| [`--ingress-class-precedence`](#ingress-class) | [true\|false] | `false` | v0.13.5 |
| [`--kubeconfig`](#kubeconfig) | /path/to/kubeconfig | in cluster config | |
| [`--local-filesystem-prefix`](#local-filesystem-prefix) | temporary base directory | | v0.14 |
| [`--master-socket`](#master-socket) | socket path | use embedded haproxy | v0.12 |
| [`--max-old-config-files`](#max-old-config-files) | num of files | `0` | |
| [`--profiling`](#stats) | [true\|false] | `true` | |
Expand Down Expand Up @@ -271,6 +272,17 @@ is deployed outside of the Kubernetes cluster.

---

## --local-filesystem-prefix

Since v0.14

Enables HAProxy Ingress to run in local mode. Define `--local-filesystem-prefix` with a temporary
directory HAProxy Ingress should create and maintain all the configuration files. Useful for local
deployment. Start HAProxy Ingress in the root directory of the repository when using
`--local-filesystem-prefix`, or simply use via `make run`.

---

## --master-socket

Since v0.12
Expand Down
1 change: 1 addition & 0 deletions pkg/common/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type Configuration struct {
ReloadStrategy string
MaxOldConfigFiles int
ValidateConfig bool
LocalFSPrefix string

ForceNamespaceIsolation bool
WaitBeforeShutdown int
Expand Down
24 changes: 16 additions & 8 deletions pkg/common/ingress/controller/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ discovery is attempted.`)
kubeConfigFile = flags.String("kubeconfig", "",
`Path to kubeconfig file with authorization and master location information.`)

localFSPrefix = flags.String("local-filesystem-prefix", "",
`Defines the prefix of a temporary directory HAProxy Ingress should create and
maintain all the configuration files. Useful for local deployment.`)

disableAPIWarnings = flags.Bool("disable-api-warnings", false,
`Disable warnings from the API server.`)

Expand Down Expand Up @@ -403,15 +407,18 @@ tracked.`)
glog.Fatalf("resync period (%vs) is too low", resyncPeriod.Seconds())
}

for _, dir := range []string{
ingress.DefaultCrtDirectory,
ingress.DefaultDHParamDirectory,
ingress.DefaultCACertsDirectory,
ingress.DefaultCrlDirectory,
ingress.DefaultMapsDirectory,
for _, dir := range []*string{
&ingress.DefaultCrtDirectory,
&ingress.DefaultDHParamDirectory,
&ingress.DefaultCACertsDirectory,
&ingress.DefaultCrlDirectory,
&ingress.DefaultVarRunDirectory,
&ingress.DefaultMapsDirectory,
} {
if err := os.MkdirAll(dir, 0755); err != nil {
glog.Fatalf("Failed to mkdir %s: %v", dir, err)
// TODO evolve this ugly trick to a proper struct that allows custom configuration
*dir = *localFSPrefix + *dir
if err := os.MkdirAll(*dir, 0755); err != nil {
glog.Fatalf("Failed to mkdir %s: %v", *dir, err)
}
}

Expand Down Expand Up @@ -477,6 +484,7 @@ tracked.`)
ReloadStrategy: *reloadStrategy,
MaxOldConfigFiles: *maxOldConfigFiles,
ValidateConfig: *validateConfig,
LocalFSPrefix: *localFSPrefix,
TCPConfigMapName: *tcpConfigMapName,
AnnPrefix: annPrefixList,
DefaultSSLCertificate: *defSSLCertificate,
Expand Down
3 changes: 3 additions & 0 deletions pkg/common/ingress/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ import (

// Default<Type>Directory defines the location where HAProxy Ingress' generated
// files should be created.
//
// These vars are dynamically changed, see launch.go
var (
DefaultCrtDirectory = "/var/lib/haproxy/crt"
DefaultDHParamDirectory = "/var/lib/haproxy/dhparam"
DefaultCACertsDirectory = "/var/lib/haproxy/cacerts"
DefaultCrlDirectory = "/var/lib/haproxy/crl"
DefaultVarRunDirectory = "/var/run/haproxy"
DefaultMapsDirectory = "/etc/haproxy/maps"
)

Expand Down
17 changes: 13 additions & 4 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,18 @@ func (hc *HAProxyController) configController() {
if hc.cfg.ReloadInterval.Seconds() > 0 {
hc.reloadQueue = utils.NewRateLimitingQueue(float32(1/hc.cfg.ReloadInterval.Seconds()), hc.reloadHAProxy)
}
var rootFSPrefix string
if hc.cfg.LocalFSPrefix != "" {
rootFSPrefix = "rootfs"
}
instanceOptions := haproxy.InstanceOptions{
HAProxyCfgDir: "/etc/haproxy",
RootFSPrefix: rootFSPrefix,
LocalFSPrefix: hc.cfg.LocalFSPrefix,
HAProxyCfgDir: hc.cfg.LocalFSPrefix + "/etc/haproxy",
HAProxyMapsDir: ingress.DefaultMapsDirectory,
MasterSocket: hc.cfg.MasterSocket,
AdminSocket: "/var/run/haproxy/admin.sock",
AdminSocket: ingress.DefaultVarRunDirectory + "/admin.sock",
AcmeSocket: ingress.DefaultVarRunDirectory + "/acme.sock",
BackendShards: hc.cfg.BackendShards,
AcmeSigner: acmeSigner,
AcmeQueue: hc.acmeQueue,
Expand All @@ -146,8 +153,10 @@ func (hc *HAProxyController) configController() {
Cache: hc.cache,
Tracker: hc.tracker,
DynamicConfig: hc.dynamicConfig,
LocalFSPrefix: hc.cfg.LocalFSPrefix,
MasterSocket: instanceOptions.MasterSocket,
AdminSocket: instanceOptions.AdminSocket,
AcmeSocket: instanceOptions.AcmeSocket,
AnnotationPrefix: hc.cfg.AnnPrefix,
DefaultBackend: hc.cfg.DefaultService,
DefaultCrtSecret: hc.cfg.DefaultSSLCertificate,
Expand Down Expand Up @@ -175,8 +184,7 @@ func (hc *HAProxyController) startServices() {
go hc.leaderelector.Run(hc.stopCh)
}
if hc.cfg.AcmeServer {
// TODO deduplicate acme socket
server := acme.NewServer(hc.logger, "/var/run/haproxy/acme.sock", hc.cache)
server := acme.NewServer(hc.logger, hc.converterOptions.AcmeSocket, hc.cache)
// TODO move goroutine from the server to the controller
if err := server.Listen(hc.stopCh); err != nil {
hc.logger.Fatal("error creating the acme server listener: %v", err)
Expand All @@ -190,6 +198,7 @@ func (hc *HAProxyController) startServices() {
}

func (hc *HAProxyController) stopServices() {
hc.instance.Shutdown()
hc.ingressQueue.ShutDown()
if hc.reloadQueue != nil {
hc.reloadQueue.ShutDown()
Expand Down
2 changes: 1 addition & 1 deletion pkg/converters/ingress/annotations/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (c *updater) buildGlobalAcme(d *globalData) {
d.acmeData.Expiring = time.Duration(d.mapper.Get(ingtypes.GlobalAcmeExpiring).Int()) * 24 * time.Hour
d.acmeData.TermsAgreed = termsAgreed
d.global.Acme.Prefix = "/.well-known/acme-challenge/"
d.global.Acme.Socket = "/var/run/haproxy/acme.sock"
d.global.Acme.Socket = c.options.AcmeSocket
d.global.Acme.Enabled = true
d.global.Acme.Shared = d.mapper.Get(ingtypes.GlobalAcmeShared).Bool()
}
Expand Down
1 change: 1 addition & 0 deletions pkg/converters/ingress/annotations/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func (c *updater) UpdateGlobalConfig(haproxyConfig haproxy.Config, mapper *Mappe
mapper: mapper,
}
d.global.AdminSocket = c.options.AdminSocket
d.global.LocalFSPrefix = c.options.LocalFSPrefix
d.global.MaxConn = mapper.Get(ingtypes.GlobalMaxConnections).Int()
d.global.DefaultBackendRedir = mapper.Get(ingtypes.GlobalDefaultBackendRedirect).String()
d.global.DefaultBackendRedirCode = mapper.Get(ingtypes.GlobalDefaultBackendRedirectCode).Int()
Expand Down
2 changes: 2 additions & 0 deletions pkg/converters/types/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ type ConverterOptions struct {
Cache Cache
Tracker Tracker
DynamicConfig *DynamicConfig
LocalFSPrefix string
MasterSocket string
AdminSocket string
AcmeSocket string
DefaultConfig func() map[string]string
DefaultBackend string
DefaultCrtSecret string
Expand Down
2 changes: 1 addition & 1 deletion pkg/haproxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (c *config) SyncConfig() {
// frontend with `inspect-delay` and `req.ssl_sni`
bindName := "_https_socket"
c.frontend.BindName = bindName
c.frontend.BindSocket = fmt.Sprintf("unix@/var/run/haproxy/%s.sock", bindName)
c.frontend.BindSocket = fmt.Sprintf("unix@%s/var/run/haproxy/%s.sock", c.global.LocalFSPrefix, bindName)
c.frontend.AcceptProxy = true
} else {
// One single HAProxy's frontend and bind
Expand Down
Loading

0 comments on commit c0d3c0c

Please sign in to comment.