Skip to content

Commit

Permalink
feat: Engine Traefik Docker labels for REST API reverse proxy routing (
Browse files Browse the repository at this point in the history
…#2019)

## Description:
Create engine Traefik Docker labels for the REST API port so we can
interact with the API via Traefik. The Host header is set to "engine".

Tested with Docker locally:

```
$ curl -i http://localhost:9730/api/engine/info -H "Host: engine"
HTTP/1.1 200 OK
...
{"engine_version":"70d65c"}
```

## Is this change user facing?
NO

## References (if applicable):
#1970
  • Loading branch information
laurentluce authored Jan 8, 2024
1 parent ded589a commit 6541884
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const (
// be stored in the port spec label
KurtosisInternalContainerGrpcPortId = "grpc"

// The ID of the REST API port.
KurtosisInternalContainerRESTAPIPortId = "rest-api"

// The engine server uses gRPC so MUST listen on TCP (no other protocols are supported)
EngineTransportProtocol = port_spec.TransportProtocol_TCP

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ func CreateEngine(
engineGuid,
consts.KurtosisInternalContainerGrpcPortId,
privateGrpcPortSpec,
consts.KurtosisInternalContainerRESTAPIPortId,
restAPIPortSpec,
)
if err != nil {
return nil, stacktrace.Propagate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ func (provider *dockerEnclaveObjectAttributesProviderImpl) getLabelsForEnclaveOb
// "traefik.http.services.65d2fb6d6732-3771c85af16a-81.loadbalancer.server.port": "81"
func (provider *dockerEnclaveObjectAttributesProviderImpl) getTraefikLabelsForEnclaveObject(serviceUuid string, ports map[string]*port_spec.PortSpec) (map[*docker_label_key.DockerLabelKey]*docker_label_value.DockerLabelValue, error) {
labels := map[*docker_label_key.DockerLabelKey]*docker_label_value.DockerLabelValue{}
labelKeyValuePairs := map[string]string{}

for _, portSpec := range ports {
maybeApplicationProtocol := ""
Expand All @@ -552,59 +553,26 @@ func (provider *dockerEnclaveObjectAttributesProviderImpl) getTraefikLabelsForEn
shortServiceUuid := uuid_generator.ShortenedUUIDString(serviceUuid)
servicePortStr := fmt.Sprintf("%s-%s-%d", shortEnclaveUuid, shortServiceUuid, portSpec.GetNumber())

// Header Host rule
ruleKeySuffix := fmt.Sprintf("http.routers.%s.rule", servicePortStr)
ruleLabelKey, err := docker_label_key.CreateNewDockerTraefikLabelKey(ruleKeySuffix)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred getting the traefik rule label key with suffix '%v'", ruleKeySuffix)
}
ruleValue := fmt.Sprintf("Host(`%d-%s-%s`)", portSpec.GetNumber(), shortServiceUuid, shortEnclaveUuid)
ruleLabelValue, err := docker_label_value.CreateNewDockerLabelValue(ruleValue)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred creating the traefik rule label value with value '%v'", ruleValue)
}
labels[ruleLabelKey] = ruleLabelValue

// Service name
serviceKeySuffix := fmt.Sprintf("http.routers.%s.service", servicePortStr)
serviceLabelKey, err := docker_label_key.CreateNewDockerTraefikLabelKey(serviceKeySuffix)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred getting the traefik service label key with suffix '%v'", serviceKeySuffix)
}
serviceValue := servicePortStr
serviceLabelValue, err := docker_label_value.CreateNewDockerLabelValue(serviceValue)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred creating the traefik service label value with value '%v'", serviceValue)
}
labels[serviceLabelKey] = serviceLabelValue

// Service port number
portKeySuffix := fmt.Sprintf("http.services.%s.loadbalancer.server.port", servicePortStr)
portLabelKey, err := docker_label_key.CreateNewDockerTraefikLabelKey(portKeySuffix)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred getting the traefik port label key with suffix '%v'", portKeySuffix)
}
portValue := strconv.Itoa(int(portSpec.GetNumber()))
portLabelValue, err := docker_label_value.CreateNewDockerLabelValue(portValue)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred creating the traefik port label value with value '%v'", portValue)
}
labels[portLabelKey] = portLabelValue
labelKeyValuePairs[fmt.Sprintf("http.routers.%s.rule", servicePortStr)] = fmt.Sprintf("Host(`%d-%s-%s`)", portSpec.GetNumber(), shortServiceUuid, shortEnclaveUuid)
labelKeyValuePairs[fmt.Sprintf("http.routers.%s.service", servicePortStr)] = servicePortStr
labelKeyValuePairs[fmt.Sprintf("http.services.%s.loadbalancer.server.port", servicePortStr)] = strconv.Itoa(int(portSpec.GetNumber()))
}
}

if len(labels) > 0 {
// Enable Traefik for this service is there is at least one traefik label
traefikEnableLabelKey, err := docker_label_key.CreateNewDockerTraefikLabelKey("enable")
if len(labelKeyValuePairs) > 0 {
labelKeyValuePairs["enable"] = "true"
}

for key, value := range labelKeyValuePairs {
labelKey, err := docker_label_key.CreateNewDockerTraefikLabelKey(key)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred getting the traefik enable label key")
return nil, stacktrace.Propagate(err, "An error occurred getting the traefik label key with suffix '%v'", key)
}
traefikEnableValue := "true"
traefikEnableLabelValue, err := docker_label_value.CreateNewDockerLabelValue(traefikEnableValue)
labelValue, err := docker_label_value.CreateNewDockerLabelValue(value)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred creating the traefik enable label value with value '%v'", traefikEnableValue)
return nil, stacktrace.Propagate(err, "An error occurred creating the traefik label value with value '%v'", value)
}
labels[traefikEnableLabelKey] = traefikEnableLabelValue
labels[labelKey] = labelValue
}

return labels, nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package object_attributes_provider

import (
"fmt"
"strconv"
"strings"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/docker/object_attributes_provider/docker_label_key"
Expand All @@ -18,6 +20,7 @@ const (
engineServerNamePrefix = "kurtosis-engine"
logsAggregatorName = "kurtosis-logs-aggregator"
logsStorageVolumeName = "kurtosis-logs-storage"
engineRESTAPIPortStr = "engine-rest-api"
reverseProxyNamePrefix = "kurtosis-reverse-proxy"
)

Expand All @@ -26,6 +29,8 @@ type DockerObjectAttributesProvider interface {
guid engine.EngineGUID,
grpcPortId string,
grpcPortSpec *port_spec.PortSpec,
restAPIPortId string,
restAPIPortSpec *port_spec.PortSpec,
) (DockerObjectAttributes, error)
ForEnclave(enclaveUuid enclave.EnclaveUUID) (DockerEnclaveObjectAttributesProvider, error)
ForLogsAggregator() (DockerObjectAttributes, error)
Expand All @@ -48,6 +53,8 @@ func (provider *dockerObjectAttributesProviderImpl) ForEngineServer(
guid engine.EngineGUID,
grpcPortId string,
grpcPortSpec *port_spec.PortSpec,
restAPIPortId string,
restAPIPortSpec *port_spec.PortSpec,
) (DockerObjectAttributes, error) {

nameStr := strings.Join(
Expand Down Expand Up @@ -86,6 +93,14 @@ func (provider *dockerObjectAttributesProviderImpl) ForEngineServer(
docker_label_key.GUIDDockerLabelKey: guidLabelValue,
}

traefikLabels, err := provider.getTraefikLabelsForEngine(restAPIPortSpec)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred getting traefik labels for engine server")
}
for traefikLabelKey, traefikLabelValue := range traefikLabels {
labels[traefikLabelKey] = traefikLabelValue
}

objectAttributes, err := newDockerObjectAttributesImpl(name, labels)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred while creating the ObjectAttributesImpl with the name '%s' and labels '%+v'", name, labels)
Expand Down Expand Up @@ -173,3 +188,34 @@ func (provider *dockerObjectAttributesProviderImpl) ForReverseProxy(engineGuid e
}
return objectAttributes, nil
}

// Return Traefik labels
// Including the labels required to route traffic to the engine rest api port if the Host header is set to "engine".
//
// "traefik.enable": "true",
// "traefik.http.routers.engine-rest-api.rule": "Host(`engine`)",
// "traefik.http.routers.engine-rest-api.service": "engine-rest-api",
// "traefik.http.services.engine-rest-api.loadbalancer.server.port": "<engine rest api port number>"
func (provider *dockerObjectAttributesProviderImpl) getTraefikLabelsForEngine(restAPIPortSpec *port_spec.PortSpec) (map[*docker_label_key.DockerLabelKey]*docker_label_value.DockerLabelValue, error) {
labels := map[*docker_label_key.DockerLabelKey]*docker_label_value.DockerLabelValue{}
labelKeyValuePairs := map[string]string{
fmt.Sprintf("http.routers.%s.rule", engineRESTAPIPortStr): fmt.Sprintf("Host(`%s`)", engine.RESTAPIPortHostHeader),
fmt.Sprintf("http.routers.%s.service", engineRESTAPIPortStr): engineRESTAPIPortStr,
fmt.Sprintf("http.services.%s.loadbalancer.server.port", engineRESTAPIPortStr): strconv.Itoa(int(restAPIPortSpec.GetNumber())),
"enable": "true",
}

for key, value := range labelKeyValuePairs {
labelKey, err := docker_label_key.CreateNewDockerTraefikLabelKey(key)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred getting the traefik label key with suffix '%v'", key)
}
labelValue, err := docker_label_value.CreateNewDockerLabelValue(value)
if err != nil {
return nil, stacktrace.Propagate(err, "An error occurred creating the traefik label value with value '%v'", value)
}
labels[labelKey] = labelValue
}

return labels, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ const (
maxWaitForEngineContainerAvailabilityRetries = 30
timeBetweenWaitForEngineContainerAvailabilityRetries = 1 * time.Second
httpApplicationProtocol = "http"

restAPIPortHost = "engine"
)

var noWait *port_spec.Wait = nil
Expand Down Expand Up @@ -584,7 +582,7 @@ func getEngineIngressRules(
) ([]netv1.IngressRule, error) {
var ingressRules []netv1.IngressRule
ingressRule := netv1.IngressRule{
Host: restAPIPortHost,
Host: engine.RESTAPIPortHostHeader,
IngressRuleValue: netv1.IngressRuleValue{
HTTP: &netv1.HTTPIngressRuleValue{
Paths: []netv1.HTTPIngressPath{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package engine

const (
RESTAPIPortAddr uint16 = 9779
RESTAPIHostIP string = "0.0.0.0"
RESTAPIPortAddr uint16 = 9779
RESTAPIHostIP string = "0.0.0.0"
RESTAPIPortHostHeader string = "engine"
)

0 comments on commit 6541884

Please sign in to comment.