Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions config/core/300-resources/route.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ spec:
description: TrafficTarget holds a single entry of the routing table for a Route.
type: object
properties:
appendHeaders:
description: |-
AppendHeaders allow specifying additional HTTP headers to add
before forwarding a request to the destination service.
type: object
additionalProperties:
type: string
configurationName:
description: |-
ConfigurationName of a configuration to whose latest revision we will send
Expand Down Expand Up @@ -219,6 +226,13 @@ spec:
description: TrafficTarget holds a single entry of the routing table for a Route.
type: object
properties:
appendHeaders:
description: |-
AppendHeaders allow specifying additional HTTP headers to add
before forwarding a request to the destination service.
type: object
additionalProperties:
type: string
configurationName:
description: |-
ConfigurationName of a configuration to whose latest revision we will send
Expand Down
14 changes: 14 additions & 0 deletions config/core/300-resources/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,13 @@ spec:
description: TrafficTarget holds a single entry of the routing table for a Route.
type: object
properties:
appendHeaders:
description: |-
AppendHeaders allow specifying additional HTTP headers to add
before forwarding a request to the destination service.
type: object
additionalProperties:
type: string
configurationName:
description: |-
ConfigurationName of a configuration to whose latest revision we will send
Expand Down Expand Up @@ -1656,6 +1663,13 @@ spec:
description: TrafficTarget holds a single entry of the routing table for a Route.
type: object
properties:
appendHeaders:
description: |-
AppendHeaders allow specifying additional HTTP headers to add
before forwarding a request to the destination service.
type: object
additionalProperties:
type: string
configurationName:
description: |-
ConfigurationName of a configuration to whose latest revision we will send
Expand Down
13 changes: 13 additions & 0 deletions docs/serving-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2082,6 +2082,19 @@ status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and
a hostname, but may not contain anything else (e.g. basic auth, url path, etc.)</p>
</td>
</tr>
<tr>
<td>
<code>appendHeaders</code><br/>
<em>
map[string]string
</em>
</td>
<td>
<em>(Optional)</em>
<p>AppendHeaders allow specifying additional HTTP headers to add
before forwarding a request to the destination service.</p>
</td>
</tr>
</tbody>
</table>
<hr/>
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/serving/v1/route_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ type TrafficTarget struct {
// a hostname, but may not contain anything else (e.g. basic auth, url path, etc.)
// +optional
URL *apis.URL `json:"url,omitempty"`

// AppendHeaders allow specifying additional HTTP headers to add
// before forwarding a request to the destination service.
//
// +optional
AppendHeaders map[string]string `json:"appendHeaders,omitempty"`
}

// RouteSpec holds the desired state of the Route (from the client).
Expand Down
21 changes: 20 additions & 1 deletion pkg/apis/serving/v1/route_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ func (tt *TrafficTarget) Validate(ctx context.Context) *apis.FieldError {
errs := tt.validateLatestRevision(ctx)
errs = tt.validateRevisionAndConfiguration(ctx, errs)
errs = tt.validateTrafficPercentage(errs)
return tt.validateURL(ctx, errs)
errs = tt.validateURL(ctx, errs)
return tt.validateAppendHeaders(ctx, errs)
}

func (tt *TrafficTarget) validateRevisionAndConfiguration(ctx context.Context, errs *apis.FieldError) *apis.FieldError {
Expand Down Expand Up @@ -191,6 +192,24 @@ func (tt *TrafficTarget) validateURL(ctx context.Context, errs *apis.FieldError)
return errs
}

func (tt *TrafficTarget) validateAppendHeaders(ctx context.Context, errs *apis.FieldError) *apis.FieldError {
if len(tt.AppendHeaders) == 0 {
return errs
}

// AppendHeaders is not allowed in status.
if apis.IsInStatus(ctx) {
return errs.Also(apis.ErrDisallowedFields("appendHeaders"))
}

for key := range tt.AppendHeaders {
if len(validation.IsHTTPHeaderName(key)) > 0 {
errs = errs.Also(apis.ErrInvalidKeyName(key, "appendHeaders"))
}
}
return errs
}

// validateLabels function validates route labels.
func (r *Route) validateLabels() (errs *apis.FieldError) {
if val, ok := r.Labels[serving.ServiceLabelKey]; ok {
Expand Down
32 changes: 32 additions & 0 deletions pkg/apis/serving/v1/route_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,38 @@ func TestTrafficTargetValidation(t *testing.T) {
},
wc: apis.WithinSpec,
want: apis.ErrDisallowedFields("url"),
}, {
name: "valid append headers",
tt: &TrafficTarget{
ConfigurationName: "foo",
Percent: ptr.Int64(100),
AppendHeaders: map[string]string{
"foo": "bar",
},
},
wc: apis.WithinSpec,
}, {
name: "invalid append headers (status)",
tt: &TrafficTarget{
RevisionName: "v1",
Percent: ptr.Int64(100),
AppendHeaders: map[string]string{
"foo": "bar",
},
},
wc: apis.WithinStatus,
want: apis.ErrDisallowedFields("appendHeaders"),
}, {
name: "invalid append headers (invalid key)",
tt: &TrafficTarget{
ConfigurationName: "foo",
Percent: ptr.Int64(100),
AppendHeaders: map[string]string{
"foo/bar": "bar",
},
},
wc: apis.WithinSpec,
want: apis.ErrInvalidKeyName("foo/bar", "appendHeaders"),
}}

for _, test := range tests {
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/serving/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions pkg/reconciler/route/resources/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,10 @@ func makeBaseIngressPath(ns string, targets traffic.RevisionTargets,
ServicePort: servicePort,
},
Percent: int(*t.Percent),
AppendHeaders: map[string]string{
AppendHeaders: kmeta.UnionMaps(map[string]string{
activator.RevisionHeaderName: t.TrafficTarget.RevisionName,
activator.RevisionHeaderNamespace: ns,
},
}, t.TrafficTarget.AppendHeaders),
})
} else {
for i := range cfg.Revisions {
Expand All @@ -334,15 +334,13 @@ func makeBaseIngressPath(ns string, targets traffic.RevisionTargets,
IngressBackend: netv1alpha1.IngressBackend{
ServiceNamespace: ns,
ServiceName: rev.RevisionName,
// Port on the public service must match port on the activator.
// Otherwise, the serverless services can't guarantee seamless positive handoff.
ServicePort: servicePort,
ServicePort: servicePort,
},
Percent: rev.Percent,
AppendHeaders: map[string]string{
AppendHeaders: kmeta.UnionMaps(map[string]string{
activator.RevisionHeaderName: rev.RevisionName,
activator.RevisionHeaderNamespace: ns,
},
}, t.TrafficTarget.AppendHeaders),
})
}
}
Expand Down
76 changes: 76 additions & 0 deletions pkg/reconciler/route/resources/ingress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,82 @@ func TestMakeIngressSpecCorrectRuleVisibility(t *testing.T) {
}
}

func TestMakeIngressSpecWithAppendHeaders(t *testing.T) {
targets := map[string]traffic.RevisionTargets{
traffic.DefaultTarget: {{
TrafficTarget: v1.TrafficTarget{
ConfigurationName: "config",
RevisionName: "v2",
Percent: ptr.Int64(100),
AppendHeaders: map[string]string{
"Foo": "Bar",
},
},
}},
}

r := Route(ns, "test-route", WithURL)

expected := []netv1alpha1.IngressRule{{
Hosts: []string{
"test-route." + ns,
"test-route." + ns + ".svc",
pkgnet.GetServiceHostname("test-route", ns),
},
HTTP: &netv1alpha1.HTTPIngressRuleValue{
Paths: []netv1alpha1.HTTPIngressPath{{
Splits: []netv1alpha1.IngressBackendSplit{{
IngressBackend: netv1alpha1.IngressBackend{
ServiceNamespace: ns,
ServiceName: "v2",
ServicePort: intstr.FromInt(80),
},
Percent: 100,
AppendHeaders: map[string]string{
"Knative-Serving-Revision": "v2",
"Knative-Serving-Namespace": ns,
"Foo": "Bar",
},
}},
}},
},
Visibility: netv1alpha1.IngressVisibilityClusterLocal,
}, {
Hosts: []string{
"test-route." + ns + ".example.com",
},
HTTP: &netv1alpha1.HTTPIngressRuleValue{
Paths: []netv1alpha1.HTTPIngressPath{{
Splits: []netv1alpha1.IngressBackendSplit{{
IngressBackend: netv1alpha1.IngressBackend{
ServiceNamespace: ns,
ServiceName: "v2",
ServicePort: intstr.FromInt(80),
},
Percent: 100,
AppendHeaders: map[string]string{
"Knative-Serving-Revision": "v2",
"Knative-Serving-Namespace": ns,
"Foo": "Bar",
},
}},
}},
},
Visibility: netv1alpha1.IngressVisibilityExternalIP,
}}

tc := &traffic.Config{Targets: targets}
ro := tc.BuildRollout()
ci, err := makeIngressSpec(testContext(), r, nil /*tls*/, tc, ro)
if err != nil {
t.Error("Unexpected error", err)
}

if !cmp.Equal(expected, ci.Rules) {
t.Error("Unexpected rules (-want, +got):", cmp.Diff(expected, ci.Rules))
}
}

func TestMakeIngressSpecCorrectRulesWithTagBasedRouting(t *testing.T) {
targets := map[string]traffic.RevisionTargets{
traffic.DefaultTarget: {{
Expand Down
Loading