├── .aspell.yml ├── .check-commit.yml ├── .dockerignore ├── .github ├── stale.yml └── workflows │ ├── .goreleaser.yml │ ├── actions.yml │ ├── docker_auto_release.yml │ ├── docker_description.yml │ ├── docker_manual_release.yml │ └── docker_nightly.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab └── kind-config.yaml ├── .golangci.yml ├── .goreleaser.yml ├── LICENSE ├── Makefile ├── Makefile.ci ├── README.md ├── assets ├── images │ └── haproxy-weblogo-210x49.png └── license-header.txt ├── bin ├── check-commit.sh └── lint-check.sh ├── build ├── Dockerfile ├── Dockerfile.dev └── Dockerfile.pebble ├── cmd └── linters │ └── main.go ├── crs ├── README.md ├── api │ └── ingress │ │ ├── v1 │ │ ├── backend.go │ │ ├── defaults.go │ │ ├── doc.go │ │ ├── global.go │ │ ├── tcp.go │ │ ├── zz_generated.deepcopy.go │ │ └── zz_generated.register.go │ │ └── v3 │ │ ├── backend.go │ │ ├── defaults.go │ │ ├── doc.go │ │ ├── global.go │ │ ├── tcp.go │ │ ├── zz_generated.deepcopy.go │ │ └── zz_generated.register.go ├── code-generator.sh ├── controller-gen │ └── version_check_test.go ├── converters │ ├── backend-spec.go │ ├── defaults-spec.go │ ├── global-spec.go │ ├── tcp-spec.go │ └── v1v3 │ │ └── convert.go ├── definition │ ├── embed.go │ ├── ingress.v1.haproxy.org_backends.yaml │ ├── ingress.v1.haproxy.org_defaults.yaml │ ├── ingress.v1.haproxy.org_globals.yaml │ ├── ingress.v1.haproxy.org_tcps.yaml │ ├── ingress.v3.haproxy.org_backends.yaml │ ├── ingress.v3.haproxy.org_defaults.yaml │ ├── ingress.v3.haproxy.org_globals.yaml │ └── ingress.v3.haproxy.org_tcps.yaml ├── generated │ └── api │ │ └── ingress │ │ ├── v1 │ │ ├── clientset │ │ │ └── versioned │ │ │ │ ├── clientset.go │ │ │ │ ├── fake │ │ │ │ ├── clientset_generated.go │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ ├── scheme │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ └── typed │ │ │ │ └── ingress │ │ │ │ └── v1 │ │ │ │ ├── backend.go │ │ │ │ ├── defaults.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_backend.go │ │ │ │ ├── fake_defaults.go │ │ │ │ ├── fake_global.go │ │ │ │ ├── fake_ingress_client.go │ │ │ │ └── fake_tcp.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── global.go │ │ │ │ ├── ingress_client.go │ │ │ │ └── tcp.go │ │ ├── informers │ │ │ └── externalversions │ │ │ │ ├── factory.go │ │ │ │ ├── generic.go │ │ │ │ ├── ingress │ │ │ │ ├── interface.go │ │ │ │ └── v1 │ │ │ │ │ ├── backend.go │ │ │ │ │ ├── defaults.go │ │ │ │ │ ├── global.go │ │ │ │ │ ├── interface.go │ │ │ │ │ └── tcp.go │ │ │ │ └── internalinterfaces │ │ │ │ └── factory_interfaces.go │ │ └── listers │ │ │ └── ingress │ │ │ ├── v1 │ │ │ ├── backend.go │ │ │ ├── defaults.go │ │ │ ├── expansion_generated.go │ │ │ ├── global.go │ │ │ └── tcp.go │ │ │ └── v3 │ │ │ ├── backend.go │ │ │ ├── defaults.go │ │ │ ├── expansion_generated.go │ │ │ ├── global.go │ │ │ └── tcp.go │ │ └── v3 │ │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ └── ingress │ │ │ └── v3 │ │ │ ├── backend.go │ │ │ ├── defaults.go │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_backend.go │ │ │ ├── fake_defaults.go │ │ │ ├── fake_global.go │ │ │ ├── fake_ingress_client.go │ │ │ └── fake_tcp.go │ │ │ ├── generated_expansion.go │ │ │ ├── global.go │ │ │ ├── ingress_client.go │ │ │ └── tcp.go │ │ ├── informers │ │ └── externalversions │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ ├── ingress │ │ │ ├── interface.go │ │ │ └── v3 │ │ │ │ ├── backend.go │ │ │ │ ├── defaults.go │ │ │ │ ├── global.go │ │ │ │ ├── interface.go │ │ │ │ └── tcp.go │ │ │ └── internalinterfaces │ │ │ └── factory_interfaces.go │ │ └── listers │ │ └── ingress │ │ ├── v1 │ │ ├── backend.go │ │ ├── defaults.go │ │ ├── expansion_generated.go │ │ ├── global.go │ │ └── tcp.go │ │ └── v3 │ │ ├── backend.go │ │ ├── defaults.go │ │ ├── expansion_generated.go │ │ ├── global.go │ │ └── tcp.go └── remove-fields-from-crds.sh ├── deploy ├── haproxy-ingress-daemonset.yaml ├── haproxy-ingress.yaml ├── kustomization.yaml └── tests │ ├── README.md │ ├── config │ ├── 0.namespace.yaml │ ├── 1.rbac.yaml │ ├── 2.2.ingressclass.yaml │ ├── 2.configmap.yaml │ ├── 3.ingress-controller.yaml │ ├── crd │ │ ├── job-crd.yaml │ │ └── rbac.yaml │ ├── echo-app.yaml │ └── experimental │ │ ├── gwapi-echo-app.yaml │ │ ├── gwapi-rbac.yaml │ │ ├── gwapi-resources.yaml │ │ └── gwapi.experimental.yaml │ ├── create.sh │ ├── delete.sh │ ├── e2e │ ├── access-control │ │ ├── access_control_test.go │ │ ├── config │ │ │ ├── deploy.yaml.tmpl │ │ │ ├── patternfile-a.yml │ │ │ └── patternfile-empty.yml │ │ └── suite_test.go │ ├── admin-port │ │ ├── pprof_test.go │ │ └── suite_test.go │ ├── basic-auth │ │ ├── basic_auth_test.go │ │ ├── config │ │ │ └── deploy.yaml.tmpl │ │ └── suite_test.go │ ├── canary-deployment │ │ ├── config │ │ │ └── deploy.yaml.tmpl │ │ ├── percentage_test.go │ │ └── suite_test.go │ ├── client.go │ ├── config-snippet │ │ ├── backend_cfg_snippet_test.go │ │ ├── config │ │ │ ├── backend-cfg-snippet.yaml │ │ │ ├── configmap.yaml │ │ │ └── deploy.yaml.tmpl │ │ ├── frontend_cfg_snippet_test.go │ │ └── suite_test.go │ ├── cookie-persistence │ │ ├── config │ │ │ └── deploy.yml.tmpl │ │ ├── cookie-persistence_test.go │ │ └── suite_test.go │ ├── cors │ │ ├── config │ │ │ ├── configmap.yaml.tmpl │ │ │ └── deploy.yaml.tmpl │ │ ├── cors_test.go │ │ └── suite_test.go │ ├── crd-tcp │ │ ├── config │ │ │ ├── crd-v1 │ │ │ │ ├── backend-cr.yaml │ │ │ │ ├── tcp-cr-add-services.yaml │ │ │ │ ├── tcp-cr-backend-switching-rule-acls.yaml │ │ │ │ ├── tcp-cr-backend-switching-rule.yaml │ │ │ │ ├── tcp-cr-full.yaml │ │ │ │ ├── tcp-cr-no-ingress-class.yaml │ │ │ │ ├── tcp-cr-ssl.yaml │ │ │ │ └── tcp-cr.yaml │ │ │ ├── crd-v3 │ │ │ │ ├── backend-cr.yaml │ │ │ │ ├── tcp-cr-add-services.yaml │ │ │ │ ├── tcp-cr-backend-switching-rule-acls.yaml │ │ │ │ ├── tcp-cr-backend-switching-rule.yaml │ │ │ │ ├── tcp-cr-full.yaml │ │ │ │ ├── tcp-cr-no-ingress-class.yaml │ │ │ │ ├── tcp-cr-ssl.yaml │ │ │ │ └── tcp-cr.yaml │ │ │ ├── deploy-index.yaml.tmpl │ │ │ ├── deploy.yaml.tmpl │ │ │ └── tcp-secret.yaml │ │ ├── cr_tcp_additional_services_test.go │ │ ├── cr_tcp_backend_switching_rule_test.go │ │ ├── cr_tcp_full_test.go │ │ ├── cr_tcp_no_ingress_class_test.go │ │ ├── cr_tcp_test.go │ │ └── suite_test.go │ ├── crd │ │ ├── config │ │ │ ├── backend.yaml.tmpl │ │ │ ├── crd-v1 │ │ │ │ └── global-full.yaml │ │ │ ├── crd-v3 │ │ │ │ └── global-full.yaml │ │ │ ├── defaults.yaml.tmpl │ │ │ ├── deploy.yaml │ │ │ ├── global.yaml.tmpl │ │ │ └── ingress.yaml.tmpl │ │ ├── cr_deploy_validation_test.go │ │ ├── cr_global_test.go │ │ └── suite_test.go │ ├── dump.go │ ├── endpoints │ │ ├── config │ │ │ ├── endpoints.yaml.tmpl │ │ │ └── tcp.yaml │ │ ├── http_test.go │ │ ├── not_ready_test.go │ │ ├── suite_test.go │ │ └── tcp_test.go │ ├── global-config │ │ ├── config │ │ │ ├── configmap-maxconn.yaml │ │ │ ├── configmap-pp-1.yaml │ │ │ └── configmap-pp-2.yaml │ │ ├── maxconn_test.go │ │ ├── proxy_protocol_test.go │ │ └── suite_test.go │ ├── haproxy-files │ │ ├── config │ │ │ ├── deploy.yaml.tmpl │ │ │ ├── errorfiles.yaml │ │ │ ├── patternfiles-1.yaml │ │ │ └── patternfiles-2.yaml │ │ ├── errorfiles_test.go │ │ ├── patternfiles_test.go │ │ └── suite_test.go │ ├── https-runtime │ │ ├── config │ │ │ ├── crd-v1 │ │ │ │ └── backend-crd.yaml │ │ │ ├── crd-v3 │ │ │ │ └── backend-crd.yaml │ │ │ ├── echo-app-offload-backend-crd.yaml │ │ │ ├── echo-app-offload-default.yaml │ │ │ ├── echo-app-offload.yaml │ │ │ ├── secret-default.yaml │ │ │ ├── secret-offload-1.yaml │ │ │ ├── secret-offload-2.yaml │ │ │ ├── secret-offload-3.yaml │ │ │ ├── secret-offload-4.yaml │ │ │ ├── secret-offload-with-4-content.yaml │ │ │ └── secret-offload.yaml │ │ ├── offload_runtime_test.go │ │ └── suite_test.go │ ├── https │ │ ├── config │ │ │ ├── deploy.yaml │ │ │ └── ingress.yaml.tmpl │ │ ├── offload_test.go │ │ ├── passthrough_test.go │ │ ├── redirect_test.go │ │ └── suite_test.go │ ├── ingress-match │ │ ├── config │ │ │ ├── deploy.yaml.tmpl │ │ │ └── ingress.yaml.tmpl │ │ ├── ingress_match_path_test.go │ │ └── suite_test.go │ ├── ingressclass │ │ ├── config │ │ │ ├── deploy.yaml │ │ │ ├── ingress.yaml.tmpl │ │ │ └── ingressclass.yaml │ │ ├── ingressClass_test.go │ │ └── suite_test.go │ ├── map-updates │ │ ├── config │ │ │ ├── deploy.yaml │ │ │ └── ingress.yaml.tmpl │ │ ├── suite_test.go │ │ └── update_test.go │ ├── rate-limiting │ │ ├── config │ │ │ ├── deploy.yaml │ │ │ └── ingress.yaml.tmpl │ │ ├── http_rate_limiting_test.go │ │ └── suite_test.go │ ├── send-proxy-protocol │ │ ├── config │ │ │ └── deploy.yaml.tmpl │ │ └── suite_test.go │ ├── service-discovery │ │ ├── config │ │ │ ├── deploy.yaml │ │ │ └── ingress.yaml.tmpl │ │ ├── port_discovery_test.go │ │ └── suite_test.go │ ├── set-header │ │ ├── config │ │ │ ├── deploy.yaml │ │ │ └── ingress.yaml.tmpl │ │ ├── set_header_test.go │ │ ├── set_host_test.go │ │ └── suite_test.go │ ├── source-ip │ │ ├── config │ │ │ └── deploy.yaml.tmpl │ │ ├── source_ip_test.go │ │ └── suite_test.go │ ├── tls-auth │ │ ├── client-certs │ │ │ ├── valid.crt │ │ │ ├── valid.key │ │ │ ├── wrong.crt │ │ │ └── wrong.key │ │ ├── client_auth_test.go │ │ ├── config │ │ │ ├── client-auth.yaml │ │ │ └── secrets │ │ │ │ ├── client-ca.yaml │ │ │ │ └── default-cert.yaml │ │ └── suite_test.go │ └── utils.go │ ├── images │ ├── http-echo │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── generate-cert.sh │ │ ├── go.mod │ │ ├── handlers.go │ │ └── main.go │ └── proxy-protocol │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── integration │ ├── base-suite.go │ ├── config-snippet │ │ ├── disable_configsnippet_test.go │ │ └── suite_test.go │ ├── customresources │ │ ├── customresource_test.go │ │ └── suite_test.go │ ├── pod-maxconn │ │ ├── pod-maxconn_test.go │ │ └── suite_test.go │ └── timeout-server │ │ ├── suite_test.go │ │ └── timeoutserver_test.go │ ├── kind-config.yaml │ ├── rebuild.sh │ ├── tnr │ └── routeacl │ │ ├── suite_test.go │ │ └── usebackend_test.go │ └── ut │ ├── acls │ ├── acls_test.go │ └── suite_test.go │ └── httprequests │ ├── httprequests_test.go │ └── suite_test.go ├── documentation ├── README.md ├── annotations.md ├── canary-deployment.md ├── controller.md ├── custom-resource-tcp.md ├── custom-resources.md ├── doc.yaml ├── gateway-api.md ├── gen │ ├── README.md │ ├── annotations.go │ ├── controller.go │ ├── go.mod │ ├── go.sum │ ├── lifecycle.go │ ├── main.go │ ├── readme.go │ ├── types.go │ └── version.go ├── ingressclass.md ├── lifecycle.md ├── lifecycle.yaml ├── pebble.md ├── prometheus.md ├── secondary-config.md └── tcp-cr-full-example │ ├── echo-0.yaml │ ├── echo-1.yaml │ ├── echo.yaml │ ├── haproxy.cfg │ └── tcp-cr-full.yaml ├── examples └── test-informers │ └── replay-delete.sh ├── fs ├── etc │ └── s6-overlay │ │ ├── s6-rc.d │ │ ├── aux-cfg │ │ │ ├── type │ │ │ └── up │ │ ├── haproxy │ │ │ ├── dependencies.d │ │ │ │ ├── aux-cfg │ │ │ │ ├── base │ │ │ │ └── sigusr1 │ │ │ ├── run │ │ │ └── type │ │ ├── ingress-controller │ │ │ ├── dependencies.d │ │ │ │ ├── aux-cfg │ │ │ │ ├── base │ │ │ │ ├── haproxy │ │ │ │ └── sigusr1 │ │ │ ├── finish │ │ │ ├── run │ │ │ └── type │ │ ├── sigusr1 │ │ │ ├── type │ │ │ └── up │ │ └── user │ │ │ └── contents.d │ │ │ ├── haproxy │ │ │ └── ingress-controller │ │ └── scripts │ │ ├── 00-sigusr1 │ │ └── 01-aux-cfg ├── start-pebble.sh ├── start.sh ├── usr │ └── local │ │ └── etc │ │ └── haproxy │ │ └── haproxy.cfg └── var │ └── lib │ └── pebble │ └── default │ ├── layers │ └── 001-haproxy.yaml │ ├── run-controller │ └── run-haproxy ├── go.mod ├── go.sum ├── main.go ├── pkg ├── annotations │ ├── annotations.go │ ├── cfgSnippet.go │ ├── cfgSnippetHandler.go │ ├── common │ │ └── main.go │ ├── global │ │ ├── connectionMode.go │ │ ├── hardStopAfter.go │ │ ├── logFormat.go │ │ ├── maxconn.go │ │ ├── nbthread.go │ │ ├── option.go │ │ ├── syslogServer.go │ │ └── timeout.go │ ├── ingress │ │ ├── accessControl.go │ │ ├── basicAuth.go │ │ ├── hostRedirect.go │ │ ├── httpsRedirect.go │ │ ├── reqCapture.go │ │ ├── reqPathRewrite.go │ │ ├── reqRateLimit.go │ │ ├── reqSetHdr.go │ │ ├── reqSetHost.go │ │ ├── resSetCORS.go │ │ └── srcIPHdr.go │ ├── models.go │ └── service │ │ ├── abortOnClose.go │ │ ├── backendForwardedFor.go │ │ ├── ca.go │ │ ├── check.go │ │ ├── checkHTTP.go │ │ ├── checkInter.go │ │ ├── cookie.go │ │ ├── crt.go │ │ ├── loadbalance.go │ │ ├── maxconn.go │ │ ├── proto.go │ │ ├── sendProxy.go │ │ ├── ssl.go │ │ ├── timeoutCheck.go │ │ └── timeoutServer.go ├── controller │ ├── builder.go │ ├── constants │ │ └── const.go │ ├── controller.go │ ├── global.go │ ├── handler.go │ └── monitor.go ├── fs │ ├── fs-delayed.go │ ├── fs.go │ └── fs_test.go ├── gateways │ ├── gateways.go │ ├── status.go │ └── updates.go ├── handler │ ├── errorfiles.go │ ├── globalcfg.go │ ├── handler.go │ ├── http-bind.go │ ├── https.go │ ├── pattern-files.go │ ├── pprof.go │ ├── prometheus.go │ ├── proxy-protocol.go │ ├── quic.go │ ├── refresh.go │ ├── tcp-cr.go │ └── tcp-services.go ├── haproxy │ ├── api │ │ ├── acl.go │ │ ├── api.go │ │ ├── backend.go │ │ ├── backend_switching_rule.go │ │ ├── capture.go │ │ ├── filters.go │ │ ├── frontend.go │ │ ├── global.go │ │ ├── httprequest.go │ │ ├── log_target.go │ │ ├── runtime.go │ │ ├── tcp_request_rule.go │ │ └── userlist.go │ ├── certs │ │ └── main.go │ ├── env │ │ ├── defaults.go │ │ └── main.go │ ├── instance │ │ └── configuration.go │ ├── main.go │ ├── maps │ │ └── main.go │ ├── process │ │ ├── direct-control.go │ │ ├── interface.go │ │ ├── pebble.go │ │ └── s6-overlay.go │ └── rules │ │ ├── main.go │ │ ├── reqAcceptContent.go │ │ ├── reqBasicAuth.go │ │ ├── reqCapture.go │ │ ├── reqDeny.go │ │ ├── reqInspectDelay.go │ │ ├── reqPathRewrite.go │ │ ├── reqProxyProtocol.go │ │ ├── reqRatelimit.go │ │ ├── reqRequestRedirect.go │ │ ├── reqRequestRedirectQuic.go │ │ ├── reqReturnStatus.go │ │ ├── reqSetSrc.go │ │ ├── reqSetVar.go │ │ ├── reqTrack.go │ │ ├── setHdr.go │ │ └── types.go ├── ingress │ ├── ingress.go │ ├── status.go │ └── types.go ├── job │ └── crd-check.go ├── k8s │ ├── cr-backend.go │ ├── cr-defaults.go │ ├── cr-global.go │ ├── cr-tcp.go │ ├── crs-deprecated-v1.go │ ├── crs-monitor.go │ ├── informer-utils.go │ ├── informers.go │ ├── logging.go │ ├── main.go │ ├── meta │ │ ├── meta.go │ │ └── store.go │ ├── sync │ │ └── sync.go │ ├── transform │ │ ├── transform-backend.go │ │ ├── transform-common.go │ │ ├── transform-configmap.go │ │ ├── transform-endpoints.go │ │ ├── transform-ingress.go │ │ ├── transform-ingressclass.go │ │ ├── transform-namespace.go │ │ ├── transform-secret.go │ │ └── transform-service.go │ └── types.go ├── metrics │ └── prometheus.go ├── reference-counter │ └── reference-counter.go ├── route │ └── route.go ├── rules │ ├── acls │ │ └── acls.go │ ├── backend_switching_rules │ │ └── backend_switching_rules.go │ ├── binds │ │ └── binds.go │ ├── captures │ │ └── captures.go │ ├── constant.go │ ├── filters │ │ └── filters.go │ ├── httprequests │ │ └── httprequests.go │ ├── log_targets │ │ └── log_targets.go │ └── tcp_request_rules │ │ └── tcp_request_rules.go ├── secret │ └── secret.go ├── service │ ├── endpoints.go │ └── service.go ├── status │ └── updatestatus.go ├── store │ ├── convert.go │ ├── events-cr-backend.go │ ├── events-cr-defaults.go │ ├── events-cr-global.go │ ├── events-cr-tcp.go │ ├── events-gateway.go │ ├── events.go │ ├── status.go │ ├── store.go │ ├── stringw.go │ ├── types-configmap.go │ ├── types-endpoints.go │ ├── types-equal.go │ ├── types-equal_test.go │ ├── types-ingress.go │ ├── types-ingressclass.go │ ├── types-namespace.go │ ├── types-pod.go │ ├── types-secret.go │ ├── types-service.go │ ├── types-tcp-cr.go │ ├── types-utils.go │ └── types.go ├── utils │ ├── errors.go │ ├── flags-experimental.go │ ├── flags.go │ ├── logging.go │ ├── orderedset.go │ ├── types-equal.go │ └── utils.go └── version │ ├── runtime.go │ └── version.go └── test ├── annotations ├── cfgSnippet_test.go └── loadbalance_test.go ├── crs ├── v1-deep-copy │ ├── backend_test.go │ ├── defaults_test.go │ └── global_test.go └── v3-deep-copy │ ├── backend_test.go │ ├── defaults_test.go │ ├── global_test.go │ └── tcp_test.go ├── tcp-cr ├── expectations │ ├── check-collisions │ │ ├── 2-collisions.yaml │ │ ├── coll-address-port-2.yaml │ │ ├── coll-address-port.yaml │ │ └── coll-fe-name.yaml │ ├── has-collisions │ │ ├── coll-address-port-2.yaml │ │ ├── coll-address-port-3.yaml │ │ ├── coll-address-port.yaml │ │ └── coll-fe-name.yaml │ ├── no-collision.yaml │ └── ordered.yaml ├── manifests │ ├── tcp1-coll-address-port-2.yaml │ ├── tcp1-coll-address-port.yaml │ ├── tcp1-coll-fe-name.yaml │ ├── tcp1.yaml │ ├── tcp2-coll-address-port.yaml │ ├── tcp2.yaml │ ├── tcp3.yaml │ ├── tcp4.yaml │ └── unordered.yaml └── types-tcp-cr_test.go └── transform ├── data ├── ingress │ ├── dupl-1.yaml │ ├── expectation │ │ └── no-duplicate.yaml │ └── no-duplicate.yaml ├── rule │ ├── dupl-1.yaml │ ├── expectation │ │ └── no-duplicate.yaml │ ├── no-dupl-1.yaml │ ├── no-dupl-2.yaml │ └── no-duplicate.yaml └── tls │ ├── dupl-both.yaml │ ├── dupl-hosts.yaml │ ├── dupl-tls-2.yaml │ ├── dupl-tls.yaml │ ├── expectation │ ├── dupl-tls-2.yaml │ └── no-duplicate.yaml │ └── no-duplicate.yaml └── transform-ingress_test.go /.aspell.yml: -------------------------------------------------------------------------------- 1 | mode: commit 2 | min_length: 3 3 | allowed: 4 | - aspell 5 | - repo 6 | - yaml 7 | - config 8 | - Github 9 | - Gitlab 10 | - env 11 | - failsafe 12 | - golang 13 | - mkdir 14 | - WORKDIR 15 | - apk 16 | - ENTRYPOINT 17 | - ubuntu 18 | - golangci 19 | - sudo 20 | - releaser 21 | - backend 22 | - backends 23 | - frontend 24 | - frontends 25 | - tcp 26 | - crd 27 | - linter 28 | - linters 29 | - tls 30 | - lifecycle 31 | - passthrough 32 | - ssl 33 | - unix 34 | - parallelize 35 | - maxconn 36 | - kubebuilder 37 | - cfg 38 | - optim 39 | - prometheus 40 | - configmaps 41 | - namespace 42 | - namespaces 43 | - http 44 | - CORS 45 | -------------------------------------------------------------------------------- /.check-commit.yml: -------------------------------------------------------------------------------- 1 | HelpText: "Please refer to https://github.com/haproxy/haproxy/blob/master/CONTRIBUTING#L632" 2 | PatchScopes: 3 | HAProxy Standard Scope: 4 | - MINOR 5 | - MEDIUM 6 | - MAJOR 7 | - CRITICAL 8 | PatchTypes: 9 | HAProxy Standard Patch: 10 | Values: 11 | - BUG 12 | - BUILD 13 | - CLEANUP 14 | - DOC 15 | - LICENSE 16 | - OPTIM 17 | - RELEASE 18 | - REORG 19 | - TEST 20 | - REVERT 21 | Scope: HAProxy Standard Scope 22 | HAProxy Standard Feature Commit: 23 | Values: 24 | - MINOR 25 | - MEDIUM 26 | - MAJOR 27 | - CRITICAL 28 | Custom Resource patch: 29 | Values: 30 | - CR 31 | Continuous Integration patch: 32 | Values: 33 | - CI 34 | TagOrder: 35 | - PatchTypes: 36 | - HAProxy Standard Patch 37 | - HAProxy Standard Feature Commit 38 | - PatchTypes: 39 | - Custom Resource patch 40 | Optional: true 41 | - PatchTypes: 42 | - Continuous Integration patch 43 | Optional: true 44 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .github 2 | .gitlab 3 | .dockerignore 4 | build 5 | assets 6 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - enhancement 8 | - bug 9 | - documentation 10 | # Label to use when marking an issue as stale 11 | staleLabel: stale 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | -------------------------------------------------------------------------------- /.github/workflows/.goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | on: 3 | push: 4 | tags: 5 | - "*" 6 | jobs: 7 | goreleaser: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | with: 13 | # we have to fetch all history to be able to generate the release note. c.f. https://goreleaser.com/ci/actions/. 14 | fetch-depth: 0 15 | - name: Set up Go 16 | uses: actions/setup-go@v5 17 | with: 18 | go-version-file: "go.mod" 19 | check-latest: true 20 | - name: Run GoReleaser 21 | uses: goreleaser/goreleaser-action@v4 22 | with: 23 | distribution: goreleaser 24 | version: 1.17.1 25 | args: release --clean 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/docker_description.yml: -------------------------------------------------------------------------------- 1 | name: Update Docker Hub description 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - README.md 8 | workflow_dispatch: 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | env: 13 | DOCKER_IMAGE: haproxytech/kubernetes-ingress 14 | steps: 15 | - name: Check out repo 16 | id: checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Update Docker Hub description 20 | id: description 21 | uses: peter-evans/dockerhub-description@v2 22 | with: 23 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 24 | password: ${{ secrets.DOCKER_HUB_PASSWORD }} 25 | repository: ${{ env.DOCKER_IMAGE }} 26 | short-description: ${{ github.event.repository.description }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .idea/* 3 | .test/* 4 | kubernetes-ingress 5 | dist/ 6 | .code-generator/ 7 | bin/golangci-lint 8 | bin/check-commit 9 | .local/* 10 | __debug_bin* 11 | -------------------------------------------------------------------------------- /.gitlab/kind-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kind.x-k8s.io/v1alpha4 2 | networking: 3 | ipFamily: dual 4 | apiServerAddress: 0.0.0.0 5 | apiServerPort: 6443 6 | # add to the apiServer certSANs the name of the docker (dind) service in order to be able to reach the cluster through it 7 | kubeadmConfigPatchesJSON6902: 8 | - group: kubeadm.k8s.io 9 | version: KUBEADM_VER 10 | kind: ClusterConfiguration 11 | patch: | 12 | - op: add 13 | path: /apiServer/certSANs/- 14 | value: docker 15 | kind: Cluster 16 | nodes: 17 | - role: control-plane 18 | image: CI_REGISTRY_GO/kindest/node:K8S_VERSION 19 | extraPortMappings: 20 | - hostPort: 30080 21 | containerPort: 30080 22 | #listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0" 23 | #protocol: udp # Optional, defaults to tcp 24 | - hostPort: 30443 25 | containerPort: 30443 26 | - hostPort: 31024 27 | containerPort: 31024 28 | - hostPort: 32766 29 | containerPort: 32766 30 | - hostPort: 32767 31 | containerPort: 32767 32 | kubeadmConfigPatches: 33 | - | 34 | kind: ClusterConfiguration 35 | controllerManager: 36 | extraArgs: 37 | max-endpoints-per-slice: "5" -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters-settings: 2 | govet: 3 | shadow: true 4 | gocyclo: 5 | min-complexity: 42 6 | cyclop: 7 | max-complexity: 42 8 | dupl: 9 | threshold: 200 10 | revive: 11 | rules: 12 | - name: var-naming 13 | severity: warning 14 | disabled: true 15 | linters: 16 | enable-all: true 17 | disable: 18 | - ireturn 19 | - tagliatelle 20 | - dupl 21 | - exhaustive 22 | - funlen 23 | - gci 24 | - gochecknoglobals 25 | - gocognit 26 | - goconst 27 | - gocyclo 28 | - godot 29 | - lll 30 | - nestif 31 | - nlreturn 32 | - wrapcheck 33 | - wsl 34 | - nakedret 35 | - paralleltest 36 | - testpackage 37 | - varnamelen 38 | - exhaustruct 39 | - nonamedreturns 40 | - forcetypeassert 41 | - depguard 42 | - mnd 43 | - inamedparam 44 | - asasalint 45 | - err113 # maybe tmp disable 46 | - recvcheck # maybe tmp disable 47 | - tenv # deprecated 48 | issues: 49 | exclude: 50 | - "tag is not aligned, should be:" # this is harder to read 51 | -------------------------------------------------------------------------------- /Makefile.ci: -------------------------------------------------------------------------------- 1 | .PHONY: ci-e2e-parallel 2 | ci-e2e-parallel: 3 | go clean -testcache 4 | KIND_URL=docker CGO_ENABLED=0 gotest -t e2e_parallel 5 | 6 | .PHONY: ci-e2e-https 7 | ci-e2e-https: 8 | go clean -testcache 9 | KIND_URL=docker CGO_ENABLED=0 gotest -t e2e_https 10 | 11 | .PHONY: ci-e2e-sequential-1 12 | ci-e2e-sequential-1: 13 | go clean -testcache 14 | KIND_URL=docker CGO_ENABLED=0 gotest -t e2e_sequential -n 2 -s 0 -p 1 15 | 16 | .PHONY: ci-e2e-sequential-2 17 | ci-e2e-sequential-2: 18 | go clean -testcache 19 | KIND_URL=docker CGO_ENABLED=0 gotest -t e2e_sequential -n 2 -s 1 -p 1 20 | -------------------------------------------------------------------------------- /assets/images/haproxy-weblogo-210x49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/assets/images/haproxy-weblogo-210x49.png -------------------------------------------------------------------------------- /assets/license-header.txt: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /bin/check-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | V=$(./check-commit tag) 3 | 4 | if echo "$V" | grep -q "v$CHECK_COMMIT"; then 5 | echo "$V" 6 | else 7 | echo "go install github.com/haproxytech/check-commit/v5@v$CHECK_COMMIT" 8 | GOBIN=$(pwd) go install github.com/haproxytech/check-commit/v5@v$CHECK_COMMIT 9 | fi 10 | -------------------------------------------------------------------------------- /bin/lint-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | V=$(./golangci-lint --version) 3 | 4 | case "$V" in 5 | *$GOLANGCI_LINT_VERSION*) echo "$V" ;; 6 | *) curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(pwd) "v$GOLANGCI_LINT_VERSION" ;; 7 | esac 8 | -------------------------------------------------------------------------------- /crs/api/ingress/v1/doc.go: -------------------------------------------------------------------------------- 1 | // Package v1 contains the core v1 API group 2 | // 3 | // +k8s:deepcopy-gen=package 4 | // +groupName=ingress.v1.haproxy.org 5 | package v1 6 | -------------------------------------------------------------------------------- /crs/api/ingress/v3/doc.go: -------------------------------------------------------------------------------- 1 | // Package v3 contains the core v3 API group 2 | // 3 | // +k8s:deepcopy-gen=package 4 | // +groupName=ingress.v3.haproxy.org 5 | package v3 6 | -------------------------------------------------------------------------------- /crs/controller-gen/version_check_test.go: -------------------------------------------------------------------------------- 1 | package controllergenversion 2 | 3 | // Blank import just to ensure via go.mod that we have a specific controller-gen version 4 | import _ "sigs.k8s.io/controller-tools/pkg/version" 5 | -------------------------------------------------------------------------------- /crs/converters/defaults-spec.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package converters 16 | 17 | import ( 18 | cnv2 "github.com/haproxytech/client-native/v5/models" 19 | convert "github.com/haproxytech/client-native/v6/configuration/convert/v2v3" 20 | cnv3 "github.com/haproxytech/client-native/v6/models" 21 | v1 "github.com/haproxytech/kubernetes-ingress/crs/api/ingress/v1" 22 | v3 "github.com/haproxytech/kubernetes-ingress/crs/api/ingress/v3" 23 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 24 | ) 25 | 26 | func DeepConvertDefaultsSpecV1toV3(i v1.DefaultsSpec) v3.DefaultsSpec { 27 | v3Defaults := v3.DefaultsSpec{} 28 | logger := utils.GetLogger() 29 | 30 | // Defaults 31 | defaultsv3t, err := convert.V2Tov3[cnv2.Defaults, cnv3.Defaults](i.Config) 32 | if err != nil { 33 | logger.Error(err) 34 | return v3.DefaultsSpec{} 35 | } 36 | v3Defaults.Defaults = *defaultsv3t 37 | 38 | return v3Defaults 39 | } 40 | -------------------------------------------------------------------------------- /crs/definition/embed.go: -------------------------------------------------------------------------------- 1 | package definition 2 | 3 | import _ "embed" 4 | 5 | //go:embed ingress.v3.haproxy.org_defaults.yaml 6 | var Defaults []byte 7 | 8 | //go:embed ingress.v3.haproxy.org_globals.yaml 9 | var Globals []byte 10 | 11 | //go:embed ingress.v3.haproxy.org_backends.yaml 12 | var Backends []byte 13 | 14 | //go:embed ingress.v3.haproxy.org_tcps.yaml 15 | var TCPs []byte 16 | 17 | func GetCRDs() map[string][]byte { 18 | return map[string][]byte{ 19 | "defaults.ingress.v3.haproxy.org": Defaults, 20 | "globals.ingress.v3.haproxy.org": Globals, 21 | "backends.ingress.v3.haproxy.org": Backends, 22 | "tcps.ingress.v3.haproxy.org": TCPs, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v1/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated fake clientset. 19 | package fake 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v1/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package contains the scheme of the automatically generated clientset. 19 | package scheme 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v1/clientset/versioned/typed/ingress/v1/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated typed clients. 19 | package v1 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v1/clientset/versioned/typed/ingress/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // Package fake has the automatically generated clients. 19 | package fake 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v1/clientset/versioned/typed/ingress/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | package v1 19 | 20 | type BackendExpansion interface{} 21 | 22 | type DefaultsExpansion interface{} 23 | 24 | type GlobalExpansion interface{} 25 | 26 | type TCPExpansion interface{} 27 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v3/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated fake clientset. 19 | package fake 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v3/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package contains the scheme of the automatically generated clientset. 19 | package scheme 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v3/clientset/versioned/typed/ingress/v3/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated typed clients. 19 | package v3 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v3/clientset/versioned/typed/ingress/v3/fake/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // Package fake has the automatically generated clients. 19 | package fake 20 | -------------------------------------------------------------------------------- /crs/generated/api/ingress/v3/clientset/versioned/typed/ingress/v3/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 HAProxy Technologies LLC 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | package v3 19 | 20 | type BackendExpansion interface{} 21 | 22 | type DefaultsExpansion interface{} 23 | 24 | type GlobalExpansion interface{} 25 | 26 | type TCPExpansion interface{} 27 | -------------------------------------------------------------------------------- /crs/remove-fields-from-crds.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | yq -i 'del(.spec.versions[0].schema.openAPIV3Schema.properties.spec.properties.servers)' crs/definition/ingress.v3.haproxy.org_backends.yaml 4 | -------------------------------------------------------------------------------- /deploy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - haproxy-ingress.yaml 5 | -------------------------------------------------------------------------------- /deploy/tests/config/0.namespace.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: haproxy-controller 6 | -------------------------------------------------------------------------------- /deploy/tests/config/2.2.ingressclass.yaml: -------------------------------------------------------------------------------- 1 | kind: IngressClass 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: haproxy 5 | spec: 6 | controller: haproxy.org/ingress-controller/haproxy 7 | -------------------------------------------------------------------------------- /deploy/tests/config/2.configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: haproxy-kubernetes-ingress 5 | namespace: haproxy-controller 6 | data: 7 | global-config-snippet: | 8 | stats socket 0.0.0.0:31024 9 | syslog-server: | 10 | address: stdout, format: raw, facility:daemon 11 | maxconn: "1000" 12 | server-slots: "4" 13 | timeout-client: 50s 14 | timeout-connect: 5s 15 | timeout-http-keep-alive: 1m 16 | timeout-http-request: 5s 17 | timeout-queue: 5s 18 | timeout-server: 50s 19 | timeout-tunnel: 1h 20 | cr-global: haproxy-controller/global-full 21 | -------------------------------------------------------------------------------- /deploy/tests/config/crd/job-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: haproxy-ingress-crd # each deploymnent should have a unique name, in example we always recreate the custer 5 | namespace: haproxy-controller 6 | spec: 7 | template: 8 | spec: 9 | serviceAccountName: haproxy-kubernetes-ingress-crd 10 | containers: 11 | - name: haproxy-ingress-crd 12 | image: haproxytech/kubernetes-ingress:latest 13 | imagePullPolicy: Never 14 | command: ["./haproxy-ingress-controller","--job-check-crd"] 15 | restartPolicy: Never 16 | backoffLimit: 0 17 | -------------------------------------------------------------------------------- /deploy/tests/config/crd/rbac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: haproxy-kubernetes-ingress-crd 6 | namespace: haproxy-controller 7 | --- 8 | kind: ClusterRole 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | metadata: 11 | name: haproxy-kubernetes-ingress-crd 12 | rules: 13 | - apiGroups: 14 | - "apiextensions.k8s.io" 15 | resources: 16 | - customresourcedefinitions 17 | verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] 18 | --- 19 | kind: ClusterRoleBinding 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | metadata: 22 | name: haproxy-kubernetes-ingress-crd 23 | namespace: haproxy-controller 24 | roleRef: 25 | apiGroup: rbac.authorization.k8s.io 26 | kind: ClusterRole 27 | name: haproxy-kubernetes-ingress-crd 28 | subjects: 29 | - kind: ServiceAccount 30 | name: haproxy-kubernetes-ingress-crd 31 | namespace: haproxy-controller 32 | -------------------------------------------------------------------------------- /deploy/tests/config/echo-app.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: http-echo 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: http-echo 10 | template: 11 | metadata: 12 | labels: 13 | app: http-echo 14 | spec: 15 | containers: 16 | - name: http-echo 17 | image: "haproxytech/http-echo:latest" 18 | imagePullPolicy: Never 19 | ports: 20 | - name: http 21 | containerPort: 8888 22 | protocol: TCP 23 | - name: https 24 | containerPort: 8443 25 | protocol: TCP 26 | --- 27 | kind: Service 28 | apiVersion: v1 29 | metadata: 30 | name: http-echo 31 | spec: 32 | ports: 33 | - name: http 34 | protocol: TCP 35 | port: 80 36 | targetPort: http 37 | - name: https 38 | protocol: TCP 39 | port: 443 40 | targetPort: https 41 | selector: 42 | app: http-echo 43 | --- 44 | kind: Ingress 45 | apiVersion: networking.k8s.io/v1 46 | metadata: 47 | name: http-echo 48 | spec: 49 | ingressClassName: haproxy 50 | rules: 51 | - host: "echo.haproxy.local" 52 | http: 53 | paths: 54 | - path: / 55 | pathType: Prefix 56 | backend: 57 | service: 58 | name: http-echo 59 | port: 60 | name: http 61 | -------------------------------------------------------------------------------- /deploy/tests/config/experimental/gwapi-echo-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1alpha2 2 | kind: TCPRoute 3 | metadata: 4 | name: route1 5 | namespace: default 6 | spec: 7 | parentRefs: 8 | - group: gateway.networking.k8s.io 9 | kind: Gateway 10 | name: gateway1 11 | namespace: default 12 | rules: 13 | - backendRefs: 14 | - group: '' 15 | kind: Service 16 | name: http-echo 17 | namespace: default 18 | port: 80 19 | weight: 13 20 | -------------------------------------------------------------------------------- /deploy/tests/config/experimental/gwapi-rbac.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRole 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: haproxy-kubernetes-ingress-gwapi 5 | rules: 6 | - apiGroups: 7 | - "gateway.networking.k8s.io" 8 | resources: 9 | - referencegrants 10 | - gateways 11 | - gatewayclasses 12 | - tcproutes 13 | verbs: 14 | - get 15 | - list 16 | - watch 17 | - apiGroups: 18 | - "gateway.networking.k8s.io" 19 | resources: 20 | - gatewayclasses/status 21 | - gateways/status 22 | - tcproutes/status 23 | verbs: 24 | - update 25 | --- 26 | kind: ClusterRoleBinding 27 | apiVersion: rbac.authorization.k8s.io/v1 28 | metadata: 29 | name: haproxy-kubernetes-ingress-gwapi 30 | namespace: haproxy-controller 31 | roleRef: 32 | apiGroup: rbac.authorization.k8s.io 33 | kind: ClusterRole 34 | name: haproxy-kubernetes-ingress-gwapi 35 | subjects: 36 | - kind: ServiceAccount 37 | name: haproxy-kubernetes-ingress 38 | namespace: haproxy-controller 39 | -------------------------------------------------------------------------------- /deploy/tests/config/experimental/gwapi-resources.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: gateway.networking.k8s.io/v1alpha2 3 | kind: GatewayClass 4 | metadata: 5 | name: haproxy-gwc 6 | spec: 7 | controllerName: haproxy.org/gateway-controller 8 | --- 9 | apiVersion: gateway.networking.k8s.io/v1alpha2 10 | kind: Gateway 11 | metadata: 12 | name: gateway1 13 | namespace: default 14 | spec: 15 | gatewayClassName: haproxy-gwc 16 | listeners: 17 | - allowedRoutes: 18 | kinds: 19 | - group: gateway.networking.k8s.io 20 | kind: TCPRoute 21 | namespaces: 22 | from: All 23 | name: listener1 24 | port: 8000 25 | protocol: TCP 26 | --- 27 | apiVersion: gateway.networking.k8s.io/v1alpha2 28 | kind: ReferenceGrant 29 | metadata: 30 | name: refgrantns1 31 | namespace: default 32 | spec: 33 | from: 34 | - group: "gateway.networking.k8s.io" 35 | kind: "TCPRoute" 36 | namespace: default 37 | to: 38 | - group: "" 39 | kind: "Service" 40 | -------------------------------------------------------------------------------- /deploy/tests/delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clustername=${1:-dev} 3 | kind delete cluster --name $clustername 4 | -------------------------------------------------------------------------------- /deploy/tests/e2e/access-control/config/patternfile-a.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: patternfiles 5 | namespace: haproxy-controller 6 | data: 7 | ips: | 8 | 192.168.0.0/24 9 | 192.168.1.0/24 10 | ips2: | 11 | 192.169.0.0/24 12 | 192.169.1.0/24 13 | -------------------------------------------------------------------------------- /deploy/tests/e2e/access-control/config/patternfile-empty.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: patternfiles 5 | namespace: haproxy-controller 6 | data: {} 7 | -------------------------------------------------------------------------------- /deploy/tests/e2e/basic-auth/suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build e2e_parallel 16 | 17 | package basicauth 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/stretchr/testify/suite" 23 | 24 | "github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e" 25 | ) 26 | 27 | type HTTPBasicAuthSuite struct { 28 | suite.Suite 29 | test e2e.Test 30 | client *e2e.Client 31 | tmplData tmplData 32 | } 33 | 34 | type tmplData struct { 35 | Host string 36 | } 37 | 38 | func (suite *HTTPBasicAuthSuite) SetupSuite() { 39 | var err error 40 | suite.test, err = e2e.NewTest() 41 | suite.Require().NoError(err) 42 | suite.tmplData = tmplData{Host: suite.test.GetNS() + ".test"} 43 | suite.client, err = e2e.NewHTTPClient(suite.tmplData.Host) 44 | suite.Require().NoError(err) 45 | } 46 | 47 | func (suite *HTTPBasicAuthSuite) TearDownSuite() { 48 | suite.test.TearDown() 49 | } 50 | 51 | func TestHTTPBasicAuthSuite(t *testing.T) { 52 | suite.Run(t, new(HTTPBasicAuthSuite)) 53 | } 54 | -------------------------------------------------------------------------------- /deploy/tests/e2e/config-snippet/config/backend-cfg-snippet.yaml: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: http-echo 5 | annotations: 6 | backend-config-snippet: | 7 | http-request set-header e2e-test %[str(test)] 8 | spec: 9 | ipFamilyPolicy: RequireDualStack 10 | ports: 11 | - name: http 12 | protocol: TCP 13 | port: 80 14 | targetPort: http 15 | - name: https 16 | protocol: TCP 17 | port: 443 18 | targetPort: https 19 | selector: 20 | app: http-echo 21 | -------------------------------------------------------------------------------- /deploy/tests/e2e/config-snippet/config/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: haproxy-kubernetes-ingress 5 | namespace: haproxy-controller 6 | data: 7 | frontend-config-snippet: | 8 | unique-id-format %{+X}o\ %ci:%cp_%fi:%fp_%Ts_%rt:%pid 9 | unique-id-header X-Unique-ID 10 | syslog-server: | 11 | address: stdout, format: raw, facility:daemon 12 | maxconn: "1000" 13 | server-slots: "4" 14 | timeout-client: 50s 15 | timeout-connect: 5s 16 | timeout-http-keep-alive: 1m 17 | timeout-http-request: 5s 18 | timeout-queue: 5s 19 | timeout-server: 50s 20 | timeout-tunnel: 1h 21 | -------------------------------------------------------------------------------- /deploy/tests/e2e/config-snippet/config/deploy.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Deployment 3 | apiVersion: apps/v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: http-echo 11 | template: 12 | metadata: 13 | labels: 14 | app: http-echo 15 | spec: 16 | containers: 17 | - name: http-echo 18 | image: haproxytech/http-echo:latest 19 | imagePullPolicy: Never 20 | args: 21 | ports: 22 | - name: http 23 | containerPort: 8888 24 | protocol: TCP 25 | - name: https 26 | containerPort: 8443 27 | protocol: TCP 28 | --- 29 | kind: Service 30 | apiVersion: v1 31 | metadata: 32 | name: http-echo 33 | spec: 34 | ipFamilyPolicy: RequireDualStack 35 | ports: 36 | - name: http 37 | protocol: TCP 38 | port: 80 39 | targetPort: http 40 | - name: https 41 | protocol: TCP 42 | port: 443 43 | targetPort: https 44 | selector: 45 | app: http-echo 46 | --- 47 | kind: Ingress 48 | apiVersion: networking.k8s.io/v1 49 | metadata: 50 | name: http-echo 51 | spec: 52 | ingressClassName: haproxy 53 | rules: 54 | - host: {{ .Host }} 55 | http: 56 | paths: 57 | - path: / 58 | pathType: Prefix 59 | backend: 60 | service: 61 | name: http-echo 62 | port: 63 | name: http 64 | -------------------------------------------------------------------------------- /deploy/tests/e2e/cors/config/configmap.yaml.tmpl: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: haproxy-kubernetes-ingress 5 | namespace: haproxy-controller 6 | data: 7 | {{range .ConfigMapAnnotations}} 8 | {{ .Key }}: {{ .Value }} 9 | {{end}} 10 | global-config-snippet: | 11 | stats socket 0.0.0.0:31024 12 | syslog-server: | 13 | address: stdout, format: raw, facility:daemon 14 | maxconn: "1000" 15 | server-slots: "4" 16 | timeout-client: 50s 17 | timeout-connect: 5s 18 | timeout-http-keep-alive: 1m 19 | timeout-http-request: 5s 20 | timeout-queue: 5s 21 | timeout-server: 50s 22 | timeout-tunnel: 1h -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/backend-cr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: Backend 3 | metadata: 4 | name: mybackend 5 | spec: 6 | config: 7 | balance: 8 | algorithm: "leastconn" 9 | abortonclose: disabled 10 | name: foo 11 | default_server: 12 | verify: none 13 | resolve-prefer: ipv4 14 | check-sni: example.com 15 | sni: str(example.com) 16 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/tcp-cr-add-services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | - name: tcp-http-echo-80 9 | frontend: 10 | name: fe-http-echo-80 11 | tcplog: true 12 | log_format: "%{+Q}o %t %s" 13 | binds: 14 | - name: v4 15 | port: 32766 16 | - name: v4v6 17 | address: "::" 18 | port: 32766 19 | v4v6: true 20 | service: 21 | name: "http-echo" 22 | port: 80 23 | services: 24 | - name: "http-echo-2" 25 | port: 443 26 | - name: "http-echo-2" 27 | port: 80 28 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/tcp-cr-backend-switching-rule-acls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | - name: tcp-test 9 | frontend: 10 | name: fe-http-echo 11 | tcplog: true 12 | log_format: "%{+Q}o %t %s" 13 | binds: 14 | - name: v4 15 | address: 0.0.0.0 16 | port: 32766 17 | acl_list: 18 | - acl_name: switch_be_0 19 | criterion: req_ssl_sni 20 | index: 0 21 | value: -i backend0.example.com 22 | - acl_name: switch_be_1 23 | criterion: req_ssl_sni 24 | index: 1 25 | value: -i backend1.example.com 26 | backend_switching_rule_list: 27 | - cond: if 28 | cond_test: 'switch_be_0' 29 | index: 0 30 | name: e2e-tests-crd-tcp_http-echo-0_https 31 | - cond: if 32 | cond_test: 'switch_be_1' 33 | index: 1 34 | name: e2e-tests-crd-tcp_http-echo-1_https 35 | tcp_request_rule_list: 36 | - type: inspect-delay 37 | timeout: 5000 38 | index: 0 39 | - type: content 40 | action: accept 41 | cond: if 42 | cond_test: "{ req_ssl_hello_type 1 }" 43 | index: 1 44 | service: 45 | name: "http-echo" 46 | port: 443 47 | services: 48 | - name: "http-echo-0" 49 | port: 443 50 | - name: "http-echo-1" 51 | port: 443 52 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/tcp-cr-backend-switching-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | - name: tcp-test 9 | frontend: 10 | name: fe-http-echo 11 | tcplog: true 12 | log_format: "%{+Q}o %t %s" 13 | binds: 14 | - name: v4 15 | address: 0.0.0.0 16 | port: 32766 17 | backend_switching_rule_list: 18 | - cond: if 19 | cond_test: '{ req_ssl_sni -i backend0.example.com }' 20 | index: 0 21 | name: e2e-tests-crd-tcp_http-echo-0_https 22 | - cond: if 23 | cond_test: '{ req_ssl_sni -i backend1.example.com }' 24 | index: 1 25 | name: e2e-tests-crd-tcp_http-echo-1_https 26 | tcp_request_rule_list: 27 | - type: inspect-delay 28 | timeout: 5000 29 | index: 0 30 | - type: content 31 | action: accept 32 | cond: if 33 | cond_test: "{ req_ssl_hello_type 1 }" 34 | index: 1 35 | service: 36 | name: "http-echo" 37 | port: 443 38 | services: 39 | - name: "http-echo-0" 40 | port: 443 41 | - name: "http-echo-1" 42 | port: 443 43 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/tcp-cr-no-ingress-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | spec: 6 | - name: tcp-http-echo-80 7 | frontend: 8 | name: fe-http-echo-80 9 | tcplog: true 10 | log_format: "%{+Q}o %t %s" 11 | binds: 12 | - name: v4 13 | port: 32766 14 | - name: v4v6 15 | address: "::" 16 | port: 32766 17 | v4v6: true 18 | service: 19 | name: "http-echo" 20 | port: 80 21 | services: 22 | - name: "http-echo-2" 23 | port: 443 24 | - name: "http-echo-2" 25 | port: 80 26 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/tcp-cr-ssl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | - name: tcp-http-echo-443 9 | frontend: 10 | name: fe-http-echo-443 11 | tcplog: true 12 | log_format: "%{+Q}o %t %s" 13 | binds: 14 | - name: v4 15 | ssl: true 16 | ssl_certificate: tcp-test-cert 17 | port: 32766 18 | - name: v4v6 19 | address: "::" 20 | port: 32766 21 | v4v6: true 22 | service: 23 | name: "http-echo" 24 | port: 443 25 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v1/tcp-cr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | - name: tcp-http-echo-80 9 | frontend: 10 | name: fe-http-echo-80 11 | tcplog: true 12 | log_format: "%{+Q}o %t %s" 13 | binds: 14 | - name: v4 15 | port: 32766 16 | - name: v4v6 17 | address: "::" 18 | port: 32766 19 | v4v6: true 20 | service: 21 | name: "http-echo" 22 | port: 80 23 | - name: tcp-http-echo-81 24 | frontend: 25 | name: fe-http-echo-81 26 | tcplog: true 27 | log_format: "%{+Q}o %t %s" 28 | binds: 29 | - name: v4acceptproxy 30 | port: 32767 31 | accept_proxy: true 32 | service: 33 | name: "http-echo" 34 | port: 81 35 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/backend-cr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: Backend 3 | metadata: 4 | creationTimestamp: null 5 | name: mybackend 6 | spec: 7 | abortonclose: disabled 8 | balance: 9 | algorithm: leastconn 10 | default_server: 11 | check-sni: example.com 12 | resolve-prefer: ipv4 13 | sni: str(example.com) 14 | verify: none 15 | name: foo 16 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/tcp-cr-add-services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: TCP 3 | metadata: 4 | name: tcp-1 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | - name: tcp-http-echo-80 9 | frontend: 10 | name: fe-http-echo-80 11 | tcplog: true 12 | log_format: "%{+Q}o %t %s" 13 | binds: 14 | v4: 15 | port: 32766 16 | name: v4 17 | v4v6: 18 | address: "::" 19 | name: v4v6 20 | port: 32766 21 | v4v6: true 22 | service: 23 | name: "http-echo" 24 | port: 80 25 | services: 26 | - name: "http-echo-2" 27 | port: 443 28 | - name: "http-echo-2" 29 | port: 80 30 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/tcp-cr-backend-switching-rule-acls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: TCP 3 | metadata: 4 | annotations: 5 | ingress.class: haproxy 6 | creationTimestamp: null 7 | name: tcp-1 8 | spec: 9 | - frontend: 10 | acl_list: 11 | - acl_name: switch_be_0 12 | criterion: req_ssl_sni 13 | value: -i backend0.example.com 14 | - acl_name: switch_be_1 15 | criterion: req_ssl_sni 16 | value: -i backend1.example.com 17 | backend_switching_rule_list: 18 | - cond: if 19 | cond_test: switch_be_0 20 | name: e2e-tests-crd-tcp_http-echo-0_https 21 | - cond: if 22 | cond_test: switch_be_1 23 | name: e2e-tests-crd-tcp_http-echo-1_https 24 | binds: 25 | v4: 26 | address: 0.0.0.0 27 | name: v4 28 | port: 32766 29 | log_format: '%{+Q}o %t %s' 30 | name: fe-http-echo 31 | tcp_request_rule_list: 32 | - timeout: 5000 33 | type: inspect-delay 34 | - action: accept 35 | cond: if 36 | cond_test: '{ req_ssl_hello_type 1 }' 37 | type: content 38 | tcplog: true 39 | name: tcp-test 40 | service: 41 | name: http-echo 42 | port: 443 43 | services: 44 | - name: http-echo-0 45 | port: 443 46 | - name: http-echo-1 47 | port: 443 48 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/tcp-cr-backend-switching-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: TCP 3 | metadata: 4 | annotations: 5 | ingress.class: haproxy 6 | creationTimestamp: null 7 | name: tcp-1 8 | spec: 9 | - frontend: 10 | backend_switching_rule_list: 11 | - cond: if 12 | cond_test: '{ req_ssl_sni -i backend0.example.com }' 13 | name: e2e-tests-crd-tcp_http-echo-0_https 14 | - cond: if 15 | cond_test: '{ req_ssl_sni -i backend1.example.com }' 16 | name: e2e-tests-crd-tcp_http-echo-1_https 17 | binds: 18 | v4: 19 | address: 0.0.0.0 20 | name: v4 21 | port: 32766 22 | log_format: '%{+Q}o %t %s' 23 | name: fe-http-echo 24 | tcp_request_rule_list: 25 | - timeout: 5000 26 | type: inspect-delay 27 | - action: accept 28 | cond: if 29 | cond_test: '{ req_ssl_hello_type 1 }' 30 | type: content 31 | tcplog: true 32 | name: tcp-test 33 | service: 34 | name: http-echo 35 | port: 443 36 | services: 37 | - name: http-echo-0 38 | port: 443 39 | - name: http-echo-1 40 | port: 443 41 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/tcp-cr-no-ingress-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: TCP 3 | metadata: 4 | creationTimestamp: null 5 | name: tcp-1 6 | spec: 7 | - frontend: 8 | binds: 9 | v4: 10 | name: v4 11 | port: 32766 12 | v4v6: 13 | address: '::' 14 | name: v4v6 15 | port: 32766 16 | v4v6: true 17 | log_format: '%{+Q}o %t %s' 18 | name: fe-http-echo-80 19 | tcplog: true 20 | name: tcp-http-echo-80 21 | service: 22 | name: http-echo 23 | port: 80 24 | services: 25 | - name: http-echo-2 26 | port: 443 27 | - name: http-echo-2 28 | port: 80 29 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/tcp-cr-ssl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: TCP 3 | metadata: 4 | annotations: 5 | ingress.class: haproxy 6 | creationTimestamp: null 7 | name: tcp-1 8 | spec: 9 | - frontend: 10 | binds: 11 | v4: 12 | name: v4 13 | port: 32766 14 | ssl: true 15 | ssl_certificate: tcp-test-cert 16 | v4v6: 17 | address: '::' 18 | name: v4v6 19 | port: 32766 20 | v4v6: true 21 | log_format: '%{+Q}o %t %s' 22 | name: fe-http-echo-443 23 | tcplog: true 24 | name: tcp-http-echo-443 25 | service: 26 | name: http-echo 27 | port: 443 28 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/crd-v3/tcp-cr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: TCP 3 | metadata: 4 | annotations: 5 | ingress.class: haproxy 6 | creationTimestamp: null 7 | name: tcp-1 8 | spec: 9 | - frontend: 10 | binds: 11 | v4: 12 | name: v4 13 | port: 32766 14 | v4v6: 15 | address: '::' 16 | name: v4v6 17 | port: 32766 18 | v4v6: true 19 | log_format: '%{+Q}o %t %s' 20 | name: fe-http-echo-80 21 | tcplog: true 22 | name: tcp-http-echo-80 23 | service: 24 | name: http-echo 25 | port: 80 26 | - frontend: 27 | binds: 28 | v4acceptproxy: 29 | accept_proxy: true 30 | name: v4acceptproxy 31 | port: 32767 32 | log_format: '%{+Q}o %t %s' 33 | name: fe-http-echo-81 34 | tcplog: true 35 | name: tcp-http-echo-81 36 | service: 37 | name: http-echo 38 | port: 81 39 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/deploy-index.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Deployment 3 | apiVersion: apps/v1 4 | metadata: 5 | name: http-echo-{{ .EchoAppIndex }} 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: http-echo-{{ .EchoAppIndex }} 11 | template: 12 | metadata: 13 | labels: 14 | app: http-echo-{{ .EchoAppIndex }} 15 | spec: 16 | containers: 17 | - name: http-echo 18 | image: haproxytech/http-echo:latest 19 | imagePullPolicy: Never 20 | args: 21 | - --default-response=hostname 22 | ports: 23 | - name: http 24 | containerPort: 8888 25 | protocol: TCP 26 | - name: https 27 | containerPort: 8443 28 | protocol: TCP 29 | --- 30 | kind: Service 31 | apiVersion: v1 32 | metadata: 33 | name: http-echo-{{ .EchoAppIndex }} 34 | spec: 35 | ipFamilyPolicy: RequireDualStack 36 | ports: 37 | - name: http 38 | protocol: TCP 39 | port: 80 40 | targetPort: http 41 | - name: https 42 | protocol: TCP 43 | port: 443 44 | targetPort: https 45 | selector: 46 | app: http-echo-{{ .EchoAppIndex }} 47 | --- 48 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd-tcp/config/deploy.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Deployment 3 | apiVersion: apps/v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: http-echo 11 | template: 12 | metadata: 13 | labels: 14 | app: http-echo 15 | spec: 16 | containers: 17 | - name: http-echo 18 | image: haproxytech/http-echo:latest 19 | imagePullPolicy: Never 20 | args: 21 | - --default-response=hostname 22 | ports: 23 | - name: http 24 | containerPort: 8888 25 | protocol: TCP 26 | - name: https 27 | containerPort: 8443 28 | protocol: TCP 29 | --- 30 | kind: Service 31 | apiVersion: v1 32 | metadata: 33 | name: http-echo 34 | {{- if ne .BackendCrName "" }} 35 | annotations: 36 | cr-backend: {{ .BackendCrNamespace }}/{{ .BackendCrName }} 37 | {{- end }} 38 | spec: 39 | ipFamilyPolicy: RequireDualStack 40 | ports: 41 | - name: http 42 | protocol: TCP 43 | port: 80 44 | targetPort: http 45 | - name: https 46 | protocol: TCP 47 | port: 443 48 | targetPort: https 49 | - name: http2 50 | protocol: TCP 51 | port: 81 52 | targetPort: http 53 | selector: 54 | app: http-echo 55 | --- 56 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd/config/deploy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Deployment 3 | apiVersion: apps/v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: http-echo 11 | template: 12 | metadata: 13 | creationTimestamp: null 14 | labels: 15 | app: http-echo 16 | spec: 17 | containers: 18 | - name: http-echo 19 | image: haproxytech/http-echo:latest 20 | imagePullPolicy: Never 21 | args: 22 | - --default-response=hostname 23 | ports: 24 | - name: http 25 | containerPort: 8888 26 | protocol: TCP 27 | - name: https 28 | containerPort: 8443 29 | protocol: TCP 30 | --- 31 | kind: Service 32 | apiVersion: v1 33 | metadata: 34 | name: http-echo 35 | spec: 36 | ipFamilyPolicy: RequireDualStack 37 | ports: 38 | - name: http 39 | protocol: TCP 40 | port: 80 41 | targetPort: http 42 | - name: https 43 | protocol: TCP 44 | port: 443 45 | targetPort: https 46 | selector: 47 | app: http-echo 48 | -------------------------------------------------------------------------------- /deploy/tests/e2e/crd/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Ingress 3 | apiVersion: networking.k8s.io/v1 4 | metadata: 5 | name: http-echo 6 | annotations: 7 | {{- range .IngAnnotations}} 8 | {{ .Key }}: "{{ .Value }}" 9 | {{- end}} 10 | spec: 11 | ingressClassName: haproxy 12 | rules: 13 | - host: {{ .Host }} 14 | http: 15 | paths: 16 | - path: / 17 | pathType: Prefix 18 | backend: 19 | service: 20 | name: http-echo 21 | port: 22 | name: http 23 | -------------------------------------------------------------------------------- /deploy/tests/e2e/dump.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package e2e 16 | 17 | import "strings" 18 | 19 | func (t Test) GetIngressControllerFile(path string) (string, error) { 20 | po, err := t.getIngressControllerPod() 21 | if err != nil { 22 | return "", err 23 | } 24 | out, errExec := t.execute("", "kubectl", "exec", "-i", "-n", "haproxy-controller", 25 | po, "--", "cat", path) 26 | 27 | return out, errExec 28 | } 29 | 30 | func (t Test) getIngressControllerPod() (string, error) { 31 | out, errExec := t.execute("", "kubectl", "get", "pods", "-n", "haproxy-controller", 32 | "-l", "run=haproxy-ingress", "-o", "name", "--field-selector=status.phase==Running", "-l", "run=haproxy-ingress") 33 | return strings.TrimSuffix(out, "\n"), errExec 34 | } 35 | -------------------------------------------------------------------------------- /deploy/tests/e2e/endpoints/config/tcp.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: haproxy-kubernetes-ingress-tcp 6 | namespace: haproxy-controller 7 | data: 8 | 32766: "e2e-tests-endpoints/http-echo:443" 9 | -------------------------------------------------------------------------------- /deploy/tests/e2e/endpoints/not_ready_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build e2e_sequential 16 | 17 | package endpoints 18 | 19 | import ( 20 | "github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e" 21 | ) 22 | 23 | func (suite *EndpointsSuite) Test_Non_Ready_Endpoints() { 24 | suite.tmplData.NotReady = true 25 | suite.tmplData.Replicas = 3 26 | suite.Require().NoError(suite.test.Apply("config/endpoints.yaml.tmpl", suite.test.GetNS(), suite.tmplData)) 27 | suite.Require().Eventually(func() bool { 28 | res, cls, err := suite.client.Do() 29 | if res == nil { 30 | suite.T().Log(err) 31 | return false 32 | } 33 | defer cls() 34 | return res.StatusCode == 200 35 | }, e2e.WaitDuration, e2e.TickDuration) 36 | } 37 | -------------------------------------------------------------------------------- /deploy/tests/e2e/endpoints/tcp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build e2e_sequential 16 | 17 | package endpoints 18 | 19 | import "io" 20 | 21 | func (suite *EndpointsSuite) Test_TCP_Reach() { 22 | counter := map[string]int{} 23 | for i := 0; i < 4; i++ { 24 | func() { 25 | res, cls, err := suite.client.Do() 26 | if err != nil { 27 | suite.Require().NoError(err) 28 | return 29 | } 30 | defer cls() 31 | body, err := io.ReadAll(res.Body) 32 | if err != nil { 33 | suite.Error(err) 34 | return 35 | } 36 | counter[string(body)]++ 37 | }() 38 | } 39 | for _, v := range counter { 40 | suite.Equal(4, v) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /deploy/tests/e2e/global-config/config/configmap-maxconn.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: haproxy-kubernetes-ingress 5 | namespace: haproxy-controller 6 | data: 7 | # Mandatory config 8 | global-config-snippet: | 9 | stats socket 0.0.0.0:31024 10 | syslog-server: | 11 | address: stdout, format: raw, facility:daemon 12 | # Optional config 13 | maxconn: "1111" 14 | server-slots: "4" 15 | timeout-client: 50s 16 | timeout-connect: 5s 17 | timeout-http-keep-alive: 1m 18 | timeout-http-request: 5s 19 | timeout-queue: 5s 20 | timeout-server: 50s 21 | timeout-tunnel: 1h 22 | -------------------------------------------------------------------------------- /deploy/tests/e2e/global-config/config/configmap-pp-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: haproxy-kubernetes-ingress 5 | namespace: haproxy-controller 6 | data: 7 | # Mandatory config 8 | global-config-snippet: | 9 | stats socket 0.0.0.0:31024 10 | syslog-server: | 11 | address: stdout, format: raw, facility:daemon 12 | # Optional config 13 | proxy-protocol: 0.0.0.0/0 14 | maxconn: "1000" 15 | server-slots: "4" 16 | timeout-client: 50s 17 | timeout-connect: 5s 18 | timeout-http-keep-alive: 1m 19 | timeout-http-request: 5s 20 | timeout-queue: 5s 21 | timeout-server: 50s 22 | timeout-tunnel: 1h 23 | -------------------------------------------------------------------------------- /deploy/tests/e2e/global-config/config/configmap-pp-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: haproxy-kubernetes-ingress 5 | namespace: haproxy-controller 6 | data: 7 | # Mandatory config 8 | global-config-snippet: | 9 | stats socket 0.0.0.0:31024 10 | syslog-server: | 11 | address: stdout, format: raw, facility:daemon 12 | # Optional config 13 | proxy-protocol: 192.168.1.1 14 | maxconn: "1000" 15 | server-slots: "4" 16 | timeout-client: 50s 17 | timeout-connect: 5s 18 | timeout-http-keep-alive: 1m 19 | timeout-http-request: 5s 20 | timeout-queue: 5s 21 | timeout-server: 50s 22 | timeout-tunnel: 1h 23 | -------------------------------------------------------------------------------- /deploy/tests/e2e/global-config/maxconn_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build e2e_sequential 16 | 17 | package globalconfig 18 | 19 | import ( 20 | "github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e" 21 | ) 22 | 23 | func (suite *GlobalConfigSuite) TestMaxconn() { 24 | suite.Require().NoError(suite.test.Apply("config/configmap-maxconn.yaml", "", nil)) 25 | suite.maxconn = "1111" 26 | suite.Eventually(suite.checkMaxconn, e2e.WaitDuration, e2e.TickDuration) 27 | 28 | suite.Require().NoError(suite.test.Apply("../../config/2.configmap.yaml", "", nil)) 29 | suite.maxconn = "1000" 30 | suite.Eventually(suite.checkMaxconn, e2e.WaitDuration, e2e.TickDuration) 31 | } 32 | 33 | func (suite *GlobalConfigSuite) checkMaxconn() bool { 34 | r, err := e2e.GetGlobalHAProxyInfo() 35 | if err != nil { 36 | suite.T().Log(err) 37 | return false 38 | } 39 | return r.Maxconn == suite.maxconn 40 | } 41 | -------------------------------------------------------------------------------- /deploy/tests/e2e/global-config/suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build e2e_sequential 16 | 17 | package globalconfig 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/stretchr/testify/suite" 23 | 24 | "github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e" 25 | ) 26 | 27 | type GlobalConfigSuite struct { 28 | suite.Suite 29 | test e2e.Test 30 | maxconn string 31 | } 32 | 33 | func (suite *GlobalConfigSuite) SetupSuite() { 34 | var err error 35 | suite.test, err = e2e.NewTest() 36 | suite.Require().NoError(err) 37 | } 38 | 39 | func (suite *GlobalConfigSuite) TearDownSuite() { 40 | suite.test.TearDown() 41 | } 42 | 43 | func TestGlobalConfigSuite(t *testing.T) { 44 | suite.Run(t, new(GlobalConfigSuite)) 45 | } 46 | -------------------------------------------------------------------------------- /deploy/tests/e2e/haproxy-files/config/deploy.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | ipFamilyPolicy: RequireDualStack 8 | ports: 9 | - name: http 10 | protocol: TCP 11 | port: 80 12 | targetPort: http 13 | - name: https 14 | protocol: TCP 15 | port: 443 16 | targetPort: https 17 | selector: 18 | app: http-echo 19 | --- 20 | kind: Ingress 21 | apiVersion: networking.k8s.io/v1 22 | metadata: 23 | name: http-echo 24 | annotations: 25 | backend-config-snippet: http-after-response set-header result %[var(txn.path),ltrim(/),map(patterns/mapping)] 26 | spec: 27 | ingressClassName: haproxy 28 | rules: 29 | - host: {{ .Host }} 30 | http: 31 | paths: 32 | - path: / 33 | pathType: Prefix 34 | backend: 35 | service: 36 | name: http-echo 37 | port: 38 | name: http 39 | -------------------------------------------------------------------------------- /deploy/tests/e2e/haproxy-files/config/errorfiles.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: errorfiles 5 | namespace: haproxy-controller 6 | data: 7 | 503: |- 8 | HTTP/1.1 521 Web server is down 9 | Cache-Control: no-cache 10 | Connection: close 11 | Content-Type: text/html 12 | 13 |

Oops, that's embarrassing!

14 | There are no servers available to handle your request. 15 | 16 | -------------------------------------------------------------------------------- /deploy/tests/e2e/haproxy-files/config/patternfiles-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: patternfiles 5 | namespace: haproxy-controller 6 | data: 7 | mapping: | 8 | H+DbITVEMAJDRS4EzVF4gVDfTmyUFB3RzEkCIST9 0 9 | GBxqv9YZHQ5dBLMjTjBbyjd4/wS0F0ETZtsnLsYI 1 10 | 7tFRKRoboeFENmfTSHj+gjJKFjtOw2u+G+1d13rO 2 11 | -------------------------------------------------------------------------------- /deploy/tests/e2e/haproxy-files/config/patternfiles-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: patternfiles 5 | namespace: haproxy-controller 6 | data: 7 | mapping: | 8 | F3ITB+V5w0Yo8ZeS8LO3mkNlFBOxS3i9TDYfVjJv 0 9 | Yn9MvTzjAeLqp7MKE7g7thlf6jc2WRYTNoya+Cqb 1 10 | Y6FX16EEqxJr/B9M2Pzzt/NvivoDjZE2FTr4boBb 2 11 | -------------------------------------------------------------------------------- /deploy/tests/e2e/https-runtime/config/crd-v1/backend-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v1.haproxy.org/v1 2 | kind: Backend 3 | metadata: 4 | name: be-test 5 | spec: 6 | config: 7 | name: test-be-simple 8 | abortonclose: "enabled" 9 | accept_invalid_http_response: "enabled" 10 | default_server: 11 | check: "enabled" 12 | acls: 13 | - acl_name: switch_be_0 14 | criterion: req_ssl_sni 15 | index: 0 16 | value: -i backend100.example.com 17 | -------------------------------------------------------------------------------- /deploy/tests/e2e/https-runtime/config/crd-v3/backend-crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: ingress.v3.haproxy.org/v3 2 | kind: Backend 3 | metadata: 4 | creationTimestamp: null 5 | name: be-test 6 | spec: 7 | abortonclose: enabled 8 | accept_invalid_http_response: enabled 9 | acl_list: 10 | - acl_name: switch_be_0 11 | criterion: req_ssl_sni 12 | value: -i backend100.example.com 13 | default_server: 14 | check: enabled 15 | name: test-be-simple 16 | -------------------------------------------------------------------------------- /deploy/tests/e2e/https-runtime/suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build e2e_sequential 16 | 17 | package httpsruntime 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/stretchr/testify/suite" 23 | 24 | "github.com/haproxytech/kubernetes-ingress/deploy/tests/e2e" 25 | ) 26 | 27 | type HTTPSSuite struct { 28 | suite.Suite 29 | test e2e.Test 30 | client *e2e.Client 31 | tmplData tmplData 32 | } 33 | 34 | type tmplData struct { 35 | Port string 36 | } 37 | 38 | func (suite *HTTPSSuite) SetupSuite() { 39 | var err error 40 | suite.test, err = e2e.NewTest() 41 | suite.Require().NoError(err) 42 | } 43 | 44 | func (suite *HTTPSSuite) TearDownSuite() { 45 | suite.test.TearDown() 46 | } 47 | 48 | func TestHTTPSSuite(t *testing.T) { 49 | suite.Run(t, new(HTTPSSuite)) 50 | } 51 | -------------------------------------------------------------------------------- /deploy/tests/e2e/https/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | kind: Ingress 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: http-echo 5 | annotations: 6 | {{range .IngAnnotations}} 7 | {{ .Key }}: {{ .Value}} 8 | {{end}} 9 | spec: 10 | ingressClassName: haproxy 11 | {{if .TLSEnabled}} 12 | tls: 13 | - hosts: 14 | - {{ .Host }} 15 | secretName: haproxy-offload-test 16 | - hosts: 17 | - default.haproxy 18 | secretName: haproxy-default # Another cert to make sure HAProxy picks the right one 19 | {{end}} 20 | rules: 21 | - host: {{ .Host }} 22 | http: 23 | paths: 24 | - path: / 25 | pathType: Prefix 26 | backend: 27 | service: 28 | name: http-echo 29 | port: 30 | name: {{ .Port }} 31 | -------------------------------------------------------------------------------- /deploy/tests/e2e/ingress-match/config/deploy.yaml.tmpl: -------------------------------------------------------------------------------- 1 | {{- range $_,$i := .Apps }} 2 | --- 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: http-echo-{{$i}} 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: http-echo-{{$i}} 12 | template: 13 | metadata: 14 | labels: 15 | app: http-echo-{{$i}} 16 | spec: 17 | containers: 18 | - name: http-echo-{{$i}} 19 | image: haproxytech/http-echo:latest 20 | imagePullPolicy: Never 21 | args: 22 | - --default-response=hostname 23 | ports: 24 | - name: http 25 | containerPort: 8888 26 | protocol: TCP 27 | - name: https 28 | containerPort: 8443 29 | protocol: TCP 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: http-echo-{{$i}} 35 | spec: 36 | ipFamilyPolicy: RequireDualStack 37 | type: ClusterIP 38 | ports: 39 | - name: http 40 | protocol: TCP 41 | port: 80 42 | targetPort: http 43 | - name: https 44 | protocol: TCP 45 | port: 443 46 | targetPort: https 47 | selector: 48 | app: http-echo-{{$i}} 49 | {{- end }} 50 | -------------------------------------------------------------------------------- /deploy/tests/e2e/ingress-match/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | {{$pathType:=.PathTypeSupported}} 2 | --- 3 | kind: Ingress 4 | apiVersion: networking.k8s.io/v1 5 | metadata: 6 | name: http-echo 7 | spec: 8 | ingressClassName: haproxy 9 | rules: 10 | {{- range .Rules }} 11 | - host: "{{.Host}}" 12 | http: 13 | paths: 14 | - path: {{.Path}} 15 | {{- if $pathType}} 16 | pathType: {{.PathType}} 17 | {{- end}} 18 | backend: 19 | service: 20 | name: {{.Service}} 21 | port: 22 | name: http 23 | {{- end}} 24 | -------------------------------------------------------------------------------- /deploy/tests/e2e/ingressclass/config/deploy.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: http-echo 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: http-echo 9 | template: 10 | metadata: 11 | labels: 12 | app: http-echo 13 | spec: 14 | containers: 15 | - name: http-echo 16 | image: haproxytech/http-echo:latest 17 | imagePullPolicy: Never 18 | args: 19 | - --default-response=hostname 20 | ports: 21 | - name: http 22 | containerPort: 8888 23 | protocol: TCP 24 | - name: https 25 | containerPort: 8443 26 | protocol: TCP 27 | --- 28 | kind: Service 29 | apiVersion: v1 30 | metadata: 31 | name: http-echo 32 | spec: 33 | ipFamilyPolicy: RequireDualStack 34 | ports: 35 | - name: http 36 | protocol: TCP 37 | port: 80 38 | targetPort: http 39 | - name: https 40 | protocol: TCP 41 | port: 443 42 | targetPort: https 43 | selector: 44 | app: http-echo 45 | -------------------------------------------------------------------------------- /deploy/tests/e2e/ingressclass/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | kind: Ingress 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: http-echo 5 | spec: 6 | {{ if .IngressClassName}} 7 | ingressClassName: {{ .IngressClassName}} 8 | {{ end }} 9 | rules: 10 | - host: {{ .Host }} 11 | http: 12 | paths: 13 | - path: / 14 | pathType: Prefix 15 | backend: 16 | service: 17 | name: http-echo 18 | port: 19 | name: http 20 | -------------------------------------------------------------------------------- /deploy/tests/e2e/ingressclass/config/ingressclass.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: IngressClass 4 | metadata: 5 | name: haproxy 6 | spec: 7 | controller: haproxy.org/ingress-controller/haproxy 8 | -------------------------------------------------------------------------------- /deploy/tests/e2e/map-updates/config/deploy.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: http-echo 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: http-echo 10 | template: 11 | metadata: 12 | labels: 13 | app: http-echo 14 | spec: 15 | containers: 16 | - name: http-echo 17 | image: mo3m3n/http-echo:v1.0.0 18 | args: 19 | ports: 20 | - name: http 21 | containerPort: 8888 22 | protocol: TCP 23 | - name: https 24 | containerPort: 8443 25 | protocol: TCP 26 | --- 27 | kind: Service 28 | apiVersion: v1 29 | metadata: 30 | name: http-echo 31 | spec: 32 | ipFamilyPolicy: RequireDualStack 33 | ports: 34 | - name: http 35 | protocol: TCP 36 | port: 80 37 | targetPort: http 38 | - name: https 39 | protocol: TCP 40 | port: 443 41 | targetPort: https 42 | selector: 43 | app: http-echo 44 | -------------------------------------------------------------------------------- /deploy/tests/e2e/map-updates/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Ingress 3 | apiVersion: networking.k8s.io/v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | ingressClassName: haproxy 8 | rules: 9 | - host: {{ .Host }} 10 | http: 11 | paths: 12 | {{- range $path := .Paths}} 13 | - path: /{{$path}} 14 | pathType: ImplementationSpecific 15 | backend: 16 | service: 17 | name: http-echo 18 | port: 19 | name: http 20 | {{- end}} 21 | -------------------------------------------------------------------------------- /deploy/tests/e2e/rate-limiting/config/deploy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Deployment 3 | apiVersion: apps/v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: http-echo 11 | template: 12 | metadata: 13 | creationTimestamp: null 14 | labels: 15 | app: http-echo 16 | spec: 17 | containers: 18 | - name: http-echo 19 | image: haproxytech/http-echo:latest 20 | imagePullPolicy: Never 21 | args: 22 | - --default-response=hostname 23 | ports: 24 | - name: http 25 | containerPort: 8888 26 | protocol: TCP 27 | - name: https 28 | containerPort: 8443 29 | protocol: TCP 30 | --- 31 | kind: Service 32 | apiVersion: v1 33 | metadata: 34 | name: http-echo 35 | spec: 36 | ipFamilyPolicy: RequireDualStack 37 | ports: 38 | - name: http 39 | protocol: TCP 40 | port: 80 41 | targetPort: http 42 | - name: https 43 | protocol: TCP 44 | port: 443 45 | targetPort: https 46 | selector: 47 | app: http-echo 48 | -------------------------------------------------------------------------------- /deploy/tests/e2e/rate-limiting/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Ingress 3 | apiVersion: networking.k8s.io/v1 4 | metadata: 5 | name: http-echo 6 | annotations: 7 | {{- range .IngAnnotations}} 8 | {{ .Key }}: "{{ .Value }}" 9 | {{- end}} 10 | spec: 11 | ingressClassName: haproxy 12 | rules: 13 | - host: {{ .Host }} 14 | http: 15 | paths: 16 | - path: / 17 | pathType: Prefix 18 | backend: 19 | service: 20 | name: http-echo 21 | port: 22 | name: http 23 | -------------------------------------------------------------------------------- /deploy/tests/e2e/send-proxy-protocol/config/deploy.yaml.tmpl: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: http-echo 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: http-echo 10 | template: 11 | metadata: 12 | labels: 13 | app: http-echo 14 | spec: 15 | containers: 16 | - name: http-echo 17 | image: 'haproxytech/proxy-protocol:latest' 18 | imagePullPolicy: IfNotPresent 19 | ports: 20 | - name: http 21 | containerPort: 8080 22 | protocol: TCP 23 | --- 24 | kind: Service 25 | apiVersion: v1 26 | metadata: 27 | name: http-echo 28 | annotations: 29 | send-proxy-protocol: proxy-v1 30 | spec: 31 | ipFamilyPolicy: RequireDualStack 32 | ports: 33 | - name: http 34 | protocol: TCP 35 | port: 8080 36 | targetPort: http 37 | selector: 38 | app: http-echo 39 | --- 40 | kind: Ingress 41 | apiVersion: networking.k8s.io/v1 42 | metadata: 43 | name: http-echo 44 | spec: 45 | ingressClassName: haproxy 46 | rules: 47 | - host: {{ .Host }} 48 | http: 49 | paths: 50 | - path: / 51 | pathType: Prefix 52 | backend: 53 | service: 54 | name: http-echo 55 | port: 56 | name: http 57 | -------------------------------------------------------------------------------- /deploy/tests/e2e/service-discovery/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Ingress 3 | apiVersion: networking.k8s.io/v1 4 | metadata: 5 | name: http-echo 6 | spec: 7 | ingressClassName: haproxy 8 | rules: 9 | - host: {{ .Host }} 10 | http: 11 | paths: 12 | - path: / 13 | pathType: Prefix 14 | backend: 15 | service: 16 | name: {{ .ServiceName }} 17 | port: 18 | {{ .PortType }}: {{ .ServicePort }} 19 | -------------------------------------------------------------------------------- /deploy/tests/e2e/set-header/config/deploy.yaml: -------------------------------------------------------------------------------- 1 | kind: Deployment 2 | apiVersion: apps/v1 3 | metadata: 4 | name: http-echo 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: http-echo 10 | template: 11 | metadata: 12 | labels: 13 | app: http-echo 14 | spec: 15 | containers: 16 | - name: http-echo 17 | image: haproxytech/http-echo:latest 18 | imagePullPolicy: Never 19 | args: 20 | ports: 21 | - name: http 22 | containerPort: 8888 23 | protocol: TCP 24 | - name: https 25 | containerPort: 8443 26 | protocol: TCP 27 | --- 28 | kind: Service 29 | apiVersion: v1 30 | metadata: 31 | name: http-echo 32 | spec: 33 | ipFamilyPolicy: RequireDualStack 34 | ports: 35 | - name: http 36 | protocol: TCP 37 | port: 80 38 | targetPort: http 39 | - name: https 40 | protocol: TCP 41 | port: 443 42 | targetPort: https 43 | selector: 44 | app: http-echo 45 | -------------------------------------------------------------------------------- /deploy/tests/e2e/set-header/config/ingress.yaml.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Ingress 3 | apiVersion: networking.k8s.io/v1 4 | metadata: 5 | name: http-echo 6 | annotations: 7 | {{- range .IngAnnotations}} 8 | {{ .Key }}: "{{ .Value }}" 9 | {{- end}} 10 | spec: 11 | ingressClassName: haproxy 12 | rules: 13 | - host: {{ .Host }} 14 | http: 15 | paths: 16 | - path: / 17 | pathType: Prefix 18 | backend: 19 | service: 20 | name: http-echo 21 | port: 22 | name: http 23 | -------------------------------------------------------------------------------- /deploy/tests/e2e/tls-auth/config/client-auth.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: ConfigMap 3 | apiVersion: v1 4 | metadata: 5 | name: haproxy-kubernetes-ingress 6 | namespace: haproxy-controller 7 | data: 8 | ssl-certificate: e2e-tests-tls-auth/default-cert 9 | client-ca: e2e-tests-tls-auth/client-ca 10 | syslog-server: | 11 | address: stdout, format: raw, facility:daemon 12 | maxconn: "1000" 13 | server-slots: "4" 14 | timeout-client: 50s 15 | timeout-connect: 5s 16 | timeout-http-keep-alive: 1m 17 | timeout-http-request: 5s 18 | timeout-queue: 5s 19 | timeout-server: 50s 20 | timeout-tunnel: 1h 21 | -------------------------------------------------------------------------------- /deploy/tests/images/http-echo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24-alpine AS builder 2 | 3 | COPY *.go /src/ 4 | COPY go.mod /src/go.mod 5 | 6 | RUN cd /src && go build -o echo-http 7 | 8 | FROM alpine:3 9 | RUN apk --no-cache add openssl 10 | WORKDIR /app 11 | COPY --from=builder /src/echo-http . 12 | COPY generate-cert.sh . 13 | RUN chmod +x generate-cert.sh 14 | 15 | ENTRYPOINT ["./echo-http"] 16 | CMD [] 17 | -------------------------------------------------------------------------------- /deploy/tests/images/http-echo/README.md: -------------------------------------------------------------------------------- 1 | # http-echo 2 | 3 | A simple golang HTTP/S server that echoes back request attributes to the client in JSON formats. 4 | By default the certificate CN is the hostname of the machine where the program is running. 5 | 6 | ## How to use 7 | 8 | ```bash 9 | docker build -t haproxytech/http-echo -f deploy/tests/images/http-echo/Dockerfile deploy/tests/images/http-echo 10 | docker run -p 8888:80 -p 8443:443 --rm -t haproxytech/http-echo 11 | ``` 12 | 13 | ## Output example 14 | 15 | ```bash 16 | curl -b "test=bar" -k https://localhost:8443/path\?a\=foo1\&b\=foo2 17 | ```` 18 | ```json 19 | { 20 | "http": { 21 | "cookies": [ 22 | "test=bar" 23 | ], 24 | "headers": { 25 | "Accept": "*/*", 26 | "Cookie": "test=bar", 27 | "User-Agent": "curl/7.70.0" 28 | }, 29 | "host": "localhost:8443", 30 | "method": "GET", 31 | "path": "/path", 32 | "protocol": "HTTP/2.0", 33 | "query": "a=foo1\u0026b=foo2", 34 | "raw": "GET /path?a=foo1\u0026b=foo2 HTTP/1.1\r\nHost: localhost:8443\r\nUser-Agent: curl/7.70.0\r\nAccept: */*\r\nCookie: test=bar\r\n\r\n" 35 | }, 36 | "os": { 37 | "hostname": "traktour" 38 | }, 39 | "tcp": { 40 | "ip": "[::1]", 41 | "port": "53364" 42 | }, 43 | "tls": { 44 | "cipher": "TLS_AES_128_GCM_SHA256", 45 | "sni": "localhost" 46 | } 47 | } 48 | ``` 49 | 50 | 51 | ## Credits 52 | 53 | [mendhak/docker-http-https-echo](https://github.com/mendhak/docker-http-https-echo) 54 | -------------------------------------------------------------------------------- /deploy/tests/images/http-echo/generate-cert.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | if [ -z $SUBJECT ]; 5 | then 6 | SUBJECT="/C=FR/L=PARIS/O=Echo HTTP/CN=$(hostname)" 7 | fi 8 | openssl req -x509 -nodes -days 365 \ 9 | -newkey rsa:2048 \ 10 | -keyout server.key \ 11 | -out server.crt \ 12 | -subj "$SUBJECT" 13 | -------------------------------------------------------------------------------- /deploy/tests/images/http-echo/go.mod: -------------------------------------------------------------------------------- 1 | module echo-http 2 | 3 | go 1.24 4 | -------------------------------------------------------------------------------- /deploy/tests/images/proxy-protocol/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22-alpine AS builder 2 | 3 | COPY *.go /src/ 4 | COPY go.mod /src/go.mod 5 | COPY go.sum /src/go.sum 6 | 7 | RUN cd /src && go build -o proxy-protocol 8 | 9 | FROM alpine:3 10 | WORKDIR /app 11 | COPY --from=builder /src/proxy-protocol . 12 | 13 | ENTRYPOINT ["./proxy-protocol"] 14 | CMD [] 15 | -------------------------------------------------------------------------------- /deploy/tests/images/proxy-protocol/README.md: -------------------------------------------------------------------------------- 1 | # proxy-protocol 2 | 3 | A simple Go web server backed by PROXY Protocol. 4 | 5 | ## How to use 6 | 7 | ```bash 8 | docker build -t haproxytech/proxy-protocol -f deploy/tests/images/proxy-protocol/Dockerfile deploy/tests/images/proxy-protocol 9 | docker run -p 8080:8080 --rm -t haproxytech/proxy-protocol 10 | ``` 11 | 12 | ## Output example 13 | 14 | ``` 15 | $: curl --haproxy-protocol --http1.1 http://localhost:8080/ 16 | hello! 17 | ```` 18 | 19 | ## Credits 20 | 21 | [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) -------------------------------------------------------------------------------- /deploy/tests/images/proxy-protocol/go.mod: -------------------------------------------------------------------------------- 1 | module proxy-protocol 2 | 3 | go 1.22.0 4 | 5 | require github.com/pires/go-proxyproto v0.8.0 6 | -------------------------------------------------------------------------------- /deploy/tests/images/proxy-protocol/go.sum: -------------------------------------------------------------------------------- 1 | github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0= 2 | github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= 3 | -------------------------------------------------------------------------------- /deploy/tests/images/proxy-protocol/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/pires/go-proxyproto" 10 | ) 11 | 12 | func main() { 13 | // Create a TCP listener on port 8080 14 | addr := ":8080" 15 | tcpListener, err := net.Listen("tcp", addr) 16 | if err != nil { 17 | fmt.Fprintf(os.Stderr, "Error creating TCP listener: %v\n", err) 18 | os.Exit(1) 19 | } 20 | 21 | // Wrap it with the PROXY protocol listener 22 | proxyListener := &proxyproto.Listener{Listener: tcpListener} 23 | 24 | // Ensure the listener is closed on shutdown 25 | defer proxyListener.Close() 26 | 27 | // HTTP handler 28 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 29 | fmt.Fprint(w, "hello!") 30 | }) 31 | 32 | // Serve using custom listener 33 | fmt.Printf("Listening on %s with PROXY protocol support...\n", addr) 34 | if err := http.Serve(proxyListener, nil); err != nil { 35 | fmt.Fprintf(os.Stderr, "HTTP server error: %v\n", err) 36 | os.Exit(1) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /deploy/tests/kind-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kind.x-k8s.io/v1alpha4 2 | networking: 3 | ipFamily: dual 4 | apiServerAddress: "127.0.0.1" 5 | apiServerPort: 6443 6 | kind: Cluster 7 | nodes: 8 | - role: control-plane 9 | image: kindest/node:v1.32.2 10 | extraPortMappings: 11 | - hostPort: 30080 12 | containerPort: 30080 13 | #listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0" 14 | #protocol: udp # Optional, defaults to tcp 15 | - hostPort: 30443 16 | containerPort: 30443 17 | - hostPort: 31024 18 | containerPort: 31024 19 | - hostPort: 32766 20 | containerPort: 32766 21 | - hostPort: 32767 22 | containerPort: 32767 23 | - hostPort: 32765 # gwapi 24 | containerPort: 32765 25 | kubeadmConfigPatches: 26 | - | 27 | kind: ClusterConfiguration 28 | controllerManager: 29 | extraArgs: 30 | max-endpoints-per-slice: "5" 31 | node-cidr-mask-size-ipv4: "21" 32 | - | 33 | apiVersion: kubelet.config.k8s.io/v1beta1 34 | kind: KubeletConfiguration 35 | metadata: 36 | name: config 37 | maxPods: 2048 38 | -------------------------------------------------------------------------------- /deploy/tests/rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | command -v kind >/dev/null 2>&1 || { echo >&2 "Kind not installed. Aborting."; exit 1; } 5 | command -v kubectl >/dev/null 2>&1 || { echo >&2 "Kubectl not installed. Aborting."; exit 1; } 6 | DIR=$(dirname "$0") 7 | 8 | echo "delete image of ingress controller" 9 | kubectl delete -f $DIR/config/3.ingress-controller.yaml 10 | 11 | echo "building image for ingress controller" 12 | docker build -t haproxytech/kubernetes-ingress -f build/Dockerfile . 13 | kind --name=dev load docker-image haproxytech/kubernetes-ingress:latest 14 | 15 | echo "deploying Ingress Controller ..." 16 | kubectl apply -f $DIR/config/3.ingress-controller.yaml 17 | 18 | echo "wait --for=condition=ready ..." 19 | COUNTER=0 20 | while [ $COUNTER -lt 150 ]; do 21 | sleep 2 22 | kubectl get pods -n haproxy-controller --no-headers --selector=run=haproxy-ingress | awk '{print "haproxy-controller/haproxy-kubernetes-ingress " $3 " " $5}' 23 | result=$(kubectl get pods -n haproxy-controller --no-headers --selector=run=haproxy-ingress | awk '{print $3}') 24 | if [ "$result" = "Running" ]; then 25 | COUNTER=151 26 | else 27 | COUNTER=`expr $COUNTER + 1` 28 | fi 29 | done 30 | 31 | kubectl wait --for=condition=ready --timeout=10s pod -l run=haproxy-ingress -n haproxy-controller 32 | -------------------------------------------------------------------------------- /deploy/tests/tnr/routeacl/usebackend_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package routeacl 16 | 17 | import ( 18 | "os" 19 | "path/filepath" 20 | "strings" 21 | ) 22 | 23 | func (suite *UseBackendSuite) TestUseBackend() { 24 | // This test addresses https://github.com/haproxytech/kubernetes-ingress/issues/476 25 | suite.UseBackendFixture() 26 | suite.Run("Modifying service annotations should not duplicate use_backend clause", func() { 27 | contents, err := os.ReadFile(filepath.Join(suite.test.TempDir, "haproxy.cfg")) 28 | if err != nil { 29 | suite.T().Error(err.Error()) 30 | } 31 | c := strings.Count(string(contents), "use_backend ns_myappservice_https if { path -m beg / } { cookie(staging) -m found }") 32 | suite.Exactly(c, 2, "use_backend for route-acl is repeated %d times but expected 2", c) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /deploy/tests/ut/acls/acls_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package acls 16 | 17 | import ( 18 | "os" 19 | "path/filepath" 20 | "strings" 21 | ) 22 | 23 | func (suite *ACLSuite) TestACL() { 24 | suite.UseACLFixture() 25 | contents, err := os.ReadFile(filepath.Join(suite.test.TempDir, "haproxy.cfg")) 26 | if err != nil { 27 | suite.T().Error(err.Error()) 28 | } 29 | 30 | suite.Run("acl cookie_found", func() { 31 | c := strings.Count(string(contents), "acl cookie_found cook(JSESSIONID) -m found") 32 | suite.Exactly(c, 1, "acl cookie_found is repeated %d times but expected 1", c) 33 | c = strings.Count(string(contents), "acl is_ticket path_beg -i /ticket") 34 | suite.Exactly(c, 1, "acl is_ticket is repeated %d times but expected 1", c) 35 | }) 36 | 37 | suite.Run("acl is_ticket", func() { 38 | c := strings.Count(string(contents), "acl is_ticket path_beg -i /ticket") 39 | suite.Exactly(c, 1, "acl is_ticket is repeated %d times but expected 1", c) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /deploy/tests/ut/httprequests/httprequests_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package httprequests 16 | 17 | import ( 18 | "os" 19 | "path/filepath" 20 | "strings" 21 | ) 22 | 23 | func (suite *HTTPRequestsSuite) TestHTTPRequests() { 24 | suite.UseHTTPRequestsFixture() 25 | contents, err := os.ReadFile(filepath.Join(suite.test.TempDir, "haproxy.cfg")) 26 | if err != nil { 27 | suite.T().Fatal(err.Error()) 28 | } 29 | 30 | suite.Run("http-request set-var(txn.admintenant)", func() { 31 | c := strings.Count(string(contents), "http-request set-var(txn.admintenant) str({{RUN.serviceId2}})") 32 | suite.Exactly(c, 1, "http-request set-var(txn.admintenant) is repeated %d times but expected 1", c) 33 | }) 34 | 35 | suite.Run("http-request track-sc1 txn.key", func() { 36 | c := strings.Count(string(contents), " table connected.local if cookie_found") 37 | suite.Exactly(c, 1, "http-request track-sc1 txn.key is repeated %d times but expected 1", c) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /documentation/README.md: -------------------------------------------------------------------------------- 1 | 2 | # ![HAProxy](../assets/images/haproxy-weblogo-210x49.png "HAProxy") 3 | 4 | ## HAProxy kubernetes ingress controller 3.0 5 | 6 | ### Documentation 7 | 8 | - [Controller options](controller.md) 9 | - [Custom resource definitions](custom-resources.md) 10 | - [Annotations](annotations.md) 11 | - [Prometheus](prometheus.md) 12 | 13 | ### Lifecycle 14 | 15 | - [Lifecycle](lifecycle.md) 16 | 17 | #### Experimental 18 | - [Gateway API](gateway-api.md) 19 | 20 | #### Additional 21 | - [Supervisor](pebble.md) 22 | 23 | This is autogenerated from [doc.yaml](doc.yaml). Description can be found in [generator readme](gen/README.md) 24 | 25 | -------------------------------------------------------------------------------- /documentation/gen/go.mod: -------------------------------------------------------------------------------- 1 | module doc-gen 2 | 3 | go 1.24 4 | 5 | require ( 6 | github.com/google/renameio v1.0.1 7 | gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 8 | ) 9 | -------------------------------------------------------------------------------- /documentation/gen/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= 2 | github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= 3 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 4 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 5 | gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 h1:dbuHpmKjkDzSOMKAWl10QNlgaZUd3V1q99xc81tt2Kc= 6 | gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 7 | -------------------------------------------------------------------------------- /documentation/gen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var cn Conf 5 | cn.getConf() 6 | 7 | cn.generateReadme() 8 | cn.generateReadmeAnnotations() 9 | cn.generateReadmeController() 10 | cn.generateSupport() 11 | // cn.saveConf() 12 | // cn.saveDocConf() 13 | } 14 | -------------------------------------------------------------------------------- /documentation/gen/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type Version struct { 10 | Major int 11 | Minor int 12 | } 13 | 14 | // Implements the Unmarshaler interface of the yaml pkg. 15 | func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error { 16 | var data string 17 | err := unmarshal(&data) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | parts := strings.Split(data, ".") 23 | if len(parts) != 2 { 24 | return fmt.Errorf("version is not in correct format") 25 | } 26 | v.Major, err = strconv.Atoi(parts[0]) 27 | if err != nil { 28 | return err 29 | } 30 | v.Minor, err = strconv.Atoi(parts[1]) 31 | if err != nil { 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | func (v *Version) String() string { 38 | return fmt.Sprintf("%d.%d", v.Major, v.Minor) 39 | } 40 | 41 | func (v *Version) LowerOrEqual(active Version) bool { 42 | if active.Major < v.Major { 43 | return false 44 | } 45 | if active.Major != v.Major { 46 | return true 47 | } 48 | if active.Minor < v.Minor { 49 | return false 50 | } 51 | return true 52 | } 53 | 54 | func (v Version) MarshalYAML() (interface{}, error) { 55 | return v.String(), nil 56 | } 57 | -------------------------------------------------------------------------------- /documentation/lifecycle.md: -------------------------------------------------------------------------------- 1 | 2 | # ![HAProxy](../assets/images/haproxy-weblogo-210x49.png "HAProxy") 3 | 4 | ## HAProxy kubernetes ingress controller 5 | 6 | ### Lifecycle 7 | 8 | | Version | GA | EOL | HAProxy | *k8s versions | 9 | | -:|:-:|:-:|:-:|:-:| 10 | | **3.1** | 2025-01 | 2026-02 | 3.1 | 1.32, 1.31, 1.30 | 11 | | **3.0** | 2024-06 | 2025-06 | 3.0 | 1.30, 1.29, 1.28 | 12 | | **1.11** | 2024-02 | 2025-02 | 2.8 | 1.28, 1.27, 1.26 | 13 | | **1.10** | 2023-04 | 2024-06 | 2.7 | 1.27, 1.26, 1.25 | 14 | | **1.9** | 2022-10 | 2023-11 | 2.6 | 1.25, 1.24, 1.23 | 15 | | **1.8** | 2022-06 | 2023-05 | 2.5 | 1.23, 1.22, 1.21 | 16 | 17 | **Note:** for specific version of controller, only k8s versions that are thoroughly tested with that version are listed 18 | 19 | This is autogenerated from [lifecycle.yaml](lifecycle.yaml). Description can be found in [generator readme](gen/README.md) 20 | -------------------------------------------------------------------------------- /documentation/lifecycle.yaml: -------------------------------------------------------------------------------- 1 | versions: 2 | - version: "3.1" 3 | ga: 2025-01 4 | min_eol: 2026-02 5 | k8s: 6 | - "1.32" 7 | - "1.31" 8 | - "1.30" 9 | - version: "3.0" 10 | ga: 2024-06 11 | min_eol: 2025-06 12 | k8s: 13 | - "1.30" 14 | - "1.29" 15 | - "1.28" 16 | - version: "1.11" 17 | ga: 2024-02 18 | min_eol: 2025-02 19 | k8s: 20 | - "1.28" 21 | - "1.27" 22 | - "1.26" 23 | haproxy: "2.8" 24 | - version: "1.10" 25 | ga: 2023-04 26 | min_eol: 2024-06 27 | k8s: 28 | - "1.27" 29 | - "1.26" 30 | - "1.25" 31 | haproxy: "2.7" 32 | - version: "1.9" 33 | ga: 2022-10 34 | min_eol: 2023-11 35 | k8s: 36 | - "1.25" 37 | - "1.24" 38 | - "1.23" 39 | haproxy: "2.6" 40 | - version: "1.8" 41 | ga: 2022-06 42 | min_eol: 2023-05 43 | k8s: 44 | - "1.23" 45 | - "1.22" 46 | - "1.21" 47 | haproxy: "2.5" 48 | -------------------------------------------------------------------------------- /documentation/tcp-cr-full-example/echo-0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | annotations: 6 | deployment.kubernetes.io/revision: "1" 7 | name: http-echo-0 8 | spec: 9 | progressDeadlineSeconds: 600 10 | replicas: 1 11 | revisionHistoryLimit: 10 12 | selector: 13 | matchLabels: 14 | app: http-echo-0 15 | strategy: 16 | rollingUpdate: 17 | maxSurge: 25% 18 | maxUnavailable: 25% 19 | type: RollingUpdate 20 | template: 21 | metadata: 22 | labels: 23 | app: http-echo-0 24 | spec: 25 | containers: 26 | - args: 27 | - --default-response=hostname 28 | image: haproxytech/http-echo:latest 29 | imagePullPolicy: Never 30 | name: http-echo 31 | ports: 32 | - containerPort: 8888 33 | name: http 34 | protocol: TCP 35 | - containerPort: 8443 36 | name: https 37 | protocol: TCP 38 | terminationMessagePath: /dev/termination-log 39 | terminationMessagePolicy: File 40 | dnsPolicy: ClusterFirst 41 | restartPolicy: Always 42 | schedulerName: default-scheduler 43 | terminationGracePeriodSeconds: 30 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: http-echo-0 49 | spec: 50 | internalTrafficPolicy: Cluster 51 | ipFamilies: 52 | - IPv4 53 | - IPv6 54 | ipFamilyPolicy: RequireDualStack 55 | ports: 56 | - name: http 57 | port: 80 58 | targetPort: http 59 | - name: https 60 | port: 443 61 | targetPort: https 62 | selector: 63 | app: http-echo-0 64 | -------------------------------------------------------------------------------- /documentation/tcp-cr-full-example/echo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | annotations: 6 | deployment.kubernetes.io/revision: "1" 7 | name: http-echo 8 | spec: 9 | progressDeadlineSeconds: 600 10 | replicas: 1 11 | revisionHistoryLimit: 10 12 | selector: 13 | matchLabels: 14 | app: http-echo 15 | strategy: 16 | rollingUpdate: 17 | maxSurge: 25% 18 | maxUnavailable: 25% 19 | type: RollingUpdate 20 | template: 21 | metadata: 22 | labels: 23 | app: http-echo 24 | spec: 25 | containers: 26 | - args: 27 | - --default-response=hostname 28 | image: haproxytech/http-echo:latest 29 | imagePullPolicy: Never 30 | name: http-echo 31 | ports: 32 | - containerPort: 8888 33 | name: http 34 | protocol: TCP 35 | - containerPort: 8443 36 | name: https 37 | protocol: TCP 38 | terminationMessagePath: /dev/termination-log 39 | terminationMessagePolicy: File 40 | dnsPolicy: ClusterFirst 41 | restartPolicy: Always 42 | schedulerName: default-scheduler 43 | terminationGracePeriodSeconds: 30 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: http-echo 49 | spec: 50 | internalTrafficPolicy: Cluster 51 | ipFamilies: 52 | - IPv4 53 | - IPv6 54 | ipFamilyPolicy: RequireDualStack 55 | ports: 56 | - name: http 57 | port: 80 58 | targetPort: http 59 | - name: https 60 | port: 443 61 | targetPort: https 62 | selector: 63 | app: http-echo 64 | -------------------------------------------------------------------------------- /examples/test-informers/replay-delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | curl -X DELETE -H "Content-Type: application/yaml" --data-binary @configmap.yaml http://localhost:8081 3 | curl -X DELETE -H "Content-Type: application/yaml" --data-binary @echo-app.yaml http://localhost:8081 4 | curl -X DELETE -H "Content-Type: application/yaml" --data-binary @echo.endpointslices.yaml http://localhost:8081 5 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/aux-cfg/type: -------------------------------------------------------------------------------- 1 | oneshot 2 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/aux-cfg/up: -------------------------------------------------------------------------------- 1 | /etc/s6-overlay/scripts/01-aux-cfg 2 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/aux-cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/aux-cfg -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/base: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/base -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/sigusr1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/haproxy/dependencies.d/sigusr1 -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/haproxy/run: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | 3 | MEMLIMIT=$(free -m | awk '/Mem:/ {printf "%d\n", int($2 * 2 / 3)}') 4 | 5 | CG_LIMIT_FILE="/sys/fs/cgroup/memory/memory.limit_in_bytes" 6 | if [ -f "/sys/fs/cgroup/cgroup.controllers" ]; then 7 | CG_LIMIT_FILE="/sys/fs/cgroup/memory.max" 8 | fi 9 | 10 | if [ -r "${CG_LIMIT_FILE}" ]; then 11 | if grep -q '^max$' "${CG_LIMIT_FILE}"; then 12 | MEMLIMIT_CG="${MEMLIMIT}" 13 | else 14 | MEMLIMIT_CG=$(awk '{printf "%d\n", int($1 / 1024 / 1024 * 2 / 3)}' "${CG_LIMIT_FILE}") 15 | fi 16 | 17 | if [ "${MEMLIMIT_CG}" -gt 0 ]; then 18 | if [ "${MEMLIMIT_CG}" -lt "${MEMLIMIT}" ]; then 19 | MEMLIMIT="${MEMLIMIT_CG}" 20 | fi 21 | fi 22 | fi 23 | 24 | echo "Memory limit for HAProxy: ${MEMLIMIT}MiB" 25 | 26 | # if master socket is changed, that needs to be aligned in pkg/haproxy/process/interface.go 27 | exec /usr/local/sbin/haproxy -W -db -m "${MEMLIMIT}" -S /var/run/haproxy-master.sock,level,admin -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/haproxy-aux.cfg 28 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/haproxy/type: -------------------------------------------------------------------------------- 1 | longrun 2 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/aux-cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/aux-cfg -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/base: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/base -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/haproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/haproxy -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/sigusr1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/ingress-controller/dependencies.d/sigusr1 -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/finish: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | 3 | if [ "${1}" -ne 0 ] && [ "${1}" -ne 256 ]; then 4 | echo "Ingress Controller exited with fatal code ${1}, taking down the S6 supervision tree" 5 | 6 | exec /run/s6/basedir/bin/halt 7 | fi 8 | 9 | echo "Ingress Controller exited with code ${1}, restarting..." 10 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/run: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | 3 | MEMLIMIT=$(free -m | awk '/Mem:/ {printf "%d\n", int($2 / 3)}') 4 | 5 | CG_LIMIT_FILE="/sys/fs/cgroup/memory/memory.limit_in_bytes" 6 | if [ -f "/sys/fs/cgroup/cgroup.controllers" ]; then 7 | CG_LIMIT_FILE="/sys/fs/cgroup/memory.max" 8 | fi 9 | 10 | if [ -r "${CG_LIMIT_FILE}" ]; then 11 | if grep -q '^max$' "${CG_LIMIT_FILE}"; then 12 | MEMLIMIT_CG="${MEMLIMIT}" 13 | else 14 | MEMLIMIT_CG=$(awk '{printf "%d\n", int($1 / 1024 / 1024 / 3)}' "${CG_LIMIT_FILE}") 15 | fi 16 | 17 | if [ "${MEMLIMIT_CG}" -gt 0 ]; then 18 | if [ "${MEMLIMIT_CG}" -lt "${MEMLIMIT}" ]; then 19 | MEMLIMIT="${MEMLIMIT_CG}" 20 | fi 21 | fi 22 | fi 23 | 24 | export GOMEMLIMIT="${MEMLIMIT}MiB" 25 | 26 | echo "Memory limit for Ingress Controller: ${GOMEMLIMIT}" 27 | 28 | exec /haproxy-ingress-controller --with-s6-overlay ${EXTRA_OPTIONS} 29 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/ingress-controller/type: -------------------------------------------------------------------------------- 1 | longrun 2 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/sigusr1/type: -------------------------------------------------------------------------------- 1 | oneshot 2 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/sigusr1/up: -------------------------------------------------------------------------------- 1 | /etc/s6-overlay/scripts/00-sigusr1 2 | -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/user/contents.d/haproxy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/user/contents.d/haproxy -------------------------------------------------------------------------------- /fs/etc/s6-overlay/s6-rc.d/user/contents.d/ingress-controller: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/b7036e43cc162b92f3ff830ed5592c3a984eeb3e/fs/etc/s6-overlay/s6-rc.d/user/contents.d/ingress-controller -------------------------------------------------------------------------------- /fs/etc/s6-overlay/scripts/00-sigusr1: -------------------------------------------------------------------------------- 1 | #!/command/with-contenv sh 2 | 3 | SVSCAN_PATH="/run/s6/basedir/run-image/service/.s6-svscan" 4 | 5 | if [ ! -f "${SVSCAN_PATH}/SIGUSR1" ]; then 6 | cat > "${SVSCAN_PATH}/SIGUSR1" < maxProcs { 39 | v = maxProcs 40 | } 41 | a.global.Nbthread = int64(v) 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /pkg/annotations/ingress/reqPathRewrite.go: -------------------------------------------------------------------------------- 1 | package ingress 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/rules" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 10 | ) 11 | 12 | type ReqPathRewrite struct { 13 | rules *rules.List 14 | name string 15 | } 16 | 17 | func NewReqPathRewrite(n string, r *rules.List) *ReqPathRewrite { 18 | return &ReqPathRewrite{name: n, rules: r} 19 | } 20 | 21 | func (a *ReqPathRewrite) GetName() string { 22 | return a.name 23 | } 24 | 25 | func (a *ReqPathRewrite) Process(k store.K8s, annotations ...map[string]string) (err error) { 26 | input := strings.TrimSpace(common.GetValue(a.GetName(), annotations...)) 27 | if input == "" { 28 | return 29 | } 30 | for _, rule := range strings.Split(input, "\n") { 31 | parts := strings.Fields(strings.TrimSpace(rule)) 32 | 33 | var rewrite *rules.ReqPathRewrite 34 | switch len(parts) { 35 | case 1: 36 | rewrite = &rules.ReqPathRewrite{ 37 | PathMatch: "(.*)", 38 | PathFmt: parts[0], 39 | } 40 | case 2: 41 | rewrite = &rules.ReqPathRewrite{ 42 | PathMatch: parts[0], 43 | PathFmt: parts[1], 44 | } 45 | default: 46 | return fmt.Errorf("incorrect value '%s', path-rewrite takes 1 or 2 params ", input) 47 | } 48 | a.rules.Add(rewrite) 49 | } 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /pkg/annotations/ingress/reqSetHdr.go: -------------------------------------------------------------------------------- 1 | package ingress 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/rules" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 10 | ) 11 | 12 | type SetHdr struct { 13 | rules *rules.List 14 | name string 15 | response bool 16 | } 17 | 18 | func NewReqSetHdr(n string, r *rules.List) *SetHdr { 19 | return &SetHdr{name: n, rules: r} 20 | } 21 | 22 | func NewResSetHdr(n string, r *rules.List) *SetHdr { 23 | return &SetHdr{name: n, rules: r, response: true} 24 | } 25 | 26 | func (a *SetHdr) GetName() string { 27 | return a.name 28 | } 29 | 30 | func (a *SetHdr) Process(k store.K8s, annotations ...map[string]string) (err error) { 31 | input := common.GetValue(a.GetName(), annotations...) 32 | if input == "" { 33 | return 34 | } 35 | for _, param := range strings.Split(input, "\n") { 36 | if param == "" { 37 | continue 38 | } 39 | indexSpace := strings.IndexByte(param, ' ') 40 | if indexSpace == -1 { 41 | return fmt.Errorf("incorrect value '%s' in request-set-header annotation", param) 42 | } 43 | a.rules.Add(&rules.SetHdr{ 44 | HdrName: param[:indexSpace], 45 | HdrFormat: "\"" + param[indexSpace+1:] + "\"", 46 | Response: a.response, 47 | }) 48 | } 49 | return 50 | } 51 | -------------------------------------------------------------------------------- /pkg/annotations/ingress/reqSetHost.go: -------------------------------------------------------------------------------- 1 | package ingress 2 | 3 | import ( 4 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 5 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/rules" 6 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 7 | ) 8 | 9 | type ReqSetHost struct { 10 | rules *rules.List 11 | name string 12 | } 13 | 14 | func NewReqSetHost(n string, r *rules.List) *ReqSetHost { 15 | return &ReqSetHost{name: n, rules: r} 16 | } 17 | 18 | func (a *ReqSetHost) GetName() string { 19 | return a.name 20 | } 21 | 22 | func (a *ReqSetHost) Process(k store.K8s, annotations ...map[string]string) (err error) { 23 | input := common.GetValue(a.GetName(), annotations...) 24 | if input == "" { 25 | return 26 | } 27 | a.rules.Add(&rules.SetHdr{ 28 | HdrName: "Host", 29 | HdrFormat: input, 30 | }) 31 | return 32 | } 33 | -------------------------------------------------------------------------------- /pkg/annotations/ingress/srcIPHdr.go: -------------------------------------------------------------------------------- 1 | package ingress 2 | 3 | import ( 4 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 5 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/rules" 6 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 7 | ) 8 | 9 | type SrcIPHdr struct { 10 | rules *rules.List 11 | name string 12 | } 13 | 14 | func NewSrcIPHdr(n string, r *rules.List) *SrcIPHdr { 15 | return &SrcIPHdr{name: n, rules: r} 16 | } 17 | 18 | func (a *SrcIPHdr) GetName() string { 19 | return a.name 20 | } 21 | 22 | func (a *SrcIPHdr) Process(k store.K8s, annotations ...map[string]string) (err error) { 23 | input := common.GetValue(a.GetName(), annotations...) 24 | if input == "" { 25 | return 26 | } 27 | a.rules.Add(&rules.ReqSetSrc{ 28 | HeaderName: input, 29 | }) 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /pkg/annotations/service/abortOnClose.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 9 | ) 10 | 11 | type AbortOnClose struct { 12 | backend *models.Backend 13 | name string 14 | } 15 | 16 | func NewAbortOnClose(n string, b *models.Backend) *AbortOnClose { 17 | return &AbortOnClose{name: n, backend: b} 18 | } 19 | 20 | func (a *AbortOnClose) GetName() string { 21 | return a.name 22 | } 23 | 24 | func (a *AbortOnClose) Process(k store.K8s, annotations ...map[string]string) error { 25 | input := common.GetValue(a.GetName(), annotations...) 26 | var enabled bool 27 | var err error 28 | if input != "" { 29 | enabled, err = utils.GetBoolValue(input, "abortonclose") 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | if enabled { 35 | a.backend.Abortonclose = "enabled" 36 | } else { 37 | a.backend.Abortonclose = "disabled" 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /pkg/annotations/service/backendForwardedFor.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 10 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 11 | ) 12 | 13 | type ForwardedFor struct { 14 | backend *models.Backend 15 | name string 16 | } 17 | 18 | func NewForwardedFor(n string, b *models.Backend) *ForwardedFor { 19 | return &ForwardedFor{name: n, backend: b} 20 | } 21 | 22 | func (a *ForwardedFor) GetName() string { 23 | return a.name 24 | } 25 | 26 | func (a *ForwardedFor) Process(k store.K8s, annotations ...map[string]string) error { 27 | input := common.GetValue(a.GetName(), annotations...) 28 | if input == "" { 29 | a.backend.Forwardfor = nil 30 | return nil 31 | } 32 | var params *models.Forwardfor 33 | enabled, err := utils.GetBoolValue(input, "forwarded-for") 34 | if err != nil { 35 | return err 36 | } 37 | if enabled { 38 | params = &models.Forwardfor{ 39 | Enabled: utils.PtrString("enabled"), 40 | } 41 | if err = params.Validate(nil); err != nil { 42 | return fmt.Errorf("forwarded-for: %w", err) 43 | } 44 | } 45 | a.backend.Forwardfor = params 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/annotations/service/check.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 9 | ) 10 | 11 | type Check struct { 12 | backend *models.Backend 13 | name string 14 | } 15 | 16 | func NewCheck(n string, b *models.Backend) *Check { 17 | return &Check{name: n, backend: b} 18 | } 19 | 20 | func (a *Check) GetName() string { 21 | return a.name 22 | } 23 | 24 | // the value models.DefaultSever.Check should be a bool value and not an enum [enabled, disabled] 25 | // this avoids an uncessary update when models.DefaultSever.Check is set form empty to "disabled" 26 | func (a *Check) Process(k store.K8s, annotations ...map[string]string) error { 27 | input := common.GetValue(a.GetName(), annotations...) 28 | if input == "" { 29 | if a.backend.DefaultServer != nil { 30 | a.backend.DefaultServer.Check = "" 31 | } 32 | return nil 33 | } 34 | enabled, err := utils.GetBoolValue(input, "check") 35 | if err != nil { 36 | return err 37 | } 38 | if !enabled { 39 | if a.backend.DefaultServer != nil { 40 | a.backend.DefaultServer.Check = "" 41 | } 42 | return nil 43 | } 44 | if a.backend.DefaultServer == nil { 45 | a.backend.DefaultServer = &models.DefaultServer{ServerParams: models.ServerParams{Check: "enabled"}} 46 | } else { 47 | a.backend.DefaultServer.Check = "enabled" 48 | } 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/annotations/service/checkHTTP.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | 7 | "github.com/haproxytech/client-native/v6/models" 8 | 9 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 10 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 11 | ) 12 | 13 | type CheckHTTP struct { 14 | backend *models.Backend 15 | name string 16 | } 17 | 18 | func NewCheckHTTP(n string, b *models.Backend) *CheckHTTP { 19 | return &CheckHTTP{name: n, backend: b} 20 | } 21 | 22 | func (a *CheckHTTP) GetName() string { 23 | return a.name 24 | } 25 | 26 | func (a *CheckHTTP) Process(k store.K8s, annotations ...map[string]string) error { 27 | input := common.GetValue(a.GetName(), annotations...) 28 | if input == "" { 29 | a.backend.AdvCheck = "" 30 | a.backend.HttpchkParams = nil 31 | return nil 32 | } 33 | var params *models.HttpchkParams 34 | checkHTTPParams := strings.Fields(strings.TrimSpace(input)) 35 | switch len(checkHTTPParams) { 36 | case 0: 37 | return errors.New("httpchk option: incorrect number of params") 38 | case 1: 39 | params = &models.HttpchkParams{ 40 | URI: checkHTTPParams[0], 41 | } 42 | case 2: 43 | params = &models.HttpchkParams{ 44 | Method: checkHTTPParams[0], 45 | URI: checkHTTPParams[1], 46 | } 47 | default: 48 | params = &models.HttpchkParams{ 49 | Method: checkHTTPParams[0], 50 | URI: checkHTTPParams[1], 51 | Version: strings.Join(checkHTTPParams[2:], " "), 52 | } 53 | } 54 | 55 | a.backend.AdvCheck = "httpchk" 56 | a.backend.HttpchkParams = params 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/annotations/service/checkInter.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 9 | ) 10 | 11 | type CheckInter struct { 12 | backend *models.Backend 13 | name string 14 | } 15 | 16 | func NewCheckInter(n string, b *models.Backend) *CheckInter { 17 | return &CheckInter{name: n, backend: b} 18 | } 19 | 20 | func (a *CheckInter) GetName() string { 21 | return a.name 22 | } 23 | 24 | func (a *CheckInter) Process(k store.K8s, annotations ...map[string]string) error { 25 | input := common.GetValue(a.GetName(), annotations...) 26 | if input == "" { 27 | if a.backend.DefaultServer != nil { 28 | a.backend.DefaultServer.Inter = nil 29 | } 30 | return nil 31 | } 32 | value, err := utils.ParseTime(input) 33 | if err != nil { 34 | return err 35 | } 36 | if a.backend.DefaultServer == nil { 37 | a.backend.DefaultServer = &models.DefaultServer{} 38 | } 39 | a.backend.DefaultServer.Inter = value 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/annotations/service/maxconn.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 10 | ) 11 | 12 | type Maxconn struct { 13 | backend *models.Backend 14 | name string 15 | } 16 | 17 | func NewMaxconn(n string, b *models.Backend) *Maxconn { 18 | return &Maxconn{name: n, backend: b} 19 | } 20 | 21 | func (a *Maxconn) GetName() string { 22 | return a.name 23 | } 24 | 25 | func (a *Maxconn) Process(k store.K8s, annotations ...map[string]string) error { 26 | input := common.GetValue(a.GetName(), annotations...) 27 | if input == "" { 28 | if a.backend.DefaultServer != nil { 29 | a.backend.DefaultServer.Maxconn = nil 30 | } 31 | return nil 32 | } 33 | v, err := strconv.ParseInt(input, 10, 64) 34 | if err != nil { 35 | return err 36 | } 37 | // adjust backend maxconn when using multiple HAProxy Instances 38 | if len(k.HaProxyPods) != 0 { 39 | v /= int64(len(k.HaProxyPods)) 40 | } 41 | if a.backend.DefaultServer == nil { 42 | a.backend.DefaultServer = &models.DefaultServer{} 43 | } 44 | a.backend.DefaultServer.Maxconn = &v 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/annotations/service/proto.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 10 | ) 11 | 12 | type Proto struct { 13 | backend *models.Backend 14 | name string 15 | } 16 | 17 | func NewProto(n string, b *models.Backend) *Proto { 18 | return &Proto{name: n, backend: b} 19 | } 20 | 21 | func (a *Proto) GetName() string { 22 | return a.name 23 | } 24 | 25 | func (a *Proto) Process(k store.K8s, annotations ...map[string]string) error { 26 | input := common.GetValue(a.GetName(), annotations...) 27 | if input == "h2" { 28 | if a.backend.DefaultServer == nil { 29 | a.backend.DefaultServer = &models.DefaultServer{} 30 | } 31 | a.backend.DefaultServer.Proto = "h2" 32 | return nil 33 | } else if a.backend.DefaultServer == nil { 34 | return nil 35 | } 36 | switch input { 37 | case "": 38 | a.backend.DefaultServer.Proto = "" 39 | case "h1": 40 | // Forces H1 even when SSL is enabled 41 | a.backend.DefaultServer.Alpn = "" 42 | a.backend.DefaultServer.Proto = "" 43 | default: 44 | return fmt.Errorf("unknown proto %s", input) 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/annotations/service/ssl.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 9 | ) 10 | 11 | type SSL struct { 12 | backend *models.Backend 13 | name string 14 | } 15 | 16 | func NewSSL(n string, b *models.Backend) *SSL { 17 | return &SSL{name: n, backend: b} 18 | } 19 | 20 | func (a *SSL) GetName() string { 21 | return a.name 22 | } 23 | 24 | func (a *SSL) Process(k store.K8s, annotations ...map[string]string) error { 25 | input := common.GetValue(a.GetName(), annotations...) 26 | var enabled bool 27 | var err error 28 | if input != "" { 29 | enabled, err = utils.GetBoolValue(input, "server-ssl") 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | if enabled { 35 | if a.backend.DefaultServer == nil { 36 | a.backend.DefaultServer = &models.DefaultServer{} 37 | } 38 | a.backend.DefaultServer.Ssl = "enabled" 39 | a.backend.DefaultServer.Alpn = "h2,http/1.1" 40 | a.backend.DefaultServer.Verify = "none" 41 | } else if a.backend.DefaultServer != nil { 42 | a.backend.DefaultServer.Ssl = "" 43 | a.backend.DefaultServer.Alpn = "" 44 | a.backend.DefaultServer.Verify = "" 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/annotations/service/timeoutCheck.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 9 | ) 10 | 11 | type TimeoutCheck struct { 12 | backend *models.Backend 13 | name string 14 | } 15 | 16 | func NewTimeoutCheck(n string, b *models.Backend) *TimeoutCheck { 17 | return &TimeoutCheck{name: n, backend: b} 18 | } 19 | 20 | func (a *TimeoutCheck) GetName() string { 21 | return a.name 22 | } 23 | 24 | func (a *TimeoutCheck) Process(k store.K8s, annotations ...map[string]string) error { 25 | input := common.GetValue(a.GetName(), annotations...) 26 | if input == "" { 27 | a.backend.CheckTimeout = nil 28 | return nil 29 | } 30 | timeout, err := utils.ParseTime(input) 31 | if err != nil { 32 | return err 33 | } 34 | a.backend.CheckTimeout = timeout 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /pkg/annotations/service/timeoutServer.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations/common" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 8 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 9 | ) 10 | 11 | type TimeoutServer struct { 12 | backend *models.Backend 13 | name string 14 | } 15 | 16 | func NewTimeoutServer(n string, b *models.Backend) *TimeoutServer { 17 | return &TimeoutServer{name: n, backend: b} 18 | } 19 | 20 | func (a *TimeoutServer) GetName() string { 21 | return a.name 22 | } 23 | 24 | func (a *TimeoutServer) Process(k store.K8s, annotations ...map[string]string) error { 25 | input := common.GetValue(a.GetName(), annotations...) 26 | if input == "" { 27 | a.backend.ServerTimeout = nil 28 | return nil 29 | } 30 | timeout, err := utils.ParseTime(input) 31 | if err != nil { 32 | return err 33 | } 34 | a.backend.ServerTimeout = timeout 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /pkg/controller/constants/const.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package constants 16 | 17 | //nolint:golint, stylecheck 18 | const ( 19 | DefaultsSectionName = "haproxytech" 20 | SSL_FRONTEND = "ssl" 21 | SSL_BACKEND = "ssl-backend" 22 | HTTP_FRONTEND = "http" 23 | HTTPS_FRONTEND = "https" 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/fs/fs-delayed.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fs 16 | 17 | import ( 18 | "sync" 19 | ) 20 | 21 | var ( 22 | delayedFunc map[string]func() 23 | muDelayed sync.Mutex 24 | ) 25 | var delayedWriter = New() 26 | 27 | // AddDelayedFunc adds a function to be called prior to restarting of HAProxy 28 | func AddDelayedFunc(name string, f func()) { 29 | muDelayed.Lock() 30 | defer muDelayed.Unlock() 31 | if delayedFunc == nil { 32 | delayedFunc = make(map[string]func()) 33 | } 34 | delayedFunc[name] = f 35 | } 36 | 37 | func RunDelayedFuncs() { 38 | muDelayed.Lock() 39 | defer muDelayed.Unlock() 40 | if delayedFunc == nil { 41 | return 42 | } 43 | for _, f := range delayedFunc { 44 | delayedWriter.Write(f) 45 | } 46 | clear(delayedFunc) 47 | delayedWriter.WaitUntilWritesDone() 48 | } 49 | -------------------------------------------------------------------------------- /pkg/handler/handler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package handler 16 | 17 | import ( 18 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 19 | ) 20 | 21 | var logger = utils.GetLogger() 22 | -------------------------------------------------------------------------------- /pkg/handler/refresh.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package handler 16 | 17 | import ( 18 | "github.com/haproxytech/kubernetes-ingress/pkg/annotations" 19 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy" 20 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 21 | ) 22 | 23 | type Refresh struct{} 24 | 25 | func (handler Refresh) Update(k store.K8s, h haproxy.HAProxy, a annotations.Annotations) (err error) { 26 | cleanCrts := true 27 | cleanCrtsAnn, _ := annotations.ParseBool("clean-certs", k.ConfigMaps.Main.Annotations) 28 | // cleanCrtsAnn is empty if clean-certs not set or set with a non boolean value => error 29 | if cleanCrtsAnn == "false" { 30 | cleanCrts = false 31 | } 32 | // Certs 33 | if cleanCrts { 34 | h.RefreshCerts(h.HAProxyClient) 35 | } 36 | // Rules 37 | h.RefreshRules(h.HAProxyClient) 38 | // Maps 39 | h.RefreshMaps(h.HAProxyClient) 40 | 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /pkg/haproxy/api/capture.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/haproxytech/client-native/v6/models" 4 | 5 | func (c *clientNative) CaptureCreate(id int64, frontend string, rule models.Capture) error { 6 | configuration, err := c.nativeAPI.Configuration() 7 | if err != nil { 8 | return err 9 | } 10 | return configuration.CreateDeclareCapture(id, frontend, &rule, c.activeTransaction, 0) 11 | } 12 | 13 | func (c *clientNative) CaptureDeleteAll(frontend string) (err error) { 14 | configuration, err := c.nativeAPI.Configuration() 15 | if err != nil { 16 | return 17 | } 18 | _, rules, err := configuration.GetDeclareCaptures(frontend, c.activeTransaction) 19 | if err != nil { 20 | return 21 | } 22 | for range rules { 23 | if err = configuration.DeleteDeclareCapture(0, frontend, c.activeTransaction, 0); err != nil { 24 | break 25 | } 26 | } 27 | return 28 | } 29 | 30 | func (c *clientNative) CapturesGet(frontend string) (models.Captures, error) { 31 | configuration, err := c.nativeAPI.Configuration() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | _, rules, err := configuration.GetDeclareCaptures(frontend, c.activeTransaction) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return rules, nil 41 | } 42 | 43 | func (c *clientNative) CapturesReplace(frontend string, rules models.Captures) error { 44 | configuration, err := c.nativeAPI.Configuration() 45 | if err != nil { 46 | return err 47 | } 48 | 49 | err = configuration.ReplaceDeclareCaptures(frontend, rules, c.activeTransaction, 0) 50 | if err != nil { 51 | return err 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqAcceptContent.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | ) 10 | 11 | type ReqAcceptContent struct{} 12 | 13 | func (r ReqAcceptContent) GetType() Type { 14 | return REQ_ACCEPT_CONTENT 15 | } 16 | 17 | func (r ReqAcceptContent) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 18 | if frontend.Mode == "http" { 19 | return errors.New("tcp accept-content rule is only available in TCP frontends") 20 | } 21 | tcpRule := models.TCPRequestRule{ 22 | Action: "reject", 23 | Type: "content", 24 | Cond: "if", 25 | CondTest: "!{ req_ssl_hello_type 1 }", 26 | } 27 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqBasicAuth.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | ) 10 | 11 | type ReqBasicAuth struct { 12 | Credentials map[string][]byte 13 | AuthGroup string 14 | AuthRealm string 15 | } 16 | 17 | func (r ReqBasicAuth) GetType() Type { 18 | return REQ_AUTH 19 | } 20 | 21 | func (r ReqBasicAuth) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) (err error) { 22 | var userList bool 23 | userList, err = client.UserListExistsByGroup(r.AuthGroup) 24 | if err != nil { 25 | return 26 | } 27 | if !userList { 28 | err = client.UserListCreateByGroup(r.AuthGroup, r.Credentials) 29 | if err != nil { 30 | return 31 | } 32 | } 33 | httpRule := models.HTTPRequestRule{ 34 | Type: "auth", 35 | AuthRealm: r.AuthRealm, 36 | Cond: "if", 37 | CondTest: fmt.Sprintf("!{ http_auth_group(%s) authenticated-users }", r.AuthGroup), 38 | } 39 | if err = client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL); err != nil { 40 | return 41 | } 42 | 43 | return 44 | } 45 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqCapture.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 7 | ) 8 | 9 | type ReqCapture struct { 10 | Expression string 11 | CaptureLen int64 12 | } 13 | 14 | func (r ReqCapture) GetType() Type { 15 | return REQ_CAPTURE 16 | } 17 | 18 | func (r ReqCapture) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 19 | if frontend.Mode == "tcp" { 20 | tcpRule := models.TCPRequestRule{ 21 | Type: "content", 22 | Action: "capture", 23 | CaptureLen: r.CaptureLen, 24 | Expr: r.Expression, 25 | } 26 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 27 | } 28 | httpRule := models.HTTPRequestRule{ 29 | Type: "capture", 30 | CaptureSample: r.Expression, 31 | CaptureLen: r.CaptureLen, 32 | } 33 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqDeny.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/maps" 10 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 11 | ) 12 | 13 | type ReqDeny struct { 14 | SrcIPsMap maps.Path 15 | AllowList bool 16 | } 17 | 18 | func (r ReqDeny) GetType() Type { 19 | return REQ_DENY 20 | } 21 | 22 | func (r ReqDeny) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 23 | not := "" 24 | if r.AllowList { 25 | not = "!" 26 | } 27 | if frontend.Mode == "tcp" { 28 | tcpRule := models.TCPRequestRule{ 29 | Type: "content", 30 | Action: "reject", 31 | Cond: "if", 32 | CondTest: fmt.Sprintf("%s{ src -f %s }", not, r.SrcIPsMap), 33 | } 34 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 35 | } 36 | httpRule := models.HTTPRequestRule{ 37 | Type: "deny", 38 | DenyStatus: utils.PtrInt64(403), 39 | Cond: "if", 40 | CondTest: fmt.Sprintf("%s{ src -f %s }", not, r.SrcIPsMap), 41 | } 42 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqInspectDelay.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | ) 10 | 11 | type ReqInspectDelay struct { 12 | Timeout *int64 13 | } 14 | 15 | func (r ReqInspectDelay) GetType() Type { 16 | return REQ_INSPECT_DELAY 17 | } 18 | 19 | func (r ReqInspectDelay) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 20 | if frontend.Mode == "http" { 21 | return errors.New("tcp inspect-delay rule is only available in TCP frontends") 22 | } 23 | tcpRule := models.TCPRequestRule{ 24 | Type: "inspect-delay", 25 | Timeout: r.Timeout, 26 | } 27 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqPathRewrite.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | ) 10 | 11 | type ReqPathRewrite struct { 12 | PathMatch string 13 | PathFmt string 14 | } 15 | 16 | func (r ReqPathRewrite) GetType() Type { 17 | return REQ_PATH_REWRITE 18 | } 19 | 20 | func (r ReqPathRewrite) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 21 | if frontend.Mode == "tcp" { 22 | return errors.New("SSL redirect cannot be configured in TCP mode") 23 | } 24 | httpRule := models.HTTPRequestRule{ 25 | Type: "replace-path", 26 | PathMatch: r.PathMatch, 27 | PathFmt: r.PathFmt, 28 | } 29 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqProxyProtocol.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/maps" 10 | ) 11 | 12 | type ReqProxyProtocol struct { 13 | SrcIPsMap maps.Path 14 | } 15 | 16 | func (r ReqProxyProtocol) GetType() Type { 17 | return REQ_PROXY_PROTOCOL 18 | } 19 | 20 | func (r ReqProxyProtocol) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 21 | tcpRule := models.TCPRequestRule{ 22 | Type: "connection", 23 | Action: models.TCPRequestRuleActionExpectDashProxy, 24 | Cond: "if", 25 | CondTest: fmt.Sprintf("{ src -f %s }", r.SrcIPsMap), 26 | } 27 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqRatelimit.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/haproxytech/client-native/v6/models" 8 | 9 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 10 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 11 | ) 12 | 13 | type ReqRateLimit struct { 14 | TableName string 15 | ReqsLimit int64 16 | DenyStatusCode int64 17 | } 18 | 19 | func (r ReqRateLimit) GetType() Type { 20 | return REQ_RATELIMIT 21 | } 22 | 23 | func (r ReqRateLimit) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 24 | if frontend.Mode == "tcp" { 25 | return errors.New("request Track cannot be configured in TCP mode") 26 | } 27 | httpRule := models.HTTPRequestRule{ 28 | Type: "deny", 29 | DenyStatus: utils.PtrInt64(r.DenyStatusCode), 30 | Cond: "if", 31 | CondTest: fmt.Sprintf("{ sc0_http_req_rate(%s) gt %d }", r.TableName, r.ReqsLimit), 32 | } 33 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqRequestRedirect.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/haproxytech/client-native/v6/models" 8 | 9 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 10 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 11 | ) 12 | 13 | type RequestRedirect struct { 14 | Host string 15 | RedirectCode int64 16 | RedirectPort int 17 | SSLRequest bool 18 | SSLRedirect bool 19 | } 20 | 21 | func (r RequestRedirect) GetType() Type { 22 | return REQ_REDIRECT 23 | } 24 | 25 | func (r RequestRedirect) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 26 | if frontend.Mode == "tcp" { 27 | return errors.New("request redirection cannot be configured in TCP mode") 28 | } 29 | var rule string 30 | if r.SSLRedirect { 31 | rule = fmt.Sprintf("https://%%[hdr(host),field(1,:)]:%d%%[capture.req.uri]", r.RedirectPort) 32 | } else { 33 | scheme := "http" 34 | if r.SSLRequest { 35 | scheme = "https" 36 | } 37 | rule = fmt.Sprintf(scheme+"://%s%%[capture.req.uri]", r.Host) 38 | } 39 | httpRule := models.HTTPRequestRule{ 40 | Type: "redirect", 41 | RedirCode: utils.PtrInt64(r.RedirectCode), 42 | RedirValue: rule, 43 | RedirType: "location", 44 | } 45 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqRequestRedirectQuic.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | ) 10 | 11 | type RequestRedirectQuic struct{} 12 | 13 | func (r RequestRedirectQuic) GetType() Type { 14 | return REQ_REDIRECT 15 | } 16 | 17 | func (r RequestRedirectQuic) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 18 | if frontend.Mode == "tcp" { 19 | return errors.New("request redirection cannot be configured in TCP mode") 20 | } 21 | 22 | httpRule := models.HTTPRequestRule{ 23 | Type: "redirect", 24 | Cond: "unless", 25 | CondTest: "{ ssl_fc }", 26 | RedirType: "scheme", 27 | RedirValue: "https", 28 | } 29 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqReturnStatus.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 8 | ) 9 | 10 | //nolint:golint,stylecheck 11 | var MIME_TYPE_TEXT_PLAIN string = "text/plain" 12 | 13 | type ReqReturnStatus struct { 14 | StatusCode int64 15 | } 16 | 17 | func (r ReqReturnStatus) GetType() Type { 18 | return REQ_RETURN_STATUS 19 | } 20 | 21 | func (r ReqReturnStatus) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 22 | if frontend.Mode == "tcp" { 23 | return errors.New("HTTP status cannot be set in TCP mode") 24 | } 25 | httpRule := models.HTTPRequestRule{ 26 | ReturnStatusCode: &r.StatusCode, 27 | Type: "return", 28 | } 29 | ingressACL += " METH_OPTIONS" 30 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqSetSrc.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/client-native/v6/models" 7 | 8 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 9 | ) 10 | 11 | type ReqSetSrc struct { 12 | HeaderName string 13 | } 14 | 15 | func (r ReqSetSrc) GetType() Type { 16 | return REQ_SET_SRC 17 | } 18 | 19 | func (r ReqSetSrc) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 20 | if len(r.HeaderName) == 0 { 21 | return nil 22 | } 23 | if frontend.Mode == "tcp" { 24 | tcpRule := models.TCPRequestRule{ 25 | Type: "set-src", 26 | Expr: fmt.Sprintf("hdr(%s)", r.HeaderName), 27 | } 28 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 29 | } 30 | httpRule := models.HTTPRequestRule{ 31 | Type: "set-src", 32 | Expr: fmt.Sprintf("hdr(%s)", r.HeaderName), 33 | } 34 | ingressACL += " || !{ var(txn.path_match) -m found }" 35 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqSetVar.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 7 | ) 8 | 9 | type ReqSetVar struct { 10 | Name string 11 | Scope string 12 | Expression string 13 | CondTest string 14 | } 15 | 16 | func (r ReqSetVar) GetType() Type { 17 | return REQ_SET_VAR 18 | } 19 | 20 | func (r ReqSetVar) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 21 | if frontend.Mode == "tcp" { 22 | tcpRule := models.TCPRequestRule{ 23 | Type: "content", 24 | Action: "set-var", 25 | VarName: r.Name, 26 | VarScope: r.Scope, 27 | Expr: r.Expression, 28 | } 29 | return client.FrontendTCPRequestRuleCreate(0, frontend.Name, tcpRule, ingressACL) 30 | } 31 | httpRule := models.HTTPRequestRule{ 32 | Type: "set-var", 33 | VarName: r.Name, 34 | VarScope: r.Scope, 35 | VarExpr: r.Expression, 36 | } 37 | if r.CondTest != "" { 38 | httpRule.Cond = "if" 39 | httpRule.CondTest = r.CondTest 40 | } 41 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/reqTrack.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/haproxytech/client-native/v6/models" 8 | 9 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 10 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 11 | ) 12 | 13 | type ReqTrack struct { 14 | TableName string 15 | TablePeriod *int64 16 | TableSize *int64 17 | TrackKey string 18 | } 19 | 20 | func (r ReqTrack) GetType() Type { 21 | return REQ_TRACK 22 | } 23 | 24 | func (r ReqTrack) Create(client api.HAProxyClient, frontend *models.Frontend, ingressACL string) error { 25 | if frontend.Mode == "tcp" { 26 | return errors.New("request Track cannot be configured in TCP mode") 27 | } 28 | 29 | // Create tracking table. 30 | if !client.BackendUsed(r.TableName) { 31 | backend := models.Backend{ 32 | BackendBase: models.BackendBase{ 33 | Name: r.TableName, 34 | StickTable: &models.ConfigStickTable{ 35 | Peers: "localinstance", 36 | Type: "ip", 37 | Size: r.TableSize, 38 | Store: fmt.Sprintf("http_req_rate(%d)", *r.TablePeriod), 39 | }, 40 | }, 41 | } 42 | // Create tracking table. 43 | client.BackendCreateOrUpdate(backend) 44 | } 45 | 46 | // Create rule 47 | httpRule := models.HTTPRequestRule{ 48 | Type: "track-sc", 49 | TrackScStickCounter: utils.PtrInt64(0), 50 | TrackScKey: r.TrackKey, 51 | TrackScTable: r.TableName, 52 | } 53 | return client.FrontendHTTPRequestRuleCreate(0, frontend.Name, httpRule, ingressACL) 54 | } 55 | -------------------------------------------------------------------------------- /pkg/haproxy/rules/types.go: -------------------------------------------------------------------------------- 1 | package rules 2 | 3 | // Order matters ! 4 | // Rules will be evaluated by HAProxy in the defined order. 5 | type Type int 6 | 7 | //nolint:golint,stylecheck 8 | const ( 9 | REQ_ACCEPT_CONTENT Type = iota 10 | REQ_INSPECT_DELAY 11 | REQ_PROXY_PROTOCOL 12 | REQ_SET_VAR 13 | REQ_SET_SRC 14 | REQ_DENY 15 | REQ_TRACK 16 | REQ_AUTH 17 | REQ_RATELIMIT 18 | REQ_CAPTURE 19 | REQ_REDIRECT 20 | REQ_FORWARDED_PROTO 21 | REQ_SET_HEADER 22 | REQ_SET_HOST 23 | REQ_PATH_REWRITE 24 | REQ_RETURN_STATUS 25 | RES_SET_HEADER 26 | ) 27 | 28 | var constLookup = map[Type]string{ 29 | REQ_ACCEPT_CONTENT: "REQ_ACCEPT_CONTENT", 30 | REQ_INSPECT_DELAY: "REQ_INSPECT_DELAY", 31 | REQ_PROXY_PROTOCOL: "REQ_PROXY_PROTOCOL", 32 | REQ_SET_VAR: "REQ_SET_VAR", 33 | REQ_SET_SRC: "REQ_SET_SRC", 34 | REQ_DENY: "REQ_DENY", 35 | REQ_TRACK: "REQ_TRACK", 36 | REQ_AUTH: "REQ_AUTH", 37 | REQ_RATELIMIT: "REQ_RATELIMIT", 38 | REQ_CAPTURE: "REQ_CAPTURE", 39 | REQ_REDIRECT: "REQ_REDIRECT", 40 | REQ_FORWARDED_PROTO: "REQ_FORWARDED_PROTO", 41 | REQ_SET_HEADER: "REQ_SET_HEADER", 42 | REQ_SET_HOST: "REQ_SET_HOST", 43 | REQ_PATH_REWRITE: "REQ_PATH_REWRITE", 44 | RES_SET_HEADER: "RES_SET_HEADER", 45 | REQ_RETURN_STATUS: "REQ_RETURN_STATUS", 46 | } 47 | -------------------------------------------------------------------------------- /pkg/ingress/types.go: -------------------------------------------------------------------------------- 1 | package ingress 2 | 3 | import ( 4 | "github.com/haproxytech/kubernetes-ingress/pkg/store" 5 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 6 | corev1 "k8s.io/api/core/v1" 7 | ) 8 | 9 | var logger = utils.GetLogger() 10 | 11 | type Sync struct { 12 | Service *corev1.Service 13 | Ingress *store.Ingress 14 | } 15 | -------------------------------------------------------------------------------- /pkg/k8s/informer-utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8s 16 | 17 | import ( 18 | k8smeta "github.com/haproxytech/kubernetes-ingress/pkg/k8s/meta" 19 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 20 | "k8s.io/apimachinery/pkg/types" 21 | ) 22 | 23 | func ToSyncDataEvent(meta k8smeta.MetaInfoer, data interface{}, uid types.UID, resourceVersion string) k8ssync.SyncDataEvent { 24 | return k8ssync.SyncDataEvent{ 25 | SyncType: meta.GetType(), 26 | Namespace: meta.GetNamespace(), 27 | Name: meta.GetName(), 28 | Data: data, 29 | UID: uid, 30 | ResourceVersion: resourceVersion, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/k8s/logging.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8s 16 | 17 | import ( 18 | k8smeta "github.com/haproxytech/kubernetes-ingress/pkg/k8s/meta" 19 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 20 | "k8s.io/apimachinery/pkg/types" 21 | ) 22 | 23 | func logIncomingK8sEvent(logger utils.Logger, meta k8smeta.MetaInfoer, uid types.UID, resourceVersion string, additionalInfo ...string) { 24 | logger.Tracef("[RUNTIME] [K8s] %s %s: %s %s/%s ", 25 | meta.GetType(), 26 | meta.GetStatus(), 27 | meta.GetName(), 28 | uid, 29 | resourceVersion, 30 | additionalInfo) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/k8s/meta/meta.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package meta 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | type MetaInfoer interface { 22 | GetNamespace() string 23 | GetName() string 24 | GetType() k8ssync.SyncType 25 | GetStatus() string 26 | } 27 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-backend.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | import ( 18 | v3 "github.com/haproxytech/kubernetes-ingress/crs/api/ingress/v3" 19 | ) 20 | 21 | func TransformBackend(obj interface{}) (interface{}, error) { 22 | backend, ok := obj.(*v3.Backend) 23 | if !ok { 24 | return obj, nil 25 | } 26 | 27 | // Fields to remove 28 | backend.Spec.Servers = nil 29 | 30 | return backend, nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | import ( 18 | ammeta "k8s.io/apimachinery/pkg/api/meta" 19 | ) 20 | 21 | func TransformCommon(obj interface{}) (interface{}, error) { 22 | data, err := ammeta.Accessor(obj) 23 | if err != nil { 24 | return obj, err 25 | } 26 | 27 | data.SetManagedFields(nil) 28 | 29 | return data, nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-configmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | func TransformConfigmap(obj interface{}) (interface{}, error) { 18 | return TransformCommon(obj) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-endpoints.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | func TransformEndpoints(obj interface{}) (interface{}, error) { 18 | return TransformCommon(obj) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-ingressclass.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | func TransformIngressClass(obj interface{}) (interface{}, error) { 18 | return TransformCommon(obj) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-namespace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | func TransformNamespace(obj interface{}) (interface{}, error) { 18 | return TransformCommon(obj) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | func TransformSecret(obj interface{}) (interface{}, error) { 18 | return TransformCommon(obj) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/k8s/transform/transform-service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package k8stransform 16 | 17 | func TransformService(obj interface{}) (interface{}, error) { 18 | return TransformCommon(obj) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/rules/acls/acls.go: -------------------------------------------------------------------------------- 1 | package acls 2 | 3 | import ( 4 | "github.com/haproxytech/client-native/v6/models" 5 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/api" 6 | "github.com/haproxytech/kubernetes-ingress/pkg/haproxy/instance" 7 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 8 | ) 9 | 10 | //nolint:golint,stylecheck 11 | type ACL_DESTINATION string 12 | 13 | //nolint:golint,stylecheck 14 | const ( 15 | ACL_FRONTEND ACL_DESTINATION = "frontend" 16 | ACL_BACKEND ACL_DESTINATION = "backend" 17 | ) 18 | 19 | func PopulateBackend(client api.HAProxyClient, name string, acls models.Acls) { 20 | Populate(client, name, acls, ACL_BACKEND) 21 | } 22 | 23 | func PopulateFrontend(client api.HAProxyClient, name string, acls models.Acls) { 24 | Populate(client, name, acls, ACL_FRONTEND) 25 | } 26 | 27 | func Populate(client api.ACL, name string, rules models.Acls, resource ACL_DESTINATION) { 28 | currentAcls, errAcls := client.ACLsGet(string(resource), name) 29 | 30 | // There is a resource ... 31 | if errAcls == nil { 32 | diffAcls := rules.Diff(currentAcls) 33 | // ... with different acls from the resource. 34 | if len(diffAcls) != 0 { 35 | err := client.ACLsReplace(string(resource), name, rules) 36 | if err != nil { 37 | utils.GetLogger().Err(err) 38 | return 39 | } 40 | // ... we reload because we created some acls. 41 | instance.Reload("%s '%s', acls updated: %+v", resource, name, utils.JSONDiff(diffAcls)) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/rules/constant.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package rules 16 | 17 | type ParentType string 18 | 19 | const ( 20 | ParentTypeFrontend ParentType = "frontend" 21 | ParentTypeBackend ParentType = "backend" 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/store/events-cr-backend.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | v3 "github.com/haproxytech/kubernetes-ingress/crs/api/ingress/v3" 19 | ) 20 | 21 | func (k *K8s) EventBackendCR(namespace, name string, data *v3.Backend) bool { 22 | ns := k.GetNamespace(namespace) 23 | if data == nil { 24 | delete(ns.CRs.Backends, name) 25 | return true 26 | } 27 | ns.CRs.Backends[name] = &data.Spec 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /pkg/store/events-cr-defaults.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | v3 "github.com/haproxytech/kubernetes-ingress/crs/api/ingress/v3" 19 | ) 20 | 21 | func (k *K8s) EventDefaultsCR(namespace, name string, data *v3.Defaults) bool { 22 | ns := k.GetNamespace(namespace) 23 | if data == nil { 24 | delete(ns.CRs.Defaults, name) 25 | return true 26 | } 27 | ns.CRs.Defaults[name] = &data.Spec.Defaults 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /pkg/store/events-cr-global.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | v3 "github.com/haproxytech/kubernetes-ingress/crs/api/ingress/v3" 19 | ) 20 | 21 | func (k *K8s) EventGlobalCR(namespace, name string, data *v3.Global) bool { 22 | ns := k.GetNamespace(namespace) 23 | if data == nil { 24 | delete(ns.CRs.Global, name) 25 | return true 26 | } 27 | ns.CRs.Global[name] = &data.Spec.Global 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /pkg/store/status.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | type Status string 18 | 19 | const ( 20 | ADDED Status = "ADDED" 21 | DELETED Status = "DELETED" 22 | ERROR Status = "ERROR" 23 | EMPTY Status = "" 24 | MODIFIED Status = "MODIFIED" 25 | ) 26 | -------------------------------------------------------------------------------- /pkg/store/types-configmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (a ConfigMap) GetType() k8ssync.SyncType { 22 | return k8ssync.CONFIGMAP 23 | } 24 | 25 | func (a ConfigMap) GetName() string { 26 | return a.Name 27 | } 28 | 29 | func (a ConfigMap) GetNamespace() string { 30 | return a.Namespace 31 | } 32 | 33 | func (a ConfigMap) GetStatus() string { 34 | return string(a.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-endpoints.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (a Endpoints) GetType() k8ssync.SyncType { 22 | return k8ssync.ENDPOINTS 23 | } 24 | 25 | func (a Endpoints) GetName() string { 26 | return a.SliceName 27 | } 28 | 29 | func (a Endpoints) GetNamespace() string { 30 | return a.Namespace 31 | } 32 | 33 | func (a Endpoints) GetStatus() string { 34 | return string(a.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-ingress.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (i Ingress) GetType() k8ssync.SyncType { 22 | return k8ssync.INGRESS 23 | } 24 | 25 | func (i Ingress) GetName() string { 26 | return i.Name 27 | } 28 | 29 | func (i Ingress) GetNamespace() string { 30 | return i.Namespace 31 | } 32 | 33 | func (i Ingress) GetStatus() string { 34 | return string(i.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-ingressclass.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (a IngressClass) GetType() k8ssync.SyncType { 22 | return k8ssync.INGRESS_CLASS 23 | } 24 | 25 | func (a IngressClass) GetName() string { 26 | return a.Name 27 | } 28 | 29 | func (a IngressClass) GetNamespace() string { 30 | return "" 31 | } 32 | 33 | func (a IngressClass) GetStatus() string { 34 | return string(a.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-namespace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (ns Namespace) GetType() k8ssync.SyncType { 22 | return k8ssync.NAMESPACE 23 | } 24 | 25 | func (ns Namespace) GetName() string { 26 | return ns.Name 27 | } 28 | 29 | func (ns Namespace) GetNamespace() string { 30 | return ns.Name 31 | } 32 | 33 | func (ns Namespace) GetStatus() string { 34 | return string(ns.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-pod.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (i PodEvent) GetType() k8ssync.SyncType { 22 | return k8ssync.POD 23 | } 24 | 25 | func (i PodEvent) GetName() string { 26 | return i.Name 27 | } 28 | 29 | func (i PodEvent) GetNamespace() string { 30 | return i.Namespace 31 | } 32 | 33 | func (i PodEvent) GetStatus() string { 34 | return string(i.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-secret.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (a Secret) GetType() k8ssync.SyncType { 22 | return k8ssync.SECRET 23 | } 24 | 25 | func (a Secret) GetName() string { 26 | return a.Name 27 | } 28 | 29 | func (a Secret) GetNamespace() string { 30 | return a.Namespace 31 | } 32 | 33 | func (a Secret) GetStatus() string { 34 | return string(a.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | k8ssync "github.com/haproxytech/kubernetes-ingress/pkg/k8s/sync" 19 | ) 20 | 21 | func (a Service) GetType() k8ssync.SyncType { 22 | return k8ssync.SERVICE 23 | } 24 | 25 | func (a Service) GetName() string { 26 | return a.Name 27 | } 28 | 29 | func (a Service) GetNamespace() string { 30 | return a.Namespace 31 | } 32 | 33 | func (a Service) GetStatus() string { 34 | return string(a.Status) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/store/types-utils.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/haproxytech/kubernetes-ingress/pkg/utils" 7 | ) 8 | 9 | func (gw *Gateway) IsValid() error { 10 | if len(gw.Listeners) == 0 { 11 | return fmt.Errorf("Gateway '%s/%s' has no listeners", gw.Namespace, gw.Name) 12 | } 13 | err := utils.Errors{} 14 | combinations := map[string]struct{}{} 15 | for _, listener := range gw.Listeners { 16 | hostname := "" 17 | if listener.Hostname != nil { 18 | hostname = *listener.Hostname 19 | } 20 | key := fmt.Sprintf("%s/%d/%s", hostname, listener.Port, listener.Protocol) 21 | if _, found := combinations[key]; found { 22 | err.Add(fmt.Errorf("duplicate combination hostname/port/protocol '%s' in listeners from gateway '%s/%s", key, gw.Namespace, gw.Name)) 23 | } 24 | combinations[key] = struct{}{} 25 | } 26 | return err.Result() 27 | } 28 | 29 | func (tcproutes TCPRoutes) Less(i, j int) bool { 30 | tcprouteI := tcproutes[i] 31 | tcprouteJ := tcproutes[j] 32 | if !tcprouteI.CreationTime.Equal(tcprouteJ.CreationTime) { 33 | return tcprouteI.CreationTime.Before(tcprouteJ.CreationTime) 34 | } 35 | return tcprouteI.Namespace+tcprouteI.Name < tcprouteJ.Namespace+tcprouteJ.Name 36 | } 37 | -------------------------------------------------------------------------------- /pkg/utils/errors.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Errors []error 8 | 9 | func (e *Errors) Add(errors ...error) { 10 | for _, err := range errors { 11 | if err != nil { 12 | *e = append(*e, err) 13 | } 14 | } 15 | } 16 | 17 | func (e *Errors) Result() error { 18 | var result string 19 | for _, err := range *e { 20 | result += err.Error() + "\n" 21 | } 22 | if result == "" { 23 | return nil 24 | } 25 | return errors.New(result) 26 | } 27 | 28 | func (e *Errors) AddErrors(errors Errors) { 29 | for _, err := range errors { 30 | e.Add((err)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/utils/flags-experimental.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | type Experimental struct { 22 | UseIngressMerge bool 23 | } 24 | 25 | const ( 26 | flagSeparator = "," 27 | // Experimental flags 28 | flagUseIngressMerge = "use-ingress-merge" 29 | ) 30 | 31 | // UnmarshalFlag Unmarshal flag 32 | func (e *Experimental) UnmarshalFlag(value string) error { 33 | var errors Errors 34 | flags := strings.Split(value, flagSeparator) 35 | 36 | // Then parse 37 | for _, flag := range flags { 38 | if flag == flagUseIngressMerge { 39 | e.UseIngressMerge = true 40 | continue 41 | } 42 | } 43 | 44 | return errors.Result() 45 | } 46 | 47 | // MarshalFlag Marshals flag 48 | func (e Experimental) MarshalFlag() (string, error) { 49 | flags := []string{} 50 | if e.UseIngressMerge { 51 | flags = append(flags, flagUseIngressMerge) 52 | } 53 | return strings.Join(flags, flagSeparator), nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/utils/orderedset.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "sort" 4 | 5 | type OrderedSet[K comparable, T any] struct { 6 | items []T 7 | seen map[K]struct{} 8 | keyFn func(T) K 9 | lessFn func(a, b T) bool 10 | } 11 | 12 | func NewOrderedSet[K comparable, T any](keyFn func(T) K, lessFn func(a, b T) bool) *OrderedSet[K, T] { 13 | return &OrderedSet[K, T]{ 14 | items: []T{}, 15 | seen: make(map[K]struct{}), 16 | keyFn: keyFn, 17 | lessFn: lessFn, 18 | } 19 | } 20 | 21 | func (os *OrderedSet[K, T]) Add(item T) { 22 | key := os.keyFn(item) 23 | if _, exists := os.seen[key]; exists { 24 | return 25 | } 26 | 27 | i := sort.Search(len(os.items), func(i int) bool { 28 | return os.lessFn(item, os.items[i]) 29 | }) 30 | os.items = append(os.items, item) 31 | copy(os.items[i+1:], os.items[i:]) 32 | os.items[i] = item 33 | os.seen[key] = struct{}{} 34 | } 35 | 36 | func (os *OrderedSet[K, T]) Remove(item T) { 37 | key := os.keyFn(item) 38 | if _, exists := os.seen[key]; !exists { 39 | return 40 | } 41 | delete(os.seen, key) 42 | for i, v := range os.items { 43 | if os.keyFn(v) == key { 44 | os.items = append(os.items[:i], os.items[i+1:]...) 45 | break 46 | } 47 | } 48 | } 49 | 50 | func (os *OrderedSet[K, T]) Items() []T { 51 | return os.items 52 | } 53 | -------------------------------------------------------------------------------- /pkg/version/runtime.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "errors" 19 | "runtime/debug" 20 | ) 21 | 22 | func Set() error { 23 | buildinfo, ok := debug.ReadBuildInfo() 24 | if !ok { 25 | return errors.New("not able to read build data") 26 | } 27 | GitRepo = buildinfo.Main.Path 28 | GitCommitDate = get(buildinfo, "vcs.time") 29 | GitCommit = get(buildinfo, "vcs.revision") 30 | if len(GitCommit) > 8 { 31 | GitCommit = GitCommit[:8] 32 | } 33 | 34 | return nil 35 | } 36 | 37 | func get(buildInfo *debug.BuildInfo, key string) string { 38 | for _, setting := range buildInfo.Settings { 39 | if setting.Key == key { 40 | return setting.Value 41 | } 42 | } 43 | return "" 44 | } 45 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 HAProxy Technologies LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | // GitRepo ... 18 | var GitRepo = "" 19 | 20 | // GitTag ... 21 | var GitTag = "dev" 22 | 23 | // GitCommit ... 24 | var GitCommit = "" 25 | 26 | // GitCommitDate ... 27 | var GitCommitDate = "" 28 | 29 | // IngressControllerInfo console pretty print 30 | const IngressControllerInfo = ` 31 | _ _ _ ____ 32 | | | | | / \ | _ \ _ __ _____ ___ _ 33 | | |_| | / _ \ | |_) | '__/ _ \ \/ / | | | 34 | | _ |/ ___ \| __/| | | (_) > <| |_| | 35 | |_| |_/_/ \_\_| |_| \___/_/\_\\__, | 36 | _ __ _ |___/ ___ ____ 37 | | |/ / _| |__ ___ _ __ _ __ ___| |_ ___ ___ |_ _/ ___| 38 | | ' / | | | '_ \ / _ \ '__| '_ \ / _ \ __/ _ \/ __| | | | 39 | | . \ |_| | |_) | __/ | | | | | __/ || __/\__ \ | | |___ 40 | |_|\_\__,_|_.__/ \___|_| |_| |_|\___|\__\___||___/ |___\____| 41 | 42 | ` 43 | -------------------------------------------------------------------------------- /test/tcp-cr/expectations/check-collisions/coll-address-port.yaml: -------------------------------------------------------------------------------- 1 | - tcpmodel: 2 | name: service1CollAddPort 3 | frontend: 4 | frontendbase: 5 | name: fe11 6 | binds: 7 | acceptproxy: 8 | accept_proxy: true 9 | port: 1 10 | other: 11 | port: 11 12 | service: 13 | name: service1 14 | port: 443 15 | parent_name: tcp-1 16 | namespace: ns 17 | collision_status: ERROR 18 | reason: "-- Collision AddPort :1 with ns/tcp-1/service1 -- Collision AddPort :11 with 19 | ns/tcp-1/service1 " 20 | creation_timestamp: 2024-06-16T11:45:26.371Z 21 | - tcpmodel: 22 | name: service1 23 | frontend: 24 | frontendbase: 25 | name: fe1 26 | binds: 27 | acceptproxy: 28 | accept_proxy: true 29 | port: 1 30 | other: 31 | port: 11 32 | service: 33 | name: service1 34 | port: 443 35 | parent_name: tcp-1 36 | namespace: ns 37 | creation_timestamp: 2024-05-16T11:45:26.371Z 38 | - tcpmodel: 39 | name: service2 40 | frontend: 41 | frontendbase: 42 | name: fe2 43 | binds: 44 | acceptproxy: 45 | accept_proxy: true 46 | port: 2 47 | other: 48 | port: 22 49 | service: 50 | name: service2 51 | port: 443 52 | parent_name: tcp-1 53 | namespace: ns 54 | creation_timestamp: 2024-05-16T11:45:26.371Z 55 | -------------------------------------------------------------------------------- /test/tcp-cr/expectations/has-collisions/coll-fe-name.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fe1: 3 | - tcpmodel: 4 | name: service1CollFeName 5 | frontend: 6 | frontendbase: 7 | name: fe1 8 | binds: 9 | acceptproxy: 10 | accept_proxy: true 11 | port: 100 12 | other: 13 | port: 101 14 | service: 15 | name: service1 16 | port: 443 17 | parent_name: tcp-1 18 | namespace: ns 19 | collision_status: ERROR 20 | reason: "-- Collision FE.Name with ns/tcp-1/service1" 21 | creation_timestamp: 2024-07-16T11:45:26.371Z 22 | - tcpmodel: 23 | name: service1 24 | frontend: 25 | frontendbase: 26 | name: fe1 27 | binds: 28 | acceptproxy: 29 | accept_proxy: true 30 | port: 1 31 | other: 32 | port: 11 33 | service: 34 | name: service1 35 | port: 443 36 | parent_name: tcp-1 37 | namespace: ns 38 | collision_status: ERROR 39 | reason: "-- Collision FE.Name with ns/tcp-1/service1CollFeName" 40 | creation_timestamp: 2024-05-16T11:45:26.371Z 41 | -------------------------------------------------------------------------------- /test/tcp-cr/expectations/no-collision.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - tcpmodel: 3 | name: service1 4 | frontend: 5 | frontendbase: 6 | name: fe1 7 | binds: 8 | acceptproxy: 9 | accept_proxy: true 10 | port: 1 11 | other: 12 | port: 11 13 | service: 14 | name: service1 15 | port: 443 16 | parent_name: tcp-1 17 | namespace: ns 18 | creation_timestamp: 2024-05-16T11:45:26.371Z 19 | - tcpmodel: 20 | name: service2 21 | frontend: 22 | frontendbase: 23 | name: fe2 24 | binds: 25 | acceptproxy: 26 | accept_proxy: true 27 | port: 2 28 | other: 29 | port: 22 30 | service: 31 | name: service2 32 | port: 443 33 | parent_name: tcp-1 34 | namespace: ns 35 | creation_timestamp: 2024-05-16T11:45:26.371Z 36 | -------------------------------------------------------------------------------- /test/tcp-cr/expectations/ordered.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | items: 3 | - tcpmodel: 4 | name: service1 5 | frontend: 6 | frontendbase: 7 | name: fe1 8 | binds: 9 | other: 10 | port: 1 11 | acceptproxy: 12 | accept_proxy: true 13 | port: 11 14 | service: 15 | name: service1 16 | port: 443 17 | parent_name: tcp-1 18 | namespace: ns 19 | creation_timestamp: 2024-07-16T11:45:26.371Z 20 | - tcpmodel: 21 | name: service1 22 | frontend: 23 | frontendbase: 24 | name: fe11 25 | binds: 26 | other: 27 | port: 1 28 | acceptproxy: 29 | accept_proxy: true 30 | port: 11 31 | service: 32 | name: service1 33 | port: 443 34 | parent_name: tcp-1 35 | namespace: ns 36 | creation_timestamp: 2024-06-16T11:45:26.371Z 37 | - tcpmodel: 38 | name: service2 39 | frontend: 40 | frontendbase: 41 | name: fe2 42 | binds: 43 | acceptproxy: 44 | accept_proxy: true 45 | port: 2 46 | other: 47 | port: 22 48 | service: 49 | name: service2 50 | port: 443 51 | parent_name: tcp-1 52 | namespace: ns 53 | creation_timestamp: 2024-06-16T11:45:26.371Z 54 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp1-coll-address-port-2.yaml: -------------------------------------------------------------------------------- 1 | tcpmodel: 2 | name: service1CollAddPort2 3 | frontend: 4 | frontendbase: 5 | name: fe111 6 | binds: 7 | acceptproxy: 8 | port: 1 9 | accept_proxy: true 10 | other: 11 | port: 111 12 | service: 13 | name: service1 14 | port: 443 15 | namespace: ns 16 | creation_timestamp: 2024-08-16T11:45:26.371Z 17 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp1-coll-address-port.yaml: -------------------------------------------------------------------------------- 1 | tcpmodel: 2 | name: service1CollAddPort 3 | frontend: 4 | frontendbase: 5 | name: fe11 6 | binds: 7 | acceptproxy: 8 | port: 1 9 | accept_proxy: true 10 | other: 11 | port: 11 12 | service: 13 | name: service1 14 | port: 443 15 | namespace: ns 16 | creation_timestamp: 2024-06-16T11:45:26.371Z 17 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp1-coll-fe-name.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | tcpmodel: 3 | name: service1CollFeName 4 | frontend: 5 | frontendbase: 6 | name: fe1 7 | binds: 8 | acceptproxy: 9 | port: 100 10 | accept_proxy: true 11 | other: 12 | port: 101 13 | service: 14 | name: service1 15 | port: 443 16 | namespace: ns 17 | creation_timestamp: 2024-07-16T11:45:26.371Z 18 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp1.yaml: -------------------------------------------------------------------------------- 1 | tcpmodel: 2 | name: service1 3 | frontend: 4 | frontendbase: 5 | name: fe1 6 | binds: 7 | acceptproxy: 8 | accept_proxy: true 9 | port: 1 10 | other: 11 | port: 11 12 | service: 13 | name: service1 14 | port: 443 15 | namespace: ns 16 | creation_timestamp: 2024-05-16T11:45:26.371Z 17 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp2-coll-address-port.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | tcpmodel: 3 | name: service2CollAddrPort 4 | frontend: 5 | frontendbase: 6 | name: fe2 7 | binds: 8 | acceptproxy: 9 | port: 2 10 | accept_proxy: true 11 | other: 12 | port: 234567 13 | service: 14 | name: service2 15 | port: 443 16 | namespace: ns 17 | creation_timestamp: 2024-06-16T11:45:26.371Z 18 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | tcpmodel: 3 | name: service2 4 | frontend: 5 | frontendbase: 6 | name: fe2 7 | binds: 8 | acceptproxy: 9 | port: 2 10 | accept_proxy: true 11 | other: 12 | port: 22 13 | service: 14 | name: service2 15 | port: 443 16 | namespace: ns 17 | creation_timestamp: 2024-05-16T11:45:26.371Z 18 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp3.yaml: -------------------------------------------------------------------------------- 1 | tcpmodel: 2 | name: service3 3 | frontend: 4 | frontendbase: 5 | name: fe3 6 | binds: 7 | acceptproxy: 8 | port: 3 9 | accept_proxy: true 10 | other: 11 | port: 33 12 | service: 13 | name: service3 14 | port: 443 15 | namespace: ns 16 | creation_timestamp: 2024-05-16T11:45:26.371Z 17 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/tcp4.yaml: -------------------------------------------------------------------------------- 1 | tcpmodel: 2 | name: service4 3 | frontend: 4 | frontendbase: 5 | name: fe4 6 | binds: 7 | acceptproxy: 8 | port: 4 9 | accept_proxy: true 10 | other: 11 | port: 44 12 | service: 13 | name: service4 14 | port: 443 15 | namespace: ns 16 | creation_timestamp: 2024-05-16T11:45:26.371Z 17 | -------------------------------------------------------------------------------- /test/tcp-cr/manifests/unordered.yaml: -------------------------------------------------------------------------------- 1 | items: 2 | - tcpmodel: 3 | name: service2 4 | frontend: 5 | frontendbase: 6 | name: fe2 7 | binds: 8 | acceptproxy: 9 | accept_proxy: true 10 | port: 2 11 | other: 12 | port: 22 13 | service: 14 | name: service2 15 | port: 443 16 | parent_name: tcp-1 17 | namespace: ns 18 | creation_timestamp: 2024-06-16T11:45:26.371Z 19 | - tcpmodel: 20 | name: service1 21 | frontend: 22 | frontendbase: 23 | name: fe11 24 | binds: 25 | acceptproxy: 26 | accept_proxy: true 27 | port: 1 28 | other: 29 | port: 1 30 | service: 31 | name: service1 32 | port: 443 33 | parent_name: tcp-1 34 | namespace: ns 35 | creation_timestamp: 2024-06-16T11:45:26.371Z 36 | - tcpmodel: 37 | name: service1 38 | frontend: 39 | frontendbase: 40 | name: fe1 41 | binds: 42 | acceptproxy: 43 | accept_proxy: true 44 | port: 11 45 | other: 46 | port: 1 47 | service: 48 | name: service1 49 | port: 443 50 | parent_name: tcp-1 51 | namespace: ns 52 | creation_timestamp: 2024-07-16T11:45:26.371Z 53 | -------------------------------------------------------------------------------- /test/transform/data/ingress/dupl-1.yaml: -------------------------------------------------------------------------------- 1 | kind: Ingress 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: testingress 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | tls: 9 | - hosts: 10 | - test1.haproxy 11 | - test1.haproxy 12 | - test1.haproxy 13 | secretName: test1 14 | - hosts: 15 | - test2.haproxy 16 | secretName: test2 17 | rules: 18 | - host: "test.haproxy" 19 | http: 20 | paths: 21 | - path: / 22 | pathType: Prefix 23 | backend: 24 | service: 25 | name: foo1 26 | port: 27 | name: http 28 | - host: "test.haproxy" 29 | http: 30 | paths: 31 | - path: / 32 | pathType: Prefix 33 | backend: 34 | service: 35 | name: foo1 36 | port: 37 | name: http 38 | - host: "test.haproxy" 39 | http: 40 | paths: 41 | - path: / 42 | pathType: Prefix 43 | backend: 44 | service: 45 | name: foo1 46 | port: 47 | name: http 48 | - host: "test2.haproxy" 49 | http: 50 | paths: 51 | - path: / 52 | pathType: Prefix 53 | backend: 54 | service: 55 | name: foo2 56 | port: 57 | name: http 58 | -------------------------------------------------------------------------------- /test/transform/data/ingress/expectation/no-duplicate.yaml: -------------------------------------------------------------------------------- 1 | kind: Ingress 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: testingress 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | tls: 9 | - hosts: 10 | - test1.haproxy 11 | secretName: test1 12 | - hosts: 13 | - test2.haproxy 14 | secretName: test2 15 | rules: 16 | - host: "test.haproxy" 17 | http: 18 | paths: 19 | - path: / 20 | pathType: Prefix 21 | backend: 22 | service: 23 | name: foo1 24 | port: 25 | name: http 26 | - host: "test2.haproxy" 27 | http: 28 | paths: 29 | - path: / 30 | pathType: Prefix 31 | backend: 32 | service: 33 | name: foo2 34 | port: 35 | name: http 36 | -------------------------------------------------------------------------------- /test/transform/data/ingress/no-duplicate.yaml: -------------------------------------------------------------------------------- 1 | kind: Ingress 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: testingress 5 | annotations: 6 | ingress.class: haproxy 7 | spec: 8 | tls: 9 | - hosts: 10 | - test1.haproxy 11 | secretName: test1 12 | - hosts: 13 | - test2.haproxy 14 | secretName: test2 15 | rules: 16 | - host: "test.haproxy" 17 | http: 18 | paths: 19 | - path: / 20 | pathType: Prefix 21 | backend: 22 | service: 23 | name: foo1 24 | port: 25 | name: http 26 | - host: "test2.haproxy" 27 | http: 28 | paths: 29 | - path: / 30 | pathType: Prefix 31 | backend: 32 | service: 33 | name: foo2 34 | port: 35 | name: http 36 | -------------------------------------------------------------------------------- /test/transform/data/rule/dupl-1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - host: "test.haproxy" 3 | http: 4 | paths: 5 | - path: / 6 | pathType: Prefix 7 | backend: 8 | service: 9 | name: foo1 10 | port: 11 | name: http 12 | - host: "test.haproxy" 13 | http: 14 | paths: 15 | - path: / 16 | pathType: Prefix 17 | backend: 18 | service: 19 | name: foo1 20 | port: 21 | name: http 22 | - host: "test2.haproxy" 23 | http: 24 | paths: 25 | - path: / 26 | pathType: Prefix 27 | backend: 28 | service: 29 | name: foo2 30 | port: 31 | name: http 32 | -------------------------------------------------------------------------------- /test/transform/data/rule/expectation/no-duplicate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - host: "test.haproxy" 3 | http: 4 | paths: 5 | - path: / 6 | pathType: Prefix 7 | backend: 8 | service: 9 | name: foo1 10 | port: 11 | name: http 12 | - host: "test2.haproxy" 13 | http: 14 | paths: 15 | - path: / 16 | pathType: Prefix 17 | backend: 18 | service: 19 | name: foo2 20 | port: 21 | name: http 22 | -------------------------------------------------------------------------------- /test/transform/data/rule/no-dupl-1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - host: "test.haproxy" 3 | http: 4 | paths: 5 | - path: / 6 | pathType: Prefix 7 | backend: 8 | service: 9 | name: foo1 10 | port: 11 | name: http 12 | - host: "test.haproxy" 13 | http: 14 | paths: 15 | - path: / 16 | pathType: Prefix 17 | backend: 18 | service: 19 | name: foo111 ### It's not the same rule, it should stay 20 | port: 21 | name: http 22 | - host: "test2.haproxy" 23 | http: 24 | paths: 25 | - path: / 26 | pathType: Prefix 27 | backend: 28 | service: 29 | name: foo2 30 | port: 31 | name: http 32 | -------------------------------------------------------------------------------- /test/transform/data/rule/no-dupl-2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - host: "test.haproxy" 3 | http: 4 | paths: 5 | - path: / 6 | pathType: Prefix 7 | backend: 8 | service: 9 | name: foo1 10 | port: 11 | name: http 12 | - host: "test.haproxy" 13 | http: 14 | paths: 15 | - path: / 16 | pathType: Prefix 17 | backend: 18 | service: 19 | name: foo1 20 | port: 21 | name: http 22 | - path: /foo12 23 | pathType: Prefix 24 | backend: 25 | service: 26 | name: foo12 27 | port: 28 | name: http 29 | - host: "test2.haproxy" 30 | http: 31 | paths: 32 | - path: / 33 | pathType: Prefix 34 | backend: 35 | service: 36 | name: foo2 37 | port: 38 | name: http 39 | -------------------------------------------------------------------------------- /test/transform/data/rule/no-duplicate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - host: "test.haproxy" 3 | http: 4 | paths: 5 | - path: / 6 | pathType: Prefix 7 | backend: 8 | service: 9 | name: foo1 10 | port: 11 | name: http 12 | - host: "test2.haproxy" 13 | http: 14 | paths: 15 | - path: / 16 | pathType: Prefix 17 | backend: 18 | service: 19 | name: foo2 20 | port: 21 | name: http 22 | -------------------------------------------------------------------------------- /test/transform/data/tls/dupl-both.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | - test1.haproxy 6 | - test1.haproxy 7 | - test1.haproxy 8 | secretName: test1 9 | - hosts: 10 | - test2.haproxy 11 | - test2.haproxy 12 | secretName: test2 13 | -------------------------------------------------------------------------------- /test/transform/data/tls/dupl-hosts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | - test1.haproxy 6 | - test1.haproxy 7 | - test1.haproxy 8 | secretName: test1 9 | - hosts: 10 | - test2.haproxy 11 | secretName: test2 12 | -------------------------------------------------------------------------------- /test/transform/data/tls/dupl-tls-2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | secretName: test1 6 | - hosts: 7 | - test1.haproxy 8 | - test111.haproxy 9 | secretName: test1 10 | - hosts: 11 | - test112.haproxy 12 | secretName: test1 13 | - hosts: 14 | - test112.haproxy 15 | - test1121.haproxy 16 | secretName: test1 17 | - hosts: 18 | - test1.haproxy 19 | secretName: test1 20 | - hosts: 21 | - test1.haproxy 22 | - test111.haproxy 23 | secretName: test1 24 | - hosts: 25 | - test2.haproxy 26 | secretName: test2 27 | - hosts: 28 | - test2.haproxy 29 | secretName: test2 30 | -------------------------------------------------------------------------------- /test/transform/data/tls/dupl-tls.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | secretName: test1 6 | - hosts: 7 | - test1.haproxy 8 | secretName: test1 9 | - hosts: 10 | - test1.haproxy 11 | secretName: test1 12 | - hosts: 13 | - test1.haproxy 14 | secretName: test1 15 | - hosts: 16 | - test2.haproxy 17 | secretName: test2 18 | - hosts: 19 | - test2.haproxy 20 | secretName: test2 21 | -------------------------------------------------------------------------------- /test/transform/data/tls/expectation/dupl-tls-2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | secretName: test1 6 | - hosts: 7 | - test1.haproxy 8 | - test111.haproxy 9 | secretName: test1 10 | - hosts: 11 | - test112.haproxy 12 | secretName: test1 13 | - hosts: 14 | - test112.haproxy 15 | - test1121.haproxy 16 | secretName: test1 17 | - hosts: 18 | - test2.haproxy 19 | secretName: test2 20 | -------------------------------------------------------------------------------- /test/transform/data/tls/expectation/no-duplicate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | secretName: test1 6 | - hosts: 7 | - test2.haproxy 8 | secretName: test2 9 | -------------------------------------------------------------------------------- /test/transform/data/tls/no-duplicate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # tls: 3 | - hosts: 4 | - test1.haproxy 5 | secretName: test1 6 | - hosts: 7 | - test2.haproxy 8 | secretName: test2 9 | --------------------------------------------------------------------------------