diff --git a/.chloggen/ingress.yaml b/.chloggen/ingress.yaml new file mode 100755 index 0000000000..ebcdc9be37 --- /dev/null +++ b/.chloggen/ingress.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) +component: collector + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: support for creating ingress for extensions that consumes the service. + +# One or more tracking issues related to the change +issues: [3438] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml index fc36f4deb5..56fddb0cbd 100644 --- a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml +++ b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml @@ -6042,6 +6042,49 @@ spec: x-kubernetes-map-type: atomic type: object type: array + extensionIngress: + properties: + annotations: + additionalProperties: + type: string + type: object + hostname: + type: string + ingressClassName: + type: string + route: + properties: + termination: + enum: + - insecure + - edge + - passthrough + - reencrypt + type: string + type: object + ruleType: + enum: + - path + - subdomain + type: string + tls: + items: + properties: + hosts: + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + type: string + type: object + type: array + type: + enum: + - ingress + - route + type: string + type: object hostNetwork: type: boolean image: diff --git a/internal/manifests/collector/ingress.go b/internal/manifests/collector/ingress.go index 4384cf8c3a..a647e17bfb 100644 --- a/internal/manifests/collector/ingress.go +++ b/internal/manifests/collector/ingress.go @@ -47,12 +47,14 @@ func Ingress(params manifests.Params) (*networkingv1.Ingress, error) { return nil, err } + otelcol := naming.Service(params.OtelCol.Name) + var rules []networkingv1.IngressRule switch params.OtelCol.Spec.Ingress.RuleType { case v1beta1.IngressRuleTypePath, "": - rules = []networkingv1.IngressRule{createPathIngressRules(params.OtelCol.Name, params.OtelCol.Spec.Ingress.Hostname, ports, "service")} + rules = []networkingv1.IngressRule{createPathIngressRules(otelcol, params.OtelCol.Spec.Ingress.Hostname, ports)} case v1beta1.IngressRuleTypeSubdomain: - rules = createSubdomainIngressRules(params.OtelCol.Name, params.OtelCol.Spec.Ingress.Hostname, ports, "service") + rules = createSubdomainIngressRules(otelcol, params.OtelCol.Spec.Ingress.Hostname, ports) } return &networkingv1.Ingress{ @@ -83,12 +85,14 @@ func ExtensionIngress(params manifests.Params) (*networkingv1.Ingress, error) { return nil, err } + otelcol := naming.ExtensionService(params.OtelCol.Name) + var rules []networkingv1.IngressRule switch params.OtelCol.Spec.Ingress.RuleType { case v1beta1.IngressRuleTypePath, "": - rules = []networkingv1.IngressRule{createPathIngressRules(params.OtelCol.Name, params.OtelCol.Spec.ExtensionIngress.Hostname, ports, "extension")} + rules = []networkingv1.IngressRule{createPathIngressRules(otelcol, params.OtelCol.Spec.ExtensionIngress.Hostname, ports)} case v1beta1.IngressRuleTypeSubdomain: - rules = createSubdomainIngressRules(params.OtelCol.Name, params.OtelCol.Spec.ExtensionIngress.Hostname, ports, "extension") + rules = createSubdomainIngressRules(otelcol, params.OtelCol.Spec.ExtensionIngress.Hostname, ports) } return &networkingv1.Ingress{ @@ -106,17 +110,10 @@ func ExtensionIngress(params manifests.Params) (*networkingv1.Ingress, error) { }, nil } -func createPathIngressRules(otelcol string, hostname string, ports []corev1.ServicePort, serviceType string) networkingv1.IngressRule { +func createPathIngressRules(otelcol string, hostname string, ports []corev1.ServicePort) networkingv1.IngressRule { pathType := networkingv1.PathTypePrefix paths := make([]networkingv1.HTTPIngressPath, len(ports)) - var name string - if serviceType == "extension" { - name = naming.ExtensionService(otelcol) - } else { - name = naming.Service(otelcol) - } - for i, port := range ports { portName := naming.PortName(port.Name, port.Port) paths[i] = networkingv1.HTTPIngressPath{ @@ -124,7 +121,7 @@ func createPathIngressRules(otelcol string, hostname string, ports []corev1.Serv PathType: &pathType, Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ - Name: name, + Name: otelcol, Port: networkingv1.ServiceBackendPort{ Name: portName, }, @@ -142,17 +139,10 @@ func createPathIngressRules(otelcol string, hostname string, ports []corev1.Serv } } -func createSubdomainIngressRules(otelcol string, hostname string, ports []corev1.ServicePort, serviceType string) []networkingv1.IngressRule { +func createSubdomainIngressRules(otelcol string, hostname string, ports []corev1.ServicePort) []networkingv1.IngressRule { var rules []networkingv1.IngressRule pathType := networkingv1.PathTypePrefix - var name string - if serviceType == "extension" { - name = naming.ExtensionService(otelcol) - } else { - name = naming.Service(otelcol) - } - for _, port := range ports { portName := naming.PortName(port.Name, port.Port) @@ -171,7 +161,7 @@ func createSubdomainIngressRules(otelcol string, hostname string, ports []corev1 PathType: &pathType, Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ - Name: name, + Name: otelcol, Port: networkingv1.ServiceBackendPort{ Name: portName, }, diff --git a/internal/manifests/collector/ingress_test.go b/internal/manifests/collector/ingress_test.go index 40e736b7bb..dc4b82c9b4 100644 --- a/internal/manifests/collector/ingress_test.go +++ b/internal/manifests/collector/ingress_test.go @@ -284,3 +284,229 @@ func TestDesiredIngresses(t *testing.T) { }, got) }) } + +func TestExtensionIngress(t *testing.T) { + t.Run("no ingress for incorrect ingress type", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1beta1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.OpenTelemetryCollectorSpec{ + ExtensionIngress: v1beta1.Ingress{ + Type: v1beta1.IngressType("unknown"), + }, + }, + }, + } + actual, err := ExtensionIngress(params) + assert.Nil(t, actual) + assert.NoError(t, err) + }) + t.Run("no ingress if there's no port for extension", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1beta1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.OpenTelemetryCollectorSpec{ + Config: v1beta1.Config{ + Service: v1beta1.Service{ + Extensions: []string{"jaeger_query"}, + }, + Extensions: &v1beta1.AnyConfig{ + Object: map[string]interface{}{}, + }, + }, + ExtensionIngress: v1beta1.Ingress{ + Type: v1beta1.IngressType("ingress"), + }, + }, + }, + } + + actual, err := ExtensionIngress(params) + assert.Nil(t, actual) + assert.NoError(t, err) + }) + t.Run("ingress for extensions for rule type path", func(t *testing.T) { + var ( + ns = "test-ns" + hostname = "example.com" + ingressClassName = "nginx" + pathType = networkingv1.PathTypePrefix + ) + + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1beta1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: ns, + }, + Spec: v1beta1.OpenTelemetryCollectorSpec{ + Config: v1beta1.Config{ + Service: v1beta1.Service{ + Extensions: []string{"jaeger_query"}, + }, + Extensions: &v1beta1.AnyConfig{ + Object: map[string]interface{}{ + "jaeger_query": map[string]interface{}{ + "http": map[string]interface{}{ + "endpoint": "0.0.0.0:16686", + }, + }, + }, + }, + }, + ExtensionIngress: v1beta1.Ingress{ + Type: v1beta1.IngressType("ingress"), + IngressClassName: &ingressClassName, + Hostname: hostname, + Annotations: map[string]string{"some.key": "some.value"}, + RuleType: v1beta1.IngressRuleTypePath, + }, + }, + }, + } + + actual, err := ExtensionIngress(params) + assert.NoError(t, err) + assert.NotNil(t, actual) + assert.NotEqual(t, networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.ExtensionIngress(params.OtelCol.Name), + Namespace: ns, + Annotations: params.OtelCol.Spec.ExtensionIngress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.ExtensionIngress(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + }, + Spec: networkingv1.IngressSpec{ + IngressClassName: &ingressClassName, + Rules: []networkingv1.IngressRule{ + { + Host: hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/jaeger-query", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: naming.ExtensionService(params.OtelCol.Name), + Port: networkingv1.ServiceBackendPort{ + Name: "jaeger-query", + Number: 16686, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, actual) + }) + t.Run("ingress for extensions for rule type subdomain", func(t *testing.T) { + var ( + ns = "test-ns" + hostname = "example.com" + ingressClassName = "nginx" + pathType = networkingv1.PathTypePrefix + ) + + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1beta1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: ns, + }, + Spec: v1beta1.OpenTelemetryCollectorSpec{ + Config: v1beta1.Config{ + Service: v1beta1.Service{ + Extensions: []string{"jaeger_query"}, + }, + Extensions: &v1beta1.AnyConfig{ + Object: map[string]interface{}{ + "jaeger_query": map[string]interface{}{ + "http": map[string]interface{}{ + "endpoint": "0.0.0.0:16686", + }, + }, + }, + }, + }, + ExtensionIngress: v1beta1.Ingress{ + Type: v1beta1.IngressType("ingress"), + IngressClassName: &ingressClassName, + Hostname: hostname, + Annotations: map[string]string{"some.key": "some.value"}, + RuleType: v1beta1.IngressRuleTypeSubdomain, + }, + }, + }, + } + + actual, err := ExtensionIngress(params) + assert.NoError(t, err) + assert.NotNil(t, actual) + assert.NotEqual(t, networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.ExtensionIngress(params.OtelCol.Name), + Namespace: ns, + Annotations: params.OtelCol.Spec.ExtensionIngress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.ExtensionIngress(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + }, + Spec: networkingv1.IngressSpec{ + IngressClassName: &ingressClassName, + Rules: []networkingv1.IngressRule{ + { + Host: "jaeger-query." + hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: naming.ExtensionService(params.OtelCol.Name), + Port: networkingv1.ServiceBackendPort{ + Name: "jaeger-query", + Number: 16686, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, actual) + }) +}