diff --git a/cmd/activator/main.go b/cmd/activator/main.go index 099533a2a3f1..8a8c164c577e 100644 --- a/cmd/activator/main.go +++ b/cmd/activator/main.go @@ -184,14 +184,22 @@ func main() { logger.Fatalw("Failed to start configuration manager", zap.Error(err)) } - srv := h2c.NewServer(":8080", ah) + http1Srv := h2c.NewServer(":8080", ah) go func() { - if err := srv.ListenAndServe(); err != nil { + if err := http1Srv.ListenAndServe(); err != nil { + logger.Errorw("Error running HTTP server", zap.Error(err)) + } + }() + + h2cSrv := h2c.NewServer(":8081", ah) + go func() { + if err := h2cSrv.ListenAndServe(); err != nil { logger.Errorw("Error running HTTP server", zap.Error(err)) } }() <-stopCh a.Shutdown() - srv.Shutdown(context.TODO()) + http1Srv.Shutdown(context.TODO()) + h2cSrv.Shutdown(context.TODO()) } diff --git a/cmd/queue/main.go b/cmd/queue/main.go index 22761599e59a..3ab290fe448c 100644 --- a/cmd/queue/main.go +++ b/cmd/queue/main.go @@ -189,6 +189,7 @@ func handler(w http.ResponseWriter, r *http.Request) { } else { proxy.ServeHTTP(w, r) } + } // Sets up /health and /quitquitquit endpoints. diff --git a/config/400-activator-service.yaml b/config/400-activator-service.yaml index 95614ad5559e..c928bdeb06a4 100644 --- a/config/400-activator-service.yaml +++ b/config/400-activator-service.yaml @@ -29,6 +29,10 @@ spec: port: 80 targetPort: 8080 nodePort: # empty removing existing value to avoid upgrade error # TODO delete after v0.4 + - name: http2 + protocol: TCP + port: 81 + targetPort: 8081 - name: metrics protocol: TCP port: 9090 diff --git a/config/activator.yaml b/config/activator.yaml index a858d1d51916..7f4281affd16 100644 --- a/config/activator.yaml +++ b/config/activator.yaml @@ -41,8 +41,10 @@ spec: # and substituted here. image: github.com/knative/serving/cmd/activator ports: - - name: activator-port + - name: http1-port containerPort: 8080 + - name: h2c-port + containerPort: 8081 - name: metrics-port containerPort: 9090 args: diff --git a/pkg/activator/activator.go b/pkg/activator/activator.go index 12c401de3481..f24a69108aea 100644 --- a/pkg/activator/activator.go +++ b/pkg/activator/activator.go @@ -16,6 +16,8 @@ limitations under the License. package activator +import "github.com/knative/serving/pkg/apis/serving/v1alpha1" + const ( // K8sServiceName is the name of the activator service K8sServiceName = "activator-service" @@ -25,6 +27,11 @@ const ( RevisionHeaderName string = "knative-serving-revision" // RevisionHeaderNamespace is the header key for revision's namespace RevisionHeaderNamespace string = "knative-serving-namespace" + + // ServicePortHTTP1 is the port number for activating HTTP1 revisions + ServicePortHTTP1 int32 = 80 + // ServicePortHTTP1 is the port number for activating H2C revisions + ServicePortH2C int32 = 81 ) // Activator provides an active endpoint for a revision or an error and @@ -53,3 +60,13 @@ type ActivationResult struct { ConfigurationName string Error error } + +// ServicePort returns the activator service port for the given Revision protocol. +// Default is `ServicePortHTTP1`. +func ServicePort(protocol v1alpha1.RevisionProtocolType) int32 { + if protocol == v1alpha1.RevisionProtocolH2C { + return ServicePortH2C + } + + return ServicePortHTTP1 +} diff --git a/pkg/activator/activator_test.go b/pkg/activator/activator_test.go new file mode 100644 index 000000000000..d15da7e6b367 --- /dev/null +++ b/pkg/activator/activator_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package activator + +import ( + "testing" + + "github.com/knative/serving/pkg/apis/serving/v1alpha1" +) + +func TestServicePort(t *testing.T) { + tests := []struct { + name string + protocol v1alpha1.RevisionProtocolType + port int32 + }{{ + name: "h2c", + protocol: v1alpha1.RevisionProtocolH2C, + port: ServicePortH2C, + }, { + name: "http1", + protocol: v1alpha1.RevisionProtocolHTTP1, + port: ServicePortHTTP1, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + want := tt.port + got := ServicePort(tt.protocol) + + if want != got { + t.Errorf("unexpected port. want %d, got %d", want, got) + } + }) + } +} diff --git a/pkg/activator/handler/enforce_length_handler.go b/pkg/activator/handler/enforce_length_handler.go index b4b2da84bae3..6ef6156963c4 100644 --- a/pkg/activator/handler/enforce_length_handler.go +++ b/pkg/activator/handler/enforce_length_handler.go @@ -15,6 +15,8 @@ package handler import ( "net/http" + + "github.com/knative/serving/pkg/activator/util" ) // EnforceMaxContentLengthHandler prevents uploads larger than `MaxContentLengthBytes` @@ -24,10 +26,14 @@ type EnforceMaxContentLengthHandler struct { } func (h *EnforceMaxContentLengthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // If we have a ContentLength, we can fail early if r.ContentLength > h.MaxContentLengthBytes { w.WriteHeader(http.StatusRequestEntityTooLarge) return } + // Enforce MaxContentLengthBytes + r.Body = util.LimitReadCloser(r.Body, h.MaxContentLengthBytes) + h.NextHandler.ServeHTTP(w, r) } diff --git a/pkg/activator/handler/enforce_length_handler_test.go b/pkg/activator/handler/enforce_length_handler_test.go index b579bc3a54b9..166b04f0f8e3 100644 --- a/pkg/activator/handler/enforce_length_handler_test.go +++ b/pkg/activator/handler/enforce_length_handler_test.go @@ -13,51 +13,104 @@ limitations under the License. package handler import ( - "bytes" + "io" + "io/ioutil" "net/http" "net/http/httptest" + "strings" "testing" ) func TestEnforceMaxContentLengthHandler(t *testing.T) { payload := "SAMPLE PAYLOAD" + // httptest.NewRequest will set ContentLength for strings.NewReader + fixed := func(p string) io.Reader { return strings.NewReader(p) } + stream := func(p string) io.Reader { return ioutil.NopCloser(strings.NewReader(p)) } + examples := []struct { label string maxUpload int + request io.Reader + response string status int }{ { - label: "under", + label: "under with ContentLength", maxUpload: len(payload) + 1, + request: fixed(payload), status: http.StatusOK, + response: payload, }, { - label: "equal", + label: "equal with ContentLength", maxUpload: len(payload), + request: fixed(payload), status: http.StatusOK, + response: payload, }, { - label: "over", + label: "over with ContentLength", maxUpload: len(payload) - 1, + request: fixed(payload), status: http.StatusRequestEntityTooLarge, + response: "", + }, + { + label: "under without ContentLength", + maxUpload: len(payload) + 1, + request: stream(payload), + status: http.StatusOK, + response: payload, + }, + { + label: "equal without ContentLength", + maxUpload: len(payload), + request: stream(payload), + status: http.StatusOK, + response: payload, + }, + { + label: "over without ContentLength", + maxUpload: len(payload) - 1, + request: stream(payload), + status: http.StatusOK, + response: payload[0 : len(payload)-1], }, } for _, e := range examples { t.Run(e.label, func(t *testing.T) { + // Return 200 and request body baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) + + if r.Body != nil { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("Error reading request body: %v", err) + } + w.Write(body) + } }) handler := EnforceMaxContentLengthHandler{NextHandler: baseHandler, MaxContentLengthBytes: int64(e.maxUpload)} resp := httptest.NewRecorder() - req := httptest.NewRequest("POST", "http://example.com", bytes.NewBufferString(payload)) + req := httptest.NewRequest("POST", "http://example.com", e.request) handler.ServeHTTP(resp, req) + gotBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Error reading response body: %v", err) + } + + if e.response != string(gotBody) { + t.Errorf("Unexpected response body. Want %q, got %q", e.response, gotBody) + } + if resp.Code != e.status { - t.Errorf("Unexpected response status for payload %q. Want %d, got %d", payload, e.status, resp.Code) + t.Errorf("Unexpected response status. Want %d, got %d", e.status, resp.Code) } }) } diff --git a/pkg/activator/revision.go b/pkg/activator/revision.go index 9bfa33e9e7c4..db5e5ecdecec 100644 --- a/pkg/activator/revision.go +++ b/pkg/activator/revision.go @@ -123,7 +123,7 @@ func (r *revisionActivator) revisionEndpoint(revision *v1alpha1.Revision) (end E // Search for the correct port in all the service ports. port := int32(-1) for _, p := range svc.Spec.Ports { - if p.Name == revisionresources.ServicePortName { + if p.Name == revisionresources.ServicePortName(revision) { port = p.Port break } diff --git a/pkg/activator/revision_test.go b/pkg/activator/revision_test.go index 5ce78afb2475..1f8c0d908743 100644 --- a/pkg/activator/revision_test.go +++ b/pkg/activator/revision_test.go @@ -289,6 +289,7 @@ func newRevisionBuilder(labels map[string]string) *revisionBuilder { Spec: v1alpha1.RevisionSpec{ Container: corev1.Container{ Image: "gcr.io/repo/image", + Ports: []corev1.ContainerPort{{Name: "h2c"}}, }, }, Status: v1alpha1.RevisionStatus{ @@ -335,7 +336,7 @@ func newServiceBuilder() *serviceBuilder { }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{{ - Name: revisionresources.ServicePortName, + Name: revisionresources.ServicePortNameH2C, Port: v1alpha1.DefaultUserPort, }, { Name: "anotherport", diff --git a/pkg/activator/util/io.go b/pkg/activator/util/io.go new file mode 100644 index 000000000000..d0c87b858f2c --- /dev/null +++ b/pkg/activator/util/io.go @@ -0,0 +1,34 @@ +/* +Copyright 2018 The Knative Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import "io" + +// LimitReadCloser returns a ReadCloser wrapped with an `io.LimitReader` +func LimitReadCloser(rc io.ReadCloser, l int64) io.ReadCloser { + return &readCloser{io.LimitReader(rc, l), rc} +} + +type readCloser struct { + Reader io.Reader + Closer io.Closer +} + +func (lrc *readCloser) Read(b []byte) (int, error) { + return lrc.Reader.Read(b) +} + +func (lrc *readCloser) Close() error { + return lrc.Closer.Close() +} diff --git a/pkg/activator/util/io_test.go b/pkg/activator/util/io_test.go index 6c4ed61b1568..a54d056d961b 100644 --- a/pkg/activator/util/io_test.go +++ b/pkg/activator/util/io_test.go @@ -12,10 +12,15 @@ limitations under the License. */ package util -import "io" +import ( + "io" + "io/ioutil" + "strings" + "testing" +) type spyReadCloser struct { - io.Reader + io.ReadCloser Closed bool ReadAfterClose bool } @@ -25,11 +30,34 @@ func (s *spyReadCloser) Read(b []byte) (n int, err error) { s.ReadAfterClose = true } - return s.Reader.Read(b) + return s.ReadCloser.Read(b) } func (s *spyReadCloser) Close() error { s.Closed = true - return nil + return s.ReadCloser.Close() +} + +func TestLimitReadCloser(t *testing.T) { + want := "test" + r := strings.NewReader(want + " foo") + rc := &spyReadCloser{ReadCloser: ioutil.NopCloser(r)} + + lrc := LimitReadCloser(rc, int64(len(want))) + + got, err := ioutil.ReadAll(lrc) + lrc.Close() + + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if string(got) != want { + t.Fatalf("Unexpected body. Want %q, got %q", want, got) + } + + if !rc.Closed { + t.Fatalf("Expected ReadCloser to be closed.") + } } diff --git a/pkg/activator/util/rewinder.go b/pkg/activator/util/rewinder.go index 243e17a23337..723c4372cb95 100644 --- a/pkg/activator/util/rewinder.go +++ b/pkg/activator/util/rewinder.go @@ -16,44 +16,65 @@ package util import ( "bytes" "io" - "io/ioutil" "sync" ) type rewinder struct { sync.Mutex - rc io.ReadCloser - rs io.ReadSeeker + original io.ReadCloser + current io.Reader + next io.ReadWriter + eof bool } -// rewinder wraps a single-use `ReadCloser` into a `ReadCloser` that can be read multiple times +// NewRewinder wraps a single-use `ReadCloser` into a `ReadCloser` +// that can be read multiple times func NewRewinder(rc io.ReadCloser) io.ReadCloser { - return &rewinder{rc: rc} + r := &rewinder{original: rc} + + r.next = new(bytes.Buffer) + r.current = r.original + + return r } func (r *rewinder) Read(b []byte) (int, error) { r.Lock() defer r.Unlock() - // On the first `Read()`, the contents of `rc` is read into a buffer `rs`. - // This buffer is used for all subsequent reads - if r.rs == nil { - buf, err := ioutil.ReadAll(r.rc) - if err != nil { - return 0, err - } - r.rc.Close() - r.rs = bytes.NewReader(buf) + // Buffer everything we read + n, err := io.TeeReader(r.current, r.next).Read(b) + + // Keep track of when we reach the end + if err == io.EOF { + r.eof = true } - return r.rs.Read(b) + return n, err } func (r *rewinder) Close() error { r.Lock() defer r.Unlock() - // Rewind the buffer on `Close()` for the next call to `Read` - r.rs.Seek(0, io.SeekStart) + + // Start = what we've read already + what we haven't read yet + start := io.MultiReader(r.next, r.current) + + // Close the original ReadCloser if we have read it to the end + if r.eof && r.original != nil { + + if r.current == r.original { + // Start = only what we've read already + start = r.next + } + + r.original.Close() + r.original = nil + } + + // Rewind back to the start + r.next = new(bytes.Buffer) + r.current = start return nil } diff --git a/pkg/activator/util/rewinder_test.go b/pkg/activator/util/rewinder_test.go index 22a4c52c5016..7fe46302b4fc 100644 --- a/pkg/activator/util/rewinder_test.go +++ b/pkg/activator/util/rewinder_test.go @@ -13,37 +13,93 @@ limitations under the License. package util import ( - "bytes" + "bufio" + "io" "io/ioutil" + "strings" "testing" ) func TestRewinder(t *testing.T) { - str := "test string" - rc := &spyReadCloser{Reader: bytes.NewBufferString(str)} - rewinder := NewRewinder(rc) - - b1, err := ioutil.ReadAll(rewinder) - if err != nil { - t.Errorf("Unexpected error reading b1: %v", err) + expectStr := func(want, got string, err error) { + if err != nil { + t.Fatalf("Expected to read string, got error: %v", err) + } + if want != got { + t.Errorf("Unexpected string, want %q, got %q", want, got) + } } + + cont := make(chan bool) + rp, wp := io.Pipe() // Note: wp.Write() blocks until rp.Read() + + rewinder := NewRewinder(rp) + out := bufio.NewReader(rewinder) + + go func() { + t.Log("Writing chunk #1") + wp.Write([]byte("s1:")) + + t.Log("Writing chunk #2") + wp.Write([]byte("s2:")) + + t.Log("Rewinding") + rewinder.Close() + + cont <- true + + t.Log("Writing chunk #3") + wp.Write([]byte("s3:")) + + t.Log("Closing stream") + wp.Close() + }() + + got, err := out.ReadString(byte(':')) + t.Log("Checking chunk #1") + expectStr("s1:", got, err) + + got, err = out.ReadString(byte(':')) + t.Log("Checking chunk #2") + expectStr("s2:", got, err) + + <-cont + + gotAll, err := ioutil.ReadAll(out) + t.Log("Checking chunks #1-3") + expectStr("s1:s2:s3:", string(gotAll), err) + + t.Log("Rewinding again") rewinder.Close() - b2, err := ioutil.ReadAll(rewinder) + gotAll, err = ioutil.ReadAll(out) + t.Log("Checking chunks #1-3 again") + expectStr("s1:s2:s3:", string(gotAll), err) +} + +func TestRewinder_Cleanup(t *testing.T) { + want := "foo" + + in := &spyReadCloser{ReadCloser: ioutil.NopCloser(strings.NewReader(want))} + rewinder := NewRewinder(in) + + got, err := ioutil.ReadAll(rewinder) if err != nil { - t.Errorf("Unexpected error reading b2: %v", err) + t.Fatalf("Expected to read string, got error: %v", err) + } + if want != string(got) { + t.Errorf("Unexpected string, want %q, got %q", want, got) } + rewinder.Close() - if string(b1) != str { - t.Errorf("Unexpected str b1. Want %q, got %q", str, b1) + if !in.Closed { + t.Error("Input ReadCloser not closed") } - if string(b2) != str { - t.Errorf("Unexpected str b2. Want %q, got %q", str, b2) - } + ioutil.ReadAll(rewinder) - if !rc.Closed { - t.Error("Expected ReadCloser to be closed") + if in.ReadAfterClose { + t.Error("Read() after Close() in input ReadCloser") } } diff --git a/pkg/activator/util/transports_test.go b/pkg/activator/util/transports_test.go index 7370e9998839..5bfc737bf5d2 100644 --- a/pkg/activator/util/transports_test.go +++ b/pkg/activator/util/transports_test.go @@ -78,7 +78,7 @@ func TestRetryRoundTripper(t *testing.T) { req := &http.Request{Header: http.Header{}} resp := func(status int) *http.Response { - return &http.Response{StatusCode: status, Body: &spyReadCloser{}} + return &http.Response{StatusCode: status, Body: &spyReadCloser{ReadCloser: ioutil.NopCloser(strings.NewReader(""))}} } someErr := errors.New("some error") @@ -213,7 +213,7 @@ func TestRetryRoundTripperRewind(t *testing.T) { conditions..., ) - spy := &spyReadCloser{Reader: strings.NewReader(bodyContent)} + spy := &spyReadCloser{ReadCloser: ioutil.NopCloser(strings.NewReader(bodyContent))} req, _ := http.NewRequest("POST", "http://test.domain", spy) rt.RoundTrip(req) diff --git a/pkg/apis/serving/v1alpha1/revision_types.go b/pkg/apis/serving/v1alpha1/revision_types.go index 9fc303be2a29..bd556331ee4d 100644 --- a/pkg/apis/serving/v1alpha1/revision_types.go +++ b/pkg/apis/serving/v1alpha1/revision_types.go @@ -118,6 +118,17 @@ const ( RevisionContainerConcurrencyMax RevisionContainerConcurrencyType = 1000 ) +// RevisionProtocolType is an enumeration of the supported application-layer protocols +// See also: https://github.com/knative/serving/blob/master/docs/runtime-contract.md#protocols-and-ports +type RevisionProtocolType string + +const ( + // HTTP/1.1 + RevisionProtocolHTTP1 RevisionProtocolType = "http1" + // HTTP/2 with Prior Knowledge + RevisionProtocolH2C RevisionProtocolType = "h2c" +) + const ( // UserPortName is the name that will be used for the Port on the // Deployment and Pod created by a Revision. This name will be set regardless of if @@ -316,6 +327,15 @@ func (r *Revision) BuildRef() *corev1.ObjectReference { return nil } +func (r *Revision) GetProtocol() RevisionProtocolType { + ports := r.Spec.Container.Ports + if len(ports) > 0 && ports[0].Name == "h2c" { + return RevisionProtocolH2C + } + + return RevisionProtocolHTTP1 +} + // IsReady looks at the conditions and if the Status has a condition // RevisionConditionReady returns true if ConditionStatus is True func (rs *RevisionStatus) IsReady() bool { diff --git a/pkg/apis/serving/v1alpha1/revision_types_test.go b/pkg/apis/serving/v1alpha1/revision_types_test.go index 002673052250..95a54e19bdd1 100644 --- a/pkg/apis/serving/v1alpha1/revision_types_test.go +++ b/pkg/apis/serving/v1alpha1/revision_types_test.go @@ -603,6 +603,57 @@ func TestRevisionBuildRefNil(t *testing.T) { } } +func TestRevisionGetProtocol(t *testing.T) { + containerWithPortName := func(name string) corev1.Container { + return corev1.Container{Ports: []corev1.ContainerPort{{Name: name}}} + } + + tests := []struct { + name string + container corev1.Container + protocol RevisionProtocolType + }{ + { + name: "undefined", + container: corev1.Container{}, + protocol: RevisionProtocolHTTP1, + }, + { + name: "http1", + container: containerWithPortName("http1"), + protocol: RevisionProtocolHTTP1, + }, + { + name: "h2c", + container: containerWithPortName("h2c"), + protocol: RevisionProtocolH2C, + }, + { + name: "unknown", + container: containerWithPortName("whatever"), + protocol: RevisionProtocolHTTP1, + }, + { + name: "empty", + container: containerWithPortName(""), + protocol: RevisionProtocolHTTP1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &Revision{Spec: RevisionSpec{Container: tt.container}} + + got := r.GetProtocol() + want := tt.protocol + + if got != want { + t.Errorf("got: %#v, want: %#v", got, want) + } + }) + } +} + func TestRevisionGetLastPinned(t *testing.T) { cases := []struct { name string diff --git a/pkg/http/h2c/h2c.go b/pkg/http/h2c/h2c.go index 8bab9d981309..fe1a52024b71 100644 --- a/pkg/http/h2c/h2c.go +++ b/pkg/http/h2c/h2c.go @@ -17,6 +17,7 @@ import ( "crypto/tls" "net" "net/http" + "time" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" @@ -42,6 +43,12 @@ func ListenAndServe(addr string, h http.Handler) error { var DefaultTransport http.RoundTripper = &http2.Transport{ AllowHTTP: true, DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) { - return net.Dial(netw, addr) + d := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + } + + return d.Dial(netw, addr) }, } diff --git a/pkg/reconciler/v1alpha1/revision/resources/constants.go b/pkg/reconciler/v1alpha1/revision/resources/constants.go index a4bf504973d4..c1d6c7a4cc2f 100644 --- a/pkg/reconciler/v1alpha1/revision/resources/constants.go +++ b/pkg/reconciler/v1alpha1/revision/resources/constants.go @@ -39,7 +39,9 @@ const ( autoscalerPort = 8080 // ServicePortName is the name of the external port of the service - ServicePortName = "http" + ServicePortNameHTTP1 = "http" + ServicePortNameH2C = "http2" + // ServicePort is the external port of the service ServicePort = int32(80) // MetricsPortName is the name of the external port of the service for metrics diff --git a/pkg/reconciler/v1alpha1/revision/resources/service.go b/pkg/reconciler/v1alpha1/revision/resources/service.go index 29ef67c4fb45..a7fa14c94071 100644 --- a/pkg/reconciler/v1alpha1/revision/resources/service.go +++ b/pkg/reconciler/v1alpha1/revision/resources/service.go @@ -28,20 +28,6 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -var ( - servicePorts = []corev1.ServicePort{{ - Name: ServicePortName, - Protocol: corev1.ProtocolTCP, - Port: ServicePort, - TargetPort: intstr.FromString(v1alpha1.RequestQueuePortName), - }, { - Name: MetricsPortName, - Protocol: corev1.ProtocolTCP, - Port: MetricsPort, - TargetPort: intstr.FromString(v1alpha1.RequestQueueMetricsPortName), - }} -) - // MakeK8sService creates a Kubernetes Service that targets all pods with the same // serving.RevisionLabelKey label. Traffic is routed to queue-proxy port. func MakeK8sService(rev *v1alpha1.Revision) *corev1.Service { @@ -56,10 +42,28 @@ func MakeK8sService(rev *v1alpha1.Revision) *corev1.Service { OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)}, }, Spec: corev1.ServiceSpec{ - Ports: servicePorts, + Ports: []corev1.ServicePort{{ + Name: ServicePortName(rev), + Protocol: corev1.ProtocolTCP, + Port: ServicePort, + TargetPort: intstr.FromString(v1alpha1.RequestQueuePortName), + }, { + Name: MetricsPortName, + Protocol: corev1.ProtocolTCP, + Port: MetricsPort, + TargetPort: intstr.FromString(v1alpha1.RequestQueueMetricsPortName), + }}, Selector: map[string]string{ serving.RevisionLabelKey: rev.Name, }, }, } } + +func ServicePortName(rev *v1alpha1.Revision) string { + if rev.GetProtocol() == v1alpha1.RevisionProtocolH2C { + return ServicePortNameH2C + } + + return ServicePortNameHTTP1 +} diff --git a/pkg/reconciler/v1alpha1/revision/resources/service_test.go b/pkg/reconciler/v1alpha1/revision/resources/service_test.go index 40d447cfc785..fe5408f2acbc 100644 --- a/pkg/reconciler/v1alpha1/revision/resources/service_test.go +++ b/pkg/reconciler/v1alpha1/revision/resources/service_test.go @@ -22,6 +22,7 @@ import ( "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/knative/serving/pkg/apis/autoscaling" "github.com/knative/serving/pkg/apis/serving" @@ -63,7 +64,17 @@ func TestMakeK8sService(t *testing.T) { }}, }, Spec: corev1.ServiceSpec{ - Ports: servicePorts, + Ports: []corev1.ServicePort{{ + Name: ServicePortNameHTTP1, + Protocol: corev1.ProtocolTCP, + Port: ServicePort, + TargetPort: intstr.FromString(v1alpha1.RequestQueuePortName), + }, { + Name: MetricsPortName, + Protocol: corev1.ProtocolTCP, + Port: MetricsPort, + TargetPort: intstr.FromString(v1alpha1.RequestQueueMetricsPortName), + }}, Selector: map[string]string{ serving.RevisionLabelKey: "bar", }, @@ -77,6 +88,13 @@ func TestMakeK8sService(t *testing.T) { Name: "baz", UID: "1234", }, + Spec: v1alpha1.RevisionSpec{ + Container: corev1.Container{ + Ports: []corev1.ContainerPort{ + {Name: "h2c"}, + }, + }, + }, }, want: &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -99,7 +117,17 @@ func TestMakeK8sService(t *testing.T) { }}, }, Spec: corev1.ServiceSpec{ - Ports: servicePorts, + Ports: []corev1.ServicePort{{ + Name: ServicePortNameH2C, + Protocol: corev1.ProtocolTCP, + Port: ServicePort, + TargetPort: intstr.FromString(v1alpha1.RequestQueuePortName), + }, { + Name: MetricsPortName, + Protocol: corev1.ProtocolTCP, + Port: MetricsPort, + TargetPort: intstr.FromString(v1alpha1.RequestQueueMetricsPortName), + }}, Selector: map[string]string{ serving.RevisionLabelKey: "baz", }, diff --git a/pkg/reconciler/v1alpha1/route/resources/cluster_ingress.go b/pkg/reconciler/v1alpha1/route/resources/cluster_ingress.go index 8ab651b350f0..425559a84b05 100644 --- a/pkg/reconciler/v1alpha1/route/resources/cluster_ingress.go +++ b/pkg/reconciler/v1alpha1/route/resources/cluster_ingress.go @@ -147,7 +147,7 @@ func addInactive(r *v1alpha1.HTTPClusterIngressPath, ns string, inactive traffic ClusterIngressBackend: v1alpha1.ClusterIngressBackend{ ServiceNamespace: system.Namespace(), ServiceName: activator.K8sServiceName, - ServicePort: intstr.FromInt(int(revisionresources.ServicePort)), + ServicePort: intstr.FromInt(int(activator.ServicePort(maxInactiveTarget.Protocol))), }, Percent: totalInactivePercent, }) diff --git a/pkg/reconciler/v1alpha1/route/resources/service.go b/pkg/reconciler/v1alpha1/route/resources/service.go index 58768a376b9d..61f1e8959fed 100644 --- a/pkg/reconciler/v1alpha1/route/resources/service.go +++ b/pkg/reconciler/v1alpha1/route/resources/service.go @@ -88,7 +88,7 @@ func makeServiceSpec(ingress *netv1alpha1.ClusterIngress) (*corev1.ServiceSpec, return &corev1.ServiceSpec{ Type: corev1.ServiceTypeClusterIP, Ports: []corev1.ServicePort{{ - Name: revisionresources.ServicePortName, + Name: revisionresources.ServicePortNameHTTP1, Port: revisionresources.ServicePort, }}, }, nil diff --git a/pkg/reconciler/v1alpha1/route/traffic/traffic.go b/pkg/reconciler/v1alpha1/route/traffic/traffic.go index c61757489bd9..5e74b0fe538d 100644 --- a/pkg/reconciler/v1alpha1/route/traffic/traffic.go +++ b/pkg/reconciler/v1alpha1/route/traffic/traffic.go @@ -27,10 +27,12 @@ import ( // DefaultTarget is the unnamed default target for the traffic. const DefaultTarget = "" -// A RevisionTarget adds the Active/Inactive state of a Revision to a flattened TrafficTarget. +// A RevisionTarget adds the Active/Inactive state and the transport protocol of a +// Revision to a flattened TrafficTarget. type RevisionTarget struct { v1alpha1.TrafficTarget - Active bool + Active bool + Protocol v1alpha1.RevisionProtocolType } // RevisionTargets is a collection of revision targets. @@ -208,6 +210,7 @@ func (t *configBuilder) addConfigurationTarget(tt *v1alpha1.TrafficTarget) error target := RevisionTarget{ TrafficTarget: *tt, Active: !rev.Status.IsActivationRequired(), + Protocol: rev.GetProtocol(), } target.TrafficTarget.RevisionName = rev.Name t.addFlattenedTarget(target) @@ -225,6 +228,7 @@ func (t *configBuilder) addRevisionTarget(tt *v1alpha1.TrafficTarget) error { target := RevisionTarget{ TrafficTarget: *tt, Active: !rev.Status.IsActivationRequired(), + Protocol: rev.GetProtocol(), } t.revisions[tt.RevisionName] = rev if configName, ok := rev.Labels[serving.ConfigurationLabelKey]; ok { diff --git a/pkg/reconciler/v1alpha1/route/traffic/traffic_test.go b/pkg/reconciler/v1alpha1/route/traffic/traffic_test.go index 3a78c672cd8e..4289d51936de 100644 --- a/pkg/reconciler/v1alpha1/route/traffic/traffic_test.go +++ b/pkg/reconciler/v1alpha1/route/traffic/traffic_test.go @@ -131,7 +131,8 @@ func TestBuildTrafficConfiguration_Vanilla(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -140,7 +141,8 @@ func TestBuildTrafficConfiguration_Vanilla(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig}, Revisions: map[string]*v1alpha1.Revision{goodNewRev.Name: goodNewRev}, @@ -164,9 +166,9 @@ func TestPartitionTargets(t *testing.T) { "skip 0 percent", RevisionTargets( []RevisionTarget{ - {v1alpha1.TrafficTarget{RevisionName: "implicit"}, true}, - {v1alpha1.TrafficTarget{RevisionName: "explicit", Percent: 0}, true}, - {v1alpha1.TrafficTarget{RevisionName: "passive", Percent: 0}, false}, + {v1alpha1.TrafficTarget{RevisionName: "implicit"}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "explicit", Percent: 0}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "passive", Percent: 0}, false, ""}, }, ), make(RevisionTargets, 0), @@ -174,49 +176,49 @@ func TestPartitionTargets(t *testing.T) { }, { "1 active", - RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, true}}), - RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, true}}), + RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, true, ""}}), + RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, true, ""}}), make(RevisionTargets, 0), }, { "1 passive", - RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, false}}), + RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, false, ""}}), make(RevisionTargets, 0), - RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, false}}), + RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "a", Percent: 1}, false, ""}}), }, { "1 active, 1 passive, 1 0 percent", RevisionTargets( []RevisionTarget{ - {v1alpha1.TrafficTarget{RevisionName: "zero"}, true}, - {v1alpha1.TrafficTarget{RevisionName: "fiver", Percent: 5}, true}, - {v1alpha1.TrafficTarget{RevisionName: "sixer", Percent: 6}, false}, + {v1alpha1.TrafficTarget{RevisionName: "zero"}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "fiver", Percent: 5}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "sixer", Percent: 6}, false, ""}, }, ), - RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "fiver", Percent: 5}, true}}), - RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "sixer", Percent: 6}, false}}), + RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "fiver", Percent: 5}, true, ""}}), + RevisionTargets([]RevisionTarget{{v1alpha1.TrafficTarget{RevisionName: "sixer", Percent: 6}, false, ""}}), }, { "2 of each", RevisionTargets( []RevisionTarget{ - {v1alpha1.TrafficTarget{RevisionName: "one", Percent: 1}, true}, - {v1alpha1.TrafficTarget{RevisionName: "two", Percent: 2}, false}, - {v1alpha1.TrafficTarget{RevisionName: "three", Percent: 3}, true}, - {v1alpha1.TrafficTarget{RevisionName: "four", Percent: 4}, false}, - {v1alpha1.TrafficTarget{RevisionName: "fiver", Percent: 0}, true}, + {v1alpha1.TrafficTarget{RevisionName: "one", Percent: 1}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "two", Percent: 2}, false, ""}, + {v1alpha1.TrafficTarget{RevisionName: "three", Percent: 3}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "four", Percent: 4}, false, ""}, + {v1alpha1.TrafficTarget{RevisionName: "fiver", Percent: 0}, true, ""}, }, ), RevisionTargets( []RevisionTarget{ - {v1alpha1.TrafficTarget{RevisionName: "one", Percent: 1}, true}, - {v1alpha1.TrafficTarget{RevisionName: "three", Percent: 3}, true}, + {v1alpha1.TrafficTarget{RevisionName: "one", Percent: 1}, true, ""}, + {v1alpha1.TrafficTarget{RevisionName: "three", Percent: 3}, true, ""}, }, ), RevisionTargets( []RevisionTarget{ - {v1alpha1.TrafficTarget{RevisionName: "two", Percent: 2}, false}, - {v1alpha1.TrafficTarget{RevisionName: "four", Percent: 4}, false}, + {v1alpha1.TrafficTarget{RevisionName: "two", Percent: 2}, false, ""}, + {v1alpha1.TrafficTarget{RevisionName: "four", Percent: 4}, false, ""}, }, ), }, @@ -246,7 +248,8 @@ func TestBuildTrafficConfiguration_NoNameRevision(t *testing.T) { ConfigurationName: goodConfig.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -255,7 +258,8 @@ func TestBuildTrafficConfiguration_NoNameRevision(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig}, Revisions: map[string]*v1alpha1.Revision{goodNewRev.Name: goodNewRev}, @@ -281,7 +285,8 @@ func TestBuildTrafficConfiguration_VanillaScaledToZero(t *testing.T) { RevisionName: inactiveRev.Name, Percent: 100, }, - Active: false, + Active: false, + Protocol: v1alpha1.RevisionProtocolHTTP1, }}, }, revisionTargets: []RevisionTarget{{ @@ -290,7 +295,8 @@ func TestBuildTrafficConfiguration_VanillaScaledToZero(t *testing.T) { RevisionName: inactiveRev.Name, Percent: 100, }, - Active: false, + Active: false, + Protocol: v1alpha1.RevisionProtocolHTTP1, }}, Configurations: map[string]*v1alpha1.Configuration{inactiveConfig.Name: inactiveConfig}, Revisions: map[string]*v1alpha1.Revision{inactiveRev.Name: inactiveRev}, @@ -319,13 +325,16 @@ func TestBuildTrafficConfiguration_TwoConfigs(t *testing.T) { RevisionName: niceNewRev.Name, Percent: 90, }, - Active: true}, { + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, + }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, Percent: 10, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -334,13 +343,16 @@ func TestBuildTrafficConfiguration_TwoConfigs(t *testing.T) { RevisionName: niceNewRev.Name, Percent: 90, }, - Active: true}, { + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, + }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, Percent: 10, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig, niceConfig.Name: niceConfig}, Revisions: map[string]*v1alpha1.Revision{goodNewRev.Name: goodNewRev, niceNewRev.Name: niceNewRev}, @@ -370,14 +382,16 @@ func TestBuildTrafficConfiguration_Canary(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 90, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, Percent: 10, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -386,14 +400,16 @@ func TestBuildTrafficConfiguration_Canary(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 90, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, Percent: 10, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig}, Revisions: map[string]*v1alpha1.Revision{goodOldRev.Name: goodOldRev, goodNewRev.Name: goodNewRev}, @@ -430,7 +446,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 49, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "two", @@ -438,7 +455,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 51, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, "one": {{ TrafficTarget: v1alpha1.TrafficTarget{ @@ -447,7 +465,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }}, "two": {{ TrafficTarget: v1alpha1.TrafficTarget{ @@ -456,7 +475,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, "also-two": {{ TrafficTarget: v1alpha1.TrafficTarget{ @@ -465,7 +485,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -475,7 +496,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 49, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "two", @@ -483,7 +505,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 50, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "also-two", @@ -491,7 +514,8 @@ func TestBuildTrafficConfiguration_Consolidated(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 1, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig}, Revisions: map[string]*v1alpha1.Revision{goodOldRev.Name: goodOldRev, goodNewRev.Name: goodNewRev}, @@ -520,14 +544,16 @@ func TestBuildTrafficConfiguration_TwoFixedRevisions(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 90, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, Percent: 10, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -536,14 +562,16 @@ func TestBuildTrafficConfiguration_TwoFixedRevisions(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 90, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, Percent: 10, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig}, Revisions: map[string]*v1alpha1.Revision{goodNewRev.Name: goodNewRev, goodOldRev.Name: goodOldRev}, @@ -572,14 +600,16 @@ func TestBuildTrafficConfiguration_TwoFixedRevisionsFromTwoConfigurations(t *tes RevisionName: goodNewRev.Name, Percent: 40, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: niceConfig.Name, RevisionName: niceNewRev.Name, Percent: 60, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -588,14 +618,16 @@ func TestBuildTrafficConfiguration_TwoFixedRevisionsFromTwoConfigurations(t *tes RevisionName: goodNewRev.Name, Percent: 40, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }, { TrafficTarget: v1alpha1.TrafficTarget{ ConfigurationName: niceConfig.Name, RevisionName: niceNewRev.Name, Percent: 60, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig, niceConfig.Name: niceConfig}, Revisions: map[string]*v1alpha1.Revision{goodNewRev.Name: goodNewRev, niceNewRev.Name: niceNewRev}, @@ -627,19 +659,24 @@ func TestBuildTrafficConfiguration_Preliminary(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 100, }, - Active: true}, { + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, + }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "beta", ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, }, - Active: true}, { + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, + }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "alpha", ConfigurationName: niceConfig.Name, RevisionName: niceNewRev.Name, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, "beta": {{ TrafficTarget: v1alpha1.TrafficTarget{ @@ -648,7 +685,9 @@ func TestBuildTrafficConfiguration_Preliminary(t *testing.T) { RevisionName: goodNewRev.Name, Percent: 100, }, - Active: true}}, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, + }}, "alpha": {{ TrafficTarget: v1alpha1.TrafficTarget{ Name: "alpha", @@ -656,7 +695,8 @@ func TestBuildTrafficConfiguration_Preliminary(t *testing.T) { RevisionName: niceNewRev.Name, Percent: 100, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, }, revisionTargets: []RevisionTarget{{ @@ -665,19 +705,24 @@ func TestBuildTrafficConfiguration_Preliminary(t *testing.T) { RevisionName: goodOldRev.Name, Percent: 100, }, - Active: true}, { + Active: true, + Protocol: v1alpha1.RevisionProtocolHTTP1, + }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "beta", ConfigurationName: goodConfig.Name, RevisionName: goodNewRev.Name, }, - Active: true}, { + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, + }, { TrafficTarget: v1alpha1.TrafficTarget{ Name: "alpha", ConfigurationName: niceConfig.Name, RevisionName: niceNewRev.Name, }, - Active: true, + Active: true, + Protocol: v1alpha1.RevisionProtocolH2C, }}, Configurations: map[string]*v1alpha1.Configuration{goodConfig.Name: goodConfig, niceConfig.Name: niceConfig}, Revisions: map[string]*v1alpha1.Revision{goodOldRev.Name: goodOldRev, goodNewRev.Name: goodNewRev, niceNewRev.Name: niceNewRev}, @@ -973,6 +1018,10 @@ func getTestReadyConfig(name string) (*v1alpha1.Configuration, *v1alpha1.Revisio Status: corev1.ConditionTrue, }}, }) + + // rev1 will use http1, rev2 will use h2c + config.Spec.RevisionTemplate.Spec.Container.Ports = []corev1.ContainerPort{{Name: "h2c"}} + rev2 := testRevForConfig(config, name+"-revision-2") rev2.Status.MarkResourcesAvailable() rev2.Status.MarkContainerHealthy()