clones vendor of k8s.io/kubelet/pkg/cri/streaming

Signed-off-by: Mike Brown <brownwm@us.ibm.com>
This commit is contained in:
Mike Brown
2025-05-12 16:49:30 +00:00
parent a806c0ae78
commit 7063ee6597
48 changed files with 2759 additions and 703 deletions

5
go.mod
View File

@@ -36,6 +36,7 @@ require (
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c
github.com/docker/go-metrics v0.0.1
github.com/docker/go-units v0.5.0
github.com/emicklei/go-restful/v3 v3.11.0
github.com/fsnotify/fsnotify v1.9.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
@@ -72,6 +73,7 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
go.opentelemetry.io/otel/sdk v1.35.0
go.opentelemetry.io/otel/trace v1.35.0
go.uber.org/goleak v1.3.0
golang.org/x/mod v0.24.0
golang.org/x/sync v0.14.0
golang.org/x/sys v0.33.0
@@ -83,7 +85,6 @@ require (
k8s.io/client-go v0.32.3
k8s.io/cri-api v0.32.3
k8s.io/klog/v2 v2.130.1
k8s.io/kubelet v0.32.3
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
tags.cncf.io/container-device-interface v1.0.1
)
@@ -95,7 +96,6 @@ require (
github.com/cilium/ebpf v0.16.0 // indirect
github.com/containers/ocicrypt v1.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
@@ -145,7 +145,6 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.32.3 // indirect
k8s.io/apiserver v0.32.3 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect

4
go.sum
View File

@@ -556,8 +556,6 @@ k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/cri-api v0.32.3 h1:E8VXbXNn4yAgmuKTeNzg0C1MFSxzTdlHSwUvjuYlPTY=
@@ -566,8 +564,6 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kubelet v0.32.3 h1:B9HzW4yB67flx8tN2FYuDwZvxnmK3v5EjxxFvOYjmc8=
k8s.io/kubelet v0.32.3/go.mod h1:yyAQSCKC+tjSlaFw4HQG7Jein+vo+GeKBGdXdQGvL1U=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=

View File

@@ -28,13 +28,13 @@ import (
"github.com/containerd/log"
"github.com/pelletier/go-toml/v2"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubelet/pkg/cri/streaming"
runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
runcoptions "github.com/containerd/containerd/api/types/runc/options"
runtimeoptions "github.com/containerd/containerd/api/types/runtimeoptions/v1"
"github.com/containerd/containerd/v2/internal/cri/annotations"
"github.com/containerd/containerd/v2/internal/cri/opts"
streaming "github.com/containerd/containerd/v2/internal/cri/streamingserver"
"github.com/containerd/containerd/v2/pkg/deprecation"
"github.com/containerd/containerd/v2/plugins"
)

View File

@@ -27,7 +27,7 @@ import (
k8snet "k8s.io/apimachinery/pkg/util/net"
k8scert "k8s.io/client-go/util/cert"
"k8s.io/kubelet/pkg/cri/streaming"
streaming "github.com/containerd/containerd/v2/internal/cri/streamingserver"
)
type streamListenerMode int

View File

@@ -32,7 +32,6 @@ import (
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go/features"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubelet/pkg/cri/streaming"
apitypes "github.com/containerd/containerd/api/types"
@@ -49,6 +48,7 @@ import (
"github.com/containerd/containerd/v2/internal/cri/store/label"
sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox"
snapshotstore "github.com/containerd/containerd/v2/internal/cri/store/snapshot"
streaming "github.com/containerd/containerd/v2/internal/cri/streamingserver"
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
"github.com/containerd/containerd/v2/internal/eventq"
nriservice "github.com/containerd/containerd/v2/internal/nri"

View File

@@ -26,8 +26,8 @@ import (
"k8s.io/client-go/tools/remotecommand"
"k8s.io/utils/exec"
streaming "github.com/containerd/containerd/v2/internal/cri/streamingserver"
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
"k8s.io/kubelet/pkg/cri/streaming"
)
type streamRuntime struct {

View File

@@ -0,0 +1,34 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 streamingserver contains server functions related the cri streaming api.
package streamingserver // import "k8s.io/kubelet/pkg/cri/streaming"

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -14,7 +30,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package streaming
package streamingserver
import (
"net/http"

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2015 The Kubernetes Authors.

View File

@@ -0,0 +1,34 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 portforward contains functions related to pod portwarding over the cri streaming api.
package portforward // import "k8s.io/kubelet/pkg/cri/streaming/portforward"

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -25,13 +41,12 @@ import (
"sync"
"time"
api "k8s.io/api/core/v1"
api "github.com/containerd/containerd/v2/internal/cri/streamingserver/v1"
"github.com/containerd/log"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/httpstream/spdy"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
)
func handleHTTPStreams(req *http.Request, w http.ResponseWriter, portForwarder PortForwarder, podName string, uid types.UID, supportedPortForwardProtocols []string, idleTimeout, streamCreationTimeout time.Duration) error {
@@ -43,7 +58,7 @@ func handleHTTPStreams(req *http.Request, w http.ResponseWriter, portForwarder P
}
streamChan := make(chan httpstream.Stream, 1)
klog.V(5).InfoS("Upgrading port forward response")
log.L.Info("Upgrading port forward response")
upgrader := spdy.NewResponseUpgrader()
conn := upgrader.UpgradeResponse(w, req, httpStreamReceived(streamChan))
if conn == nil {
@@ -51,7 +66,7 @@ func handleHTTPStreams(req *http.Request, w http.ResponseWriter, portForwarder P
}
defer conn.Close()
klog.V(5).InfoS("Connection setting port forwarding streaming connection idle timeout", "connection", conn, "idleTimeout", idleTimeout)
log.L.Infof("Connection setting port forwarding streaming connection idle timeout connection idleTimeout %v", idleTimeout)
conn.SetIdleTimeout(idleTimeout)
h := &httpStreamHandler{
@@ -122,11 +137,11 @@ func (h *httpStreamHandler) getStreamPair(requestID string) (*httpStreamPair, bo
defer h.streamPairsLock.Unlock()
if p, ok := h.streamPairs[requestID]; ok {
klog.V(5).InfoS("Connection request found existing stream pair", "connection", h.conn, "request", requestID)
log.L.Infof("Connection request found existing stream pair connection request %v", requestID)
return p, false
}
klog.V(5).InfoS("Connection request creating new stream pair", "connection", h.conn, "request", requestID)
log.L.Infof("Connection request creating new stream pair connection request %v", requestID)
p := newPortForwardPair(requestID)
h.streamPairs[requestID] = p
@@ -144,7 +159,7 @@ func (h *httpStreamHandler) monitorStreamPair(p *httpStreamPair, timeout <-chan
utilruntime.HandleError(err)
p.printError(err.Error())
case <-p.complete:
klog.V(5).InfoS("Connection request successfully received error and data streams", "connection", h.conn, "request", p.requestID)
log.L.Infof("Connection request successfully received error and data streams connection request %v", p.requestID)
}
h.removeStreamPair(p.requestID)
}
@@ -175,7 +190,7 @@ func (h *httpStreamHandler) removeStreamPair(requestID string) {
func (h *httpStreamHandler) requestID(stream httpstream.Stream) string {
requestID := stream.Headers().Get(api.PortForwardRequestIDHeader)
if len(requestID) == 0 {
klog.V(5).InfoS("Connection stream received without requestID header", "connection", h.conn)
log.L.Info("Connection stream received without requestID header connection")
// If we get here, it's because the connection came from an older client
// that isn't generating the request id header
// (https://github.com/kubernetes/kubernetes/blob/843134885e7e0b360eb5441e85b1410a8b1a7a0c/pkg/client/unversioned/portforward/portforward.go#L258-L287)
@@ -202,7 +217,7 @@ func (h *httpStreamHandler) requestID(stream httpstream.Stream) string {
requestID = strconv.Itoa(int(stream.Identifier()) - 2)
}
klog.V(5).InfoS("Connection automatically assigning request ID from stream type and stream ID", "connection", h.conn, "request", requestID, "streamType", streamType, "stream", stream.Identifier())
log.L.Infof("Connection automatically assigning request ID from stream type and stream ID connection request %v streamType %v stream %v", requestID, streamType, stream.Identifier())
}
return requestID
}
@@ -211,17 +226,17 @@ func (h *httpStreamHandler) requestID(stream httpstream.Stream) string {
// streams, invoking portForward for each complete stream pair. The loop exits
// when the httpstream.Connection is closed.
func (h *httpStreamHandler) run() {
klog.V(5).InfoS("Connection waiting for port forward streams", "connection", h.conn)
log.L.Infof("Connection waiting for port forward streams connection %v", h.conn)
Loop:
for {
select {
case <-h.conn.CloseChan():
klog.V(5).InfoS("Connection upgraded connection closed", "connection", h.conn)
log.L.Info("Connection upgraded connection closed connection")
break Loop
case stream := <-h.streamChan:
requestID := h.requestID(stream)
streamType := stream.Headers().Get(api.StreamType)
klog.V(5).InfoS("Connection request received new type of stream", "connection", h.conn, "request", requestID, "streamType", streamType)
log.L.Infof("Connection request received new type of stream connection request %v streamType %v", requestID, streamType)
p, created := h.getStreamPair(requestID)
if created {
@@ -248,9 +263,9 @@ func (h *httpStreamHandler) portForward(p *httpStreamPair) {
portString := p.dataStream.Headers().Get(api.PortHeader)
port, _ := strconv.ParseInt(portString, 10, 32)
klog.V(5).InfoS("Connection request invoking forwarder.PortForward for port", "connection", h.conn, "request", p.requestID, "port", portString)
log.L.Infof("Connection request invoking forwarder.PortForward for port connection request %v port %v", p.requestID, portString)
err := h.forwarder.PortForward(ctx, h.pod, h.uid, int32(port), p.dataStream)
klog.V(5).InfoS("Connection request done invoking forwarder.PortForward for port", "connection", h.conn, "request", p.requestID, "port", portString)
log.L.Infof("Connection request done invoking forwarder.PortForward for port connection reqeust %v port %v", p.requestID, portString)
if err != nil {
msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", port, h.pod, h.uid, err)
@@ -260,8 +275,8 @@ func (h *httpStreamHandler) portForward(p *httpStreamPair) {
// with data stream most probably, thus we want to reset the streams
// indicating there was a problem and allow a new set of streams be
// created to mitigate the problem
p.dataStream.Reset() // nolint:errcheck
p.errorStream.Reset() // nolint:errcheck
p.dataStream.Reset() //nolint:errcheck
p.errorStream.Reset() //nolint:errcheck
}
}

View File

@@ -0,0 +1,283 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 portforward
import (
"net/http"
"testing"
"time"
api "github.com/containerd/containerd/v2/internal/cri/streamingserver/v1"
"k8s.io/apimachinery/pkg/util/httpstream"
)
func TestHTTPStreamReceived(t *testing.T) {
tests := map[string]struct {
port string
streamType string
expectedError string
}{
"missing port": {
expectedError: `"port" header is required`,
},
"unable to parse port": {
port: "abc",
expectedError: `unable to parse "abc" as a port: strconv.ParseUint: parsing "abc": invalid syntax`,
},
"negative port": {
port: "-1",
expectedError: `unable to parse "-1" as a port: strconv.ParseUint: parsing "-1": invalid syntax`,
},
"missing stream type": {
port: "80",
expectedError: `"streamType" header is required`,
},
"valid port with error stream": {
port: "80",
streamType: "error",
},
"valid port with data stream": {
port: "80",
streamType: "data",
},
"invalid stream type": {
port: "80",
streamType: "foo",
expectedError: `invalid stream type "foo"`,
},
}
for name, test := range tests {
streams := make(chan httpstream.Stream, 1)
f := httpStreamReceived(streams)
stream := newFakeHTTPStream()
if len(test.port) > 0 {
stream.headers.Set("port", test.port)
}
if len(test.streamType) > 0 {
stream.headers.Set("streamType", test.streamType)
}
replySent := make(chan struct{})
err := f(stream, replySent)
close(replySent)
if len(test.expectedError) > 0 {
if err == nil {
t.Errorf("%s: expected err=%q, but it was nil", name, test.expectedError)
}
if e, a := test.expectedError, err.Error(); e != a {
t.Errorf("%s: expected err=%q, got %q", name, e, a)
}
continue
}
if err != nil {
t.Errorf("%s: unexpected error %v", name, err)
continue
}
if s := <-streams; s != stream {
t.Errorf("%s: expected stream %#v, got %#v", name, stream, s)
}
}
}
type fakeConn struct {
removeStreamsCalled bool
}
func (*fakeConn) CreateStream(headers http.Header) (httpstream.Stream, error) { return nil, nil }
func (*fakeConn) Close() error { return nil }
func (*fakeConn) CloseChan() <-chan bool { return nil }
func (*fakeConn) SetIdleTimeout(timeout time.Duration) {}
func (f *fakeConn) RemoveStreams(streams ...httpstream.Stream) { f.removeStreamsCalled = true }
func TestGetStreamPair(t *testing.T) {
timeout := make(chan time.Time)
conn := &fakeConn{}
h := &httpStreamHandler{
streamPairs: make(map[string]*httpStreamPair),
conn: conn,
}
// test adding a new entry
p, created := h.getStreamPair("1")
if p == nil {
t.Fatalf("unexpected nil pair")
}
if !created {
t.Fatal("expected created=true")
}
if p.dataStream != nil {
t.Errorf("unexpected non-nil data stream")
}
if p.errorStream != nil {
t.Errorf("unexpected non-nil error stream")
}
// start the monitor for this pair
monitorDone := make(chan struct{})
go func() {
h.monitorStreamPair(p, timeout)
close(monitorDone)
}()
if !h.hasStreamPair("1") {
t.Fatal("This should still be true")
}
// make sure we can retrieve an existing entry
p2, created := h.getStreamPair("1")
if created {
t.Fatal("expected created=false")
}
if p != p2 {
t.Fatalf("retrieving an existing pair: expected %#v, got %#v", p, p2)
}
// removed via complete
dataStream := newFakeHTTPStream()
dataStream.headers.Set(api.StreamType, api.StreamTypeData)
complete, err := p.add(dataStream)
if err != nil {
t.Fatalf("unexpected error adding data stream to pair: %v", err)
}
if complete {
t.Fatalf("unexpected complete")
}
errorStream := newFakeHTTPStream()
errorStream.headers.Set(api.StreamType, api.StreamTypeError)
complete, err = p.add(errorStream)
if err != nil {
t.Fatalf("unexpected error adding error stream to pair: %v", err)
}
if !complete {
t.Fatal("unexpected incomplete")
}
// make sure monitorStreamPair completed
<-monitorDone
if !conn.removeStreamsCalled {
t.Fatalf("connection remove stream not called")
}
conn.removeStreamsCalled = false
// make sure the pair was removed
if h.hasStreamPair("1") {
t.Fatal("expected removal of pair after both data and error streams received")
}
// removed via timeout
p, created = h.getStreamPair("2")
if !created {
t.Fatal("expected created=true")
}
if p == nil {
t.Fatal("expected p not to be nil")
}
monitorDone = make(chan struct{})
go func() {
h.monitorStreamPair(p, timeout)
close(monitorDone)
}()
// cause the timeout
close(timeout)
// make sure monitorStreamPair completed
<-monitorDone
if h.hasStreamPair("2") {
t.Fatal("expected stream pair to be removed")
}
if !conn.removeStreamsCalled {
t.Fatalf("connection remove stream not called")
}
}
func TestRequestID(t *testing.T) {
h := &httpStreamHandler{}
s := newFakeHTTPStream()
s.headers.Set(api.StreamType, api.StreamTypeError)
s.id = 1
if e, a := "1", h.requestID(s); e != a {
t.Errorf("expected %q, got %q", e, a)
}
s.headers.Set(api.StreamType, api.StreamTypeData)
s.id = 3
if e, a := "1", h.requestID(s); e != a {
t.Errorf("expected %q, got %q", e, a)
}
s.id = 7
s.headers.Set(api.PortForwardRequestIDHeader, "2")
if e, a := "2", h.requestID(s); e != a {
t.Errorf("expected %q, got %q", e, a)
}
}
type fakeHTTPStream struct {
headers http.Header
id uint32
}
func newFakeHTTPStream() *fakeHTTPStream {
return &fakeHTTPStream{
headers: make(http.Header),
}
}
var _ httpstream.Stream = &fakeHTTPStream{}
func (s *fakeHTTPStream) Read(data []byte) (int, error) {
return 0, nil
}
func (s *fakeHTTPStream) Write(data []byte) (int, error) {
return 0, nil
}
func (s *fakeHTTPStream) Close() error {
return nil
}
func (s *fakeHTTPStream) Reset() error {
return nil
}
func (s *fakeHTTPStream) Headers() http.Header {
return s.headers
}
func (s *fakeHTTPStream) Identifier() uint32 {
return s.id
}

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -27,13 +43,12 @@ import (
"sync"
"time"
"k8s.io/klog/v2"
api "k8s.io/api/core/v1"
"github.com/containerd/containerd/v2/internal/cri/streamingserver/responsewriter"
api "github.com/containerd/containerd/v2/internal/cri/streamingserver/v1"
"github.com/containerd/log"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/httpstream/wsstream"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/endpoints/responsewriter"
)
const (
@@ -187,9 +202,9 @@ func (h *websocketStreamHandler) portForward(p *websocketStreamPair) {
defer p.dataStream.Close()
defer p.errorStream.Close()
klog.V(5).InfoS("Connection invoking forwarder.PortForward for port", "connection", h.conn, "port", p.port)
log.L.Infof("Connection invoking forwarder.PortForward for port connection port %v", p.port)
err := h.forwarder.PortForward(ctx, h.pod, h.uid, p.port, p.dataStream)
klog.V(5).InfoS("Connection done invoking forwarder.PortForward for port", "connection", h.conn, "port", p.port)
log.L.Infof("Connection done invoking forwarder.PortForward for port connection port %v", p.port)
if err != nil {
msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", p.port, h.pod, h.uid, err)

View File

@@ -0,0 +1,117 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 portforward
import (
"net/http"
"reflect"
"testing"
)
func TestV4Options(t *testing.T) {
tests := map[string]struct {
url string
websocket bool
expectedOpts *V4Options
expectedError string
}{
"non-ws request": {
url: "http://example.com",
expectedOpts: &V4Options{},
},
"missing port": {
url: "http://example.com",
websocket: true,
expectedError: `query parameter "port" is required`,
},
"unable to parse port": {
url: "http://example.com?port=abc",
websocket: true,
expectedError: `unable to parse "abc" as a port: strconv.ParseUint: parsing "abc": invalid syntax`,
},
"negative port": {
url: "http://example.com?port=-1",
websocket: true,
expectedError: `unable to parse "-1" as a port: strconv.ParseUint: parsing "-1": invalid syntax`,
},
"one port": {
url: "http://example.com?port=80",
websocket: true,
expectedOpts: &V4Options{
Ports: []int32{80},
},
},
"multiple ports": {
url: "http://example.com?port=80,90,100",
websocket: true,
expectedOpts: &V4Options{
Ports: []int32{80, 90, 100},
},
},
"multiple port": {
url: "http://example.com?port=80&port=90",
websocket: true,
expectedOpts: &V4Options{
Ports: []int32{80, 90},
},
},
}
for name, test := range tests {
req, err := http.NewRequest(http.MethodGet, test.url, nil)
if err != nil {
t.Errorf("%s: invalid url %q err=%q", name, test.url, err)
continue
}
if test.websocket {
req.Header.Set("Connection", "Upgrade")
req.Header.Set("Upgrade", "websocket")
}
opts, err := NewV4Options(req)
if len(test.expectedError) > 0 {
if err == nil {
t.Errorf("%s: expected err=%q, but it was nil", name, test.expectedError)
}
if e, a := test.expectedError, err.Error(); e != a {
t.Errorf("%s: expected err=%q, got %q", name, e, a)
}
continue
}
if err != nil {
t.Errorf("%s: unexpected error %v", name, err)
continue
}
if !reflect.DeepEqual(test.expectedOpts, opts) {
t.Errorf("%s: expected options %#v, got %#v", name, test.expectedOpts, err)
}
}
}

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -17,6 +33,7 @@ limitations under the License.
package remotecommand
import (
"context"
"encoding/json"
"errors"
"fmt"
@@ -24,7 +41,8 @@ import (
"net/http"
"time"
api "k8s.io/api/core/v1"
api "github.com/containerd/containerd/v2/internal/cri/streamingserver/v1"
"github.com/containerd/log"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/httpstream"
@@ -33,8 +51,6 @@ import (
remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/klog/v2"
)
// Options contains details about which streams are required for
@@ -54,7 +70,7 @@ func NewOptions(req *http.Request) (*Options, error) {
stderr := req.FormValue(api.ExecStderrParam) == "1"
if tty && stderr {
// TODO: make this an error before we reach this method
klog.V(4).InfoS("Access to exec with tty and stderr is not supported, bypassing stderr")
log.L.Info("Access to exec with tty and stderr is not supported, bypassing stderr")
stderr = false
}
@@ -116,7 +132,7 @@ func createStreams(req *http.Request, w http.ResponseWriter, opts *Options, supp
if ctx.resizeStream != nil {
ctx.resizeChan = make(chan remotecommand.TerminalSize)
go handleResizeEvents(ctx.resizeStream, ctx.resizeChan)
go handleResizeEvents(req.Context(), ctx.resizeStream, ctx.resizeChan)
}
return ctx, true
@@ -155,7 +171,7 @@ func createHTTPStreamStreams(req *http.Request, w http.ResponseWriter, opts *Opt
case remotecommandconsts.StreamProtocolV2Name:
handler = &v2ProtocolHandler{}
case "":
klog.V(4).InfoS("Client did not request protocol negotiation. Falling back", "protocol", remotecommandconsts.StreamProtocolV1Name)
log.L.Infof("Client did not request protocol negotiation. Falling back protocol %v", remotecommandconsts.StreamProtocolV1Name)
fallthrough
case remotecommandconsts.StreamProtocolV1Name:
handler = &v1ProtocolHandler{}
@@ -409,7 +425,7 @@ WaitForStreams:
// supportsTerminalResizing returns false because v1ProtocolHandler doesn't support it.
func (*v1ProtocolHandler) supportsTerminalResizing() bool { return false }
func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalSize) {
func handleResizeEvents(reqctx context.Context, stream io.Reader, channel chan<- remotecommand.TerminalSize) {
defer runtime.HandleCrash()
defer close(channel)
@@ -419,7 +435,12 @@ func handleResizeEvents(stream io.Reader, channel chan<- remotecommand.TerminalS
if err := decoder.Decode(&size); err != nil {
break
}
channel <- size
select {
case channel <- size:
case <-reqctx.Done():
// To prevent go routine leak.
return
}
}
}

View File

@@ -0,0 +1,98 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2025 The Kubernetes 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 remotecommand
import (
"bytes"
"context"
"encoding/json"
"io"
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"k8s.io/client-go/tools/remotecommand"
)
func TestHandleResizeEvents(t *testing.T) {
var testTerminalSize remotecommand.TerminalSize
rawTerminalSize, err := json.Marshal(&testTerminalSize)
require.NoError(t, err)
testCases := []struct {
name string
resizeStreamData []byte
cancelContext bool
readFromChannel bool
}{
{
name: "data attempted to be sent on the channel; channel not read; context canceled",
resizeStreamData: rawTerminalSize,
cancelContext: true,
},
{
name: "data attempted to be sent on the channel; channel read; context not canceled",
resizeStreamData: rawTerminalSize,
readFromChannel: true,
},
{
name: "no data attempted to be sent on the channel; context canceled",
cancelContext: true,
},
{
name: "no data attempted to be sent on the channel; context not canceled",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
connCtx := connectionContext{
resizeStream: io.NopCloser(bytes.NewReader(testCase.resizeStreamData)),
resizeChan: make(chan remotecommand.TerminalSize),
}
go handleResizeEvents(ctx, connCtx.resizeStream, connCtx.resizeChan)
if testCase.readFromChannel {
gotTerminalSize := <-connCtx.resizeChan
require.Equal(t, gotTerminalSize, testTerminalSize)
}
if testCase.cancelContext {
cancel()
}
goleak.VerifyNone(t)
cancel()
})
}
}

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -21,9 +37,9 @@ import (
"net/http"
"time"
"github.com/containerd/containerd/v2/internal/cri/streamingserver/responsewriter"
"k8s.io/apimachinery/pkg/util/httpstream/wsstream"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/endpoints/responsewriter"
)
const (

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -14,7 +30,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package streaming
package streamingserver
import (
"container/list"
@@ -25,7 +41,7 @@ import (
"sync"
"time"
"k8s.io/utils/clock"
"github.com/containerd/containerd/v2/internal/cri/clock"
)
var (

View File

@@ -0,0 +1,238 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 streamingserver
import (
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
testingclock "github.com/containerd/containerd/v2/internal/cri/clock/testing"
)
func TestInsert(t *testing.T) {
c, _ := newTestCache()
// Insert normal
oldestTok, err := c.Insert(nextRequest())
require.NoError(t, err)
assert.Len(t, oldestTok, tokenLen)
assertCacheSize(t, c, 1)
// Insert until full
for i := 0; i < maxInFlight-2; i++ {
tok, err := c.Insert(nextRequest())
require.NoError(t, err)
assert.Len(t, tok, tokenLen)
}
assertCacheSize(t, c, maxInFlight-1)
newestReq := nextRequest()
newestTok, err := c.Insert(newestReq)
require.NoError(t, err)
assert.Len(t, newestTok, tokenLen)
assertCacheSize(t, c, maxInFlight)
require.Contains(t, c.tokens, oldestTok, "oldest request should still be cached")
// Consume newest token.
req, ok := c.Consume(newestTok)
assert.True(t, ok, "newest request should still be cached")
assert.Equal(t, newestReq, req)
require.Contains(t, c.tokens, oldestTok, "oldest request should still be cached")
// Insert again (still full)
tok, err := c.Insert(nextRequest())
require.NoError(t, err)
assert.Len(t, tok, tokenLen)
assertCacheSize(t, c, maxInFlight)
// Insert again (should evict)
_, err = c.Insert(nextRequest())
assert.Error(t, err, "should reject further requests")
recorder := httptest.NewRecorder()
require.NoError(t, WriteError(err, recorder))
errResponse := recorder.Result()
assert.Equal(t, http.StatusTooManyRequests, errResponse.StatusCode)
assert.Equal(t, strconv.Itoa(int(cacheTTL.Seconds())), errResponse.Header.Get("Retry-After"))
assertCacheSize(t, c, maxInFlight)
_, ok = c.Consume(oldestTok)
assert.True(t, ok, "oldest request should be valid")
}
func TestConsume(t *testing.T) {
c, clock := newTestCache()
{ // Insert & consume.
req := nextRequest()
tok, err := c.Insert(req)
require.NoError(t, err)
assertCacheSize(t, c, 1)
cachedReq, ok := c.Consume(tok)
assert.True(t, ok)
assert.Equal(t, req, cachedReq)
assertCacheSize(t, c, 0)
}
{ // Insert & consume out of order
req1 := nextRequest()
tok1, err := c.Insert(req1)
require.NoError(t, err)
assertCacheSize(t, c, 1)
req2 := nextRequest()
tok2, err := c.Insert(req2)
require.NoError(t, err)
assertCacheSize(t, c, 2)
cachedReq2, ok := c.Consume(tok2)
assert.True(t, ok)
assert.Equal(t, req2, cachedReq2)
assertCacheSize(t, c, 1)
cachedReq1, ok := c.Consume(tok1)
assert.True(t, ok)
assert.Equal(t, req1, cachedReq1)
assertCacheSize(t, c, 0)
}
{ // Consume a second time
req := nextRequest()
tok, err := c.Insert(req)
require.NoError(t, err)
assertCacheSize(t, c, 1)
cachedReq, ok := c.Consume(tok)
assert.True(t, ok)
assert.Equal(t, req, cachedReq)
assertCacheSize(t, c, 0)
_, ok = c.Consume(tok)
assert.False(t, ok)
assertCacheSize(t, c, 0)
}
{ // Consume without insert
_, ok := c.Consume("fooBAR")
assert.False(t, ok)
assertCacheSize(t, c, 0)
}
{ // Consume expired
tok, err := c.Insert(nextRequest())
require.NoError(t, err)
assertCacheSize(t, c, 1)
clock.Step(2 * cacheTTL)
_, ok := c.Consume(tok)
assert.False(t, ok)
assertCacheSize(t, c, 0)
}
}
func TestGC(t *testing.T) {
c, clock := newTestCache()
// When empty
c.gc()
assertCacheSize(t, c, 0)
tok1, err := c.Insert(nextRequest())
require.NoError(t, err)
assertCacheSize(t, c, 1)
clock.Step(10 * time.Second)
tok2, err := c.Insert(nextRequest())
require.NoError(t, err)
assertCacheSize(t, c, 2)
// expired: tok1, tok2
// non-expired: tok3, tok4
clock.Step(2 * cacheTTL)
tok3, err := c.Insert(nextRequest())
require.NoError(t, err)
assertCacheSize(t, c, 1)
clock.Step(10 * time.Second)
tok4, err := c.Insert(nextRequest())
require.NoError(t, err)
assertCacheSize(t, c, 2)
_, ok := c.Consume(tok1)
assert.False(t, ok)
_, ok = c.Consume(tok2)
assert.False(t, ok)
_, ok = c.Consume(tok3)
assert.True(t, ok)
_, ok = c.Consume(tok4)
assert.True(t, ok)
// When full, nothing is expired.
for i := 0; i < maxInFlight; i++ {
_, err := c.Insert(nextRequest())
require.NoError(t, err)
}
assertCacheSize(t, c, maxInFlight)
// When everything is expired
clock.Step(2 * cacheTTL)
_, err = c.Insert(nextRequest())
require.NoError(t, err)
assertCacheSize(t, c, 1)
}
func newTestCache() (*requestCache, *testingclock.FakeClock) {
c := newRequestCache()
fakeClock := testingclock.NewFakeClock(time.Now())
c.clock = fakeClock
return c, fakeClock
}
func assertCacheSize(t *testing.T, cache *requestCache, expectedSize int) {
tokenLen := len(cache.tokens)
llLen := cache.ll.Len()
assert.Equal(t, tokenLen, llLen, "inconsistent cache size! len(tokens)=%d; len(ll)=%d", tokenLen, llLen)
assert.Equal(t, expectedSize, tokenLen, "unexpected cache size!")
}
var requestUID = 0
func nextRequest() interface{} {
requestUID++
return requestUID
}

View File

@@ -0,0 +1,67 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2021 The Kubernetes 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 responsewriter
import (
"net/http"
)
// UserProvidedDecorator represensts a user (client that uses this package)
// provided decorator that wraps an inner http.ResponseWriter object.
// The user-provided decorator object must return the inner (decorated)
// http.ResponseWriter object via the Unwrap function.
type UserProvidedDecorator interface {
http.ResponseWriter
// Unwrap returns the inner http.ResponseWriter object associated
// with the user-provided decorator.
Unwrap() http.ResponseWriter
}
// GetOriginal goes through the chain of wrapped http.ResponseWriter objects
// and returns the original http.ResponseWriter object provided to the first
// request handler in the filter chain.
func GetOriginal(w http.ResponseWriter) http.ResponseWriter {
decorator, ok := w.(UserProvidedDecorator)
if !ok {
return w
}
inner := decorator.Unwrap()
if inner == w {
// infinite cycle here, we should never be here though.
panic("http.ResponseWriter decorator chain has a cycle")
}
return GetOriginal(inner)
}

View File

@@ -1,3 +1,19 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes Authors.
@@ -14,7 +30,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package streaming
package streamingserver
import (
"context"
@@ -32,12 +48,12 @@ import (
restful "github.com/emicklei/go-restful/v3"
"github.com/containerd/containerd/v2/internal/cri/streamingserver/portforward"
remotecommandserver "github.com/containerd/containerd/v2/internal/cri/streamingserver/remotecommand"
"k8s.io/apimachinery/pkg/types"
remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
"k8s.io/client-go/tools/remotecommand"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubelet/pkg/cri/streaming/portforward"
remotecommandserver "k8s.io/kubelet/pkg/cri/streaming/remotecommand"
)
// Server is the library interface to serve the stream requests.
@@ -146,9 +162,10 @@ func NewServer(config Config, runtime Runtime) (Server, error) {
handler.Add(ws)
s.handler = handler
s.server = &http.Server{
Addr: s.config.Addr,
Handler: s.handler,
TLSConfig: s.config.TLSConfig,
Addr: s.config.Addr,
Handler: s.handler,
TLSConfig: s.config.TLSConfig,
ReadHeaderTimeout: 0,
}
return s, nil

View File

@@ -0,0 +1,490 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 streamingserver
import (
"context"
"crypto/tls"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
kubeletportforward "github.com/containerd/containerd/v2/internal/cri/streamingserver/portforward"
api "github.com/containerd/containerd/v2/internal/cri/streamingserver/v1"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/client-go/transport/spdy"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
)
const (
testAddr = "localhost:12345"
testContainerID = "container789"
testPodSandboxID = "pod0987"
)
func TestGetExec(t *testing.T) {
serv, err := NewServer(Config{
Addr: testAddr,
}, nil)
assert.NoError(t, err)
tlsServer, err := NewServer(Config{
Addr: testAddr,
TLSConfig: &tls.Config{},
}, nil)
assert.NoError(t, err)
const pathPrefix = "cri/shim"
prefixServer, err := NewServer(Config{
Addr: testAddr,
BaseURL: &url.URL{
Scheme: "http",
Host: testAddr,
Path: "/" + pathPrefix + "/",
},
}, nil)
assert.NoError(t, err)
assertRequestToken := func(expectedReq *runtimeapi.ExecRequest, cache *requestCache, token string) {
req, ok := cache.Consume(token)
require.True(t, ok, "token %s not found!", token)
assert.Equal(t, expectedReq, req)
}
request := &runtimeapi.ExecRequest{
ContainerId: testContainerID,
Cmd: []string{"echo", "foo"},
Tty: true,
Stdin: true,
}
{ // Non-TLS
resp, err := serv.GetExec(request)
assert.NoError(t, err)
expectedURL := "http://" + testAddr + "/exec/"
assert.Contains(t, resp.Url, expectedURL)
token := strings.TrimPrefix(resp.Url, expectedURL)
assertRequestToken(request, serv.(*server).cache, token)
}
{ // TLS
resp, err := tlsServer.GetExec(request)
assert.NoError(t, err)
expectedURL := "https://" + testAddr + "/exec/"
assert.Contains(t, resp.Url, expectedURL)
token := strings.TrimPrefix(resp.Url, expectedURL)
assertRequestToken(request, tlsServer.(*server).cache, token)
}
{ // Path prefix
resp, err := prefixServer.GetExec(request)
assert.NoError(t, err)
expectedURL := "http://" + testAddr + "/" + pathPrefix + "/exec/"
assert.Contains(t, resp.Url, expectedURL)
token := strings.TrimPrefix(resp.Url, expectedURL)
assertRequestToken(request, prefixServer.(*server).cache, token)
}
}
func TestValidateExecAttachRequest(t *testing.T) {
type config struct {
tty bool
stdin bool
stdout bool
stderr bool
}
for _, tc := range []struct {
desc string
configs []config
expectErr bool
}{
{
desc: "at least one stream must be true",
expectErr: true,
configs: []config{
{false, false, false, false},
{true, false, false, false}},
},
{
desc: "tty and stderr cannot both be true",
expectErr: true,
configs: []config{
{true, false, false, true},
{true, false, true, true},
{true, true, false, true},
{true, true, true, true},
},
},
{
desc: "a valid config should pass",
expectErr: false,
configs: []config{
{false, false, false, true},
{false, false, true, false},
{false, false, true, true},
{false, true, false, false},
{false, true, false, true},
{false, true, true, false},
{false, true, true, true},
{true, false, true, false},
{true, true, false, false},
{true, true, true, false},
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
for _, c := range tc.configs {
// validate the exec request.
execReq := &runtimeapi.ExecRequest{
ContainerId: testContainerID,
Cmd: []string{"date"},
Tty: c.tty,
Stdin: c.stdin,
Stdout: c.stdout,
Stderr: c.stderr,
}
err := validateExecRequest(execReq)
assert.Equal(t, tc.expectErr, err != nil, "config: %v, err: %v", c, err)
// validate the attach request.
attachReq := &runtimeapi.AttachRequest{
ContainerId: testContainerID,
Tty: c.tty,
Stdin: c.stdin,
Stdout: c.stdout,
Stderr: c.stderr,
}
err = validateAttachRequest(attachReq)
assert.Equal(t, tc.expectErr, err != nil, "config: %v, err: %v", c, err)
}
})
}
}
func TestGetAttach(t *testing.T) {
serv, err := NewServer(Config{
Addr: testAddr,
}, nil)
require.NoError(t, err)
tlsServer, err := NewServer(Config{
Addr: testAddr,
TLSConfig: &tls.Config{},
}, nil)
require.NoError(t, err)
assertRequestToken := func(expectedReq *runtimeapi.AttachRequest, cache *requestCache, token string) {
req, ok := cache.Consume(token)
require.True(t, ok, "token %s not found!", token)
assert.Equal(t, expectedReq, req)
}
request := &runtimeapi.AttachRequest{
ContainerId: testContainerID,
Stdin: true,
Tty: true,
}
{ // Non-TLS
resp, err := serv.GetAttach(request)
assert.NoError(t, err)
expectedURL := "http://" + testAddr + "/attach/"
assert.Contains(t, resp.Url, expectedURL)
token := strings.TrimPrefix(resp.Url, expectedURL)
assertRequestToken(request, serv.(*server).cache, token)
}
{ // TLS
resp, err := tlsServer.GetAttach(request)
assert.NoError(t, err)
expectedURL := "https://" + testAddr + "/attach/"
assert.Contains(t, resp.Url, expectedURL)
token := strings.TrimPrefix(resp.Url, expectedURL)
assertRequestToken(request, tlsServer.(*server).cache, token)
}
}
func TestGetPortForward(t *testing.T) {
podSandboxID := testPodSandboxID
request := &runtimeapi.PortForwardRequest{
PodSandboxId: podSandboxID,
Port: []int32{1, 2, 3, 4},
}
{ // Non-TLS
serv, err := NewServer(Config{
Addr: testAddr,
}, nil)
assert.NoError(t, err)
resp, err := serv.GetPortForward(request)
assert.NoError(t, err)
expectedURL := "http://" + testAddr + "/portforward/"
assert.True(t, strings.HasPrefix(resp.Url, expectedURL))
token := strings.TrimPrefix(resp.Url, expectedURL)
req, ok := serv.(*server).cache.Consume(token)
require.True(t, ok, "token %s not found!", token)
assert.Equal(t, testPodSandboxID, req.(*runtimeapi.PortForwardRequest).PodSandboxId)
}
{ // TLS
tlsServer, err := NewServer(Config{
Addr: testAddr,
TLSConfig: &tls.Config{},
}, nil)
assert.NoError(t, err)
resp, err := tlsServer.GetPortForward(request)
assert.NoError(t, err)
expectedURL := "https://" + testAddr + "/portforward/"
assert.True(t, strings.HasPrefix(resp.Url, expectedURL))
token := strings.TrimPrefix(resp.Url, expectedURL)
req, ok := tlsServer.(*server).cache.Consume(token)
require.True(t, ok, "token %s not found!", token)
assert.Equal(t, testPodSandboxID, req.(*runtimeapi.PortForwardRequest).PodSandboxId)
}
}
func TestServeExec(t *testing.T) {
runRemoteCommandTest(t, "exec")
}
func TestServeAttach(t *testing.T) {
runRemoteCommandTest(t, "attach")
}
func TestServePortForward(t *testing.T) {
s, testServer := startTestServer(t)
defer testServer.Close()
resp, err := s.GetPortForward(&runtimeapi.PortForwardRequest{
PodSandboxId: testPodSandboxID,
})
require.NoError(t, err)
reqURL, err := url.Parse(resp.Url)
require.NoError(t, err)
transport, upgrader, err := spdy.RoundTripperFor(&restclient.Config{})
require.NoError(t, err)
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", reqURL)
streamConn, _, err := dialer.Dial(kubeletportforward.ProtocolV1Name)
require.NoError(t, err)
defer streamConn.Close()
// Create the streams.
headers := http.Header{}
// Error stream is required, but unused in this test.
headers.Set(api.StreamType, api.StreamTypeError)
headers.Set(api.PortHeader, strconv.Itoa(testPort))
_, err = streamConn.CreateStream(headers)
require.NoError(t, err)
// Setup the data stream.
headers.Set(api.StreamType, api.StreamTypeData)
headers.Set(api.PortHeader, strconv.Itoa(testPort))
stream, err := streamConn.CreateStream(headers)
require.NoError(t, err)
doClientStreams(t, "portforward", stream, stream, nil)
}
// Run the remote command test.
// commandType is either "exec" or "attach".
func runRemoteCommandTest(t *testing.T, commandType string) {
s, testServer := startTestServer(t)
defer testServer.Close()
var reqURL *url.URL
stdin, stdout, stderr := true, true, true
containerID := testContainerID
switch commandType {
case "exec":
resp, err := s.GetExec(&runtimeapi.ExecRequest{
ContainerId: containerID,
Cmd: []string{"echo"},
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
})
require.NoError(t, err)
reqURL, err = url.Parse(resp.Url)
require.NoError(t, err)
case "attach":
resp, err := s.GetAttach(&runtimeapi.AttachRequest{
ContainerId: containerID,
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
})
require.NoError(t, err)
reqURL, err = url.Parse(resp.Url)
require.NoError(t, err)
}
wg := sync.WaitGroup{}
wg.Add(2)
stdinR, stdinW := io.Pipe()
stdoutR, stdoutW := io.Pipe()
stderrR, stderrW := io.Pipe()
go func() {
defer wg.Done()
exec, err := remotecommand.NewSPDYExecutor(&restclient.Config{}, "POST", reqURL)
if err != nil {
t.Errorf("unexpected error %v", err)
return
}
opts := remotecommand.StreamOptions{
Stdin: stdinR,
Stdout: stdoutW,
Stderr: stderrW,
Tty: false,
}
if err = exec.StreamWithContext(context.Background(), opts); err != nil {
t.Errorf("unexpected error %v", err)
}
}()
go func() {
defer wg.Done()
doClientStreams(t, commandType, stdinW, stdoutR, stderrR)
}()
wg.Wait()
// Repeat request with the same URL should be a 404.
resp, err := http.Get(reqURL.String())
require.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
}
func startTestServer(t *testing.T) (Server, *httptest.Server) {
var s Server
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s.ServeHTTP(w, r)
}))
cleanup := true
defer func() {
if cleanup {
testServer.Close()
}
}()
testURL, err := url.Parse(testServer.URL)
require.NoError(t, err)
rt := newFakeRuntime(t)
config := DefaultConfig
config.BaseURL = testURL
s, err = NewServer(config, rt)
require.NoError(t, err)
cleanup = false // Caller must close the test server.
return s, testServer
}
const (
testInput = "abcdefg"
testOutput = "fooBARbaz"
testErr = "ERROR!!!"
testPort = 12345
)
func newFakeRuntime(t *testing.T) *fakeRuntime {
return &fakeRuntime{
t: t,
}
}
type fakeRuntime struct {
t *testing.T
}
func (f *fakeRuntime) Exec(_ context.Context, containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
assert.Equal(f.t, testContainerID, containerID)
doServerStreams(f.t, "exec", stdin, stdout, stderr)
return nil
}
func (f *fakeRuntime) Attach(_ context.Context, containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
assert.Equal(f.t, testContainerID, containerID)
doServerStreams(f.t, "attach", stdin, stdout, stderr)
return nil
}
func (f *fakeRuntime) PortForward(_ context.Context, podSandboxID string, port int32, stream io.ReadWriteCloser) error {
assert.Equal(f.t, testPodSandboxID, podSandboxID)
assert.EqualValues(f.t, testPort, port)
doServerStreams(f.t, "portforward", stream, stream, nil)
return nil
}
// Send & receive expected input/output. Must be the inverse of doClientStreams.
// Function will block until the expected i/o is finished.
func doServerStreams(t *testing.T, prefix string, stdin io.Reader, stdout, stderr io.Writer) {
if stderr != nil {
writeExpected(t, "server stderr", stderr, prefix+testErr)
}
readExpected(t, "server stdin", stdin, prefix+testInput)
writeExpected(t, "server stdout", stdout, prefix+testOutput)
}
// Send & receive expected input/output. Must be the inverse of doServerStreams.
// Function will block until the expected i/o is finished.
func doClientStreams(t *testing.T, prefix string, stdin io.Writer, stdout, stderr io.Reader) {
if stderr != nil {
readExpected(t, "client stderr", stderr, prefix+testErr)
}
writeExpected(t, "client stdin", stdin, prefix+testInput)
readExpected(t, "client stdout", stdout, prefix+testOutput)
}
// Read and verify the expected string from the stream.
func readExpected(t *testing.T, streamName string, r io.Reader, expected string) {
result := make([]byte, len(expected))
_, err := io.ReadAtLeast(r, result, len(expected))
assert.NoError(t, err, "stream %s", streamName)
assert.Equal(t, expected, string(result), "stream %s", streamName)
}
// Write and verify success of the data over the stream.
func writeExpected(t *testing.T, streamName string, w io.Writer, data string) {
n, err := io.WriteString(w, data)
assert.NoError(t, err, "stream %s", streamName)
assert.Equal(t, len(data), n, "stream %s", streamName)
}

View File

@@ -0,0 +1,34 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 v1 contains types/constants for core types used by the streaming server command functions.
package v1 // import "k8s.io/api/core/v1"

View File

@@ -0,0 +1,67 @@
/*
Copyright The containerd 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.
*/
/*
Copyright 2016 The Kubernetes 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 v1
const (
// Enable stdin for remote command execution
ExecStdinParam = "input"
// Enable stdout for remote command execution
ExecStdoutParam = "output"
// Enable stderr for remote command execution
ExecStderrParam = "error"
// Enable TTY for remote command execution
ExecTTYParam = "tty"
// Command to run for remote command execution
ExecCommandParam = "command"
// Name of header that specifies stream type
StreamType = "streamType"
// Value for streamType header for stdin stream
StreamTypeStdin = "stdin"
// Value for streamType header for stdout stream
StreamTypeStdout = "stdout"
// Value for streamType header for stderr stream
StreamTypeStderr = "stderr"
// Value for streamType header for data stream
StreamTypeData = "data"
// Value for streamType header for error stream
StreamTypeError = "error"
// Value for streamType header for terminal resize stream
StreamTypeResize = "resize"
// Name of header that specifies the port being forwarded
PortHeader = "port"
// Name of header that specifies a request ID used to associate the error
// and data streams for a single forwarded connection
PortForwardRequestIDHeader = "requestID"
)

5
vendor/go.uber.org/goleak/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
vendor/
/bin
/lint.log
/cover.out
/cover.html

28
vendor/go.uber.org/goleak/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,28 @@
output:
# Make output more digestible with quickfix in vim/emacs/etc.
sort-results: true
print-issued-lines: false
linters:
enable:
- gofumpt
- nolintlint
- revive
linters-settings:
govet:
# These govet checks are disabled by default, but they're useful.
enable:
- niliness
- reflectvaluecompare
- sortslice
- unusedwrite
issues:
# Print all issues reported by all linters.
max-issues-per-linter: 0
max-same-issues: 0
# Don't ignore some of the issues that golangci-lint considers okay.
# This includes documenting all exported entities.
exclude-use-default: false

74
vendor/go.uber.org/goleak/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,74 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.3.0]
### Fixed
- Built-in ignores now match function names more accurately.
They will no longer ignore stacks because of file names
that look similar to function names. (#112)
### Added
- Add an `IgnoreAnyFunction` option to ignore stack traces
that have the provided function anywhere in the stack. (#113)
- Ignore `testing.runFuzzing` and `testing.runFuzzTests` alongside
other already-ignored test functions (`testing.RunTests`, etc). (#105)
### Changed
- Miscellaneous CI-related fixes. (#103, #108, #114)
[1.3.0]: https://github.com/uber-go/goleak/compare/v1.2.1...v1.3.0
## [1.2.1]
### Changed
- Drop golang/x/lint dependency.
[1.2.1]: https://github.com/uber-go/goleak/compare/v1.2.0...v1.2.1
## [1.2.0]
### Added
- Add Cleanup option that can be used for registering cleanup callbacks. (#78)
### Changed
- Mark VerifyNone as a test helper. (#75)
Thanks to @tallclair for their contribution to this release.
[1.2.0]: https://github.com/uber-go/goleak/compare/v1.1.12...v1.2.0
## [1.1.12]
### Fixed
- Fixed logic for ignoring trace related goroutines on Go versions 1.16 and above.
[1.1.12]: https://github.com/uber-go/goleak/compare/v1.1.11...v1.1.12
## [1.1.11]
### Fixed
- Documentation fix on how to test.
- Update dependency on stretchr/testify to v1.7.0. (#59)
- Update dependency on golang.org/x/tools to address CVE-2020-14040. (#62)
[1.1.11]: https://github.com/uber-go/goleak/compare/v1.1.10...v1.1.11
## [1.1.10]
### Added
- [#49]: Add option to ignore current goroutines, which checks for any additional leaks and allows for incremental adoption of goleak in larger projects.
Thanks to @denis-tingajkin for their contributions to this release.
[#49]: https://github.com/uber-go/goleak/pull/49
[1.1.10]: https://github.com/uber-go/goleak/compare/v1.0.0...v1.1.10
## [1.0.0]
### Changed
- Migrate to Go modules.
### Fixed
- Ignore trace related goroutines that cause false positives with -trace.
[1.0.0]: https://github.com/uber-go/goleak/compare/v0.10.0...v1.0.0
## [0.10.0]
- Initial release.
[0.10.0]: https://github.com/uber-go/goleak/compare/v0.10.0...HEAD

21
vendor/go.uber.org/goleak/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Uber Technologies, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

45
vendor/go.uber.org/goleak/Makefile generated vendored Normal file
View File

@@ -0,0 +1,45 @@
# Directory containing the Makefile.
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
export GOBIN = $(PROJECT_ROOT)/bin
export PATH := $(GOBIN):$(PATH)
GO_FILES = $(shell find . \
-path '*/.*' -prune -o \
'(' -type f -a -name '*.go' ')' -print)
# Additional test flags.
TEST_FLAGS ?=
.PHONY: all
all: lint build test
.PHONY: lint
lint: golangci-lint tidy-lint
.PHONY: build
build:
go build ./...
.PHONY: test
test:
go test -v -race ./...
go test -v -trace=/dev/null .
.PHONY: cover
cover:
go test -race -coverprofile=cover.out -coverpkg=./... ./...
go tool cover -html=cover.out -o cover.html
.PHONY: golangci-lint
golangci-lint:
golangci-lint run
.PHONY: tidy
tidy:
go mod tidy
.PHONY: tidy-lint
tidy-lint:
go mod tidy
git diff --exit-code -- go.mod go.sum

74
vendor/go.uber.org/goleak/README.md generated vendored Normal file
View File

@@ -0,0 +1,74 @@
# goleak [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
Goroutine leak detector to help avoid Goroutine leaks.
## Installation
You can use `go get` to get the latest version:
`go get -u go.uber.org/goleak`
`goleak` also supports semver releases.
Note that go-leak only [supports][release] the two most recent minor versions of Go.
## Quick Start
To verify that there are no unexpected goroutines running at the end of a test:
```go
func TestA(t *testing.T) {
defer goleak.VerifyNone(t)
// test logic here.
}
```
Instead of checking for leaks at the end of every test, `goleak` can also be run
at the end of every test package by creating a `TestMain` function for your
package:
```go
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
```
## Determine Source of Package Leaks
When verifying leaks using `TestMain`, the leak test is only run once after all tests
have been run. This is typically enough to ensure there's no goroutines leaked from
tests, but when there are leaks, it's hard to determine which test is causing them.
You can use the following bash script to determine the source of the failing test:
```sh
# Create a test binary which will be used to run each test individually
$ go test -c -o tests
# Run each test individually, printing "." for successful tests, or the test name
# for failing tests.
$ for test in $(go test -list . | grep -E "^(Test|Example)"); do ./tests -test.run "^$test\$" &>/dev/null && echo -n "." || echo -e "\n$test failed"; done
```
This will only print names of failing tests which can be investigated individually. E.g.,
```
.....
TestLeakyTest failed
.......
```
## Stability
goleak is v1 and follows [SemVer](http://semver.org/) strictly.
No breaking changes will be made to exported APIs before 2.0.
[doc-img]: https://godoc.org/go.uber.org/goleak?status.svg
[doc]: https://godoc.org/go.uber.org/goleak
[ci-img]: https://github.com/uber-go/goleak/actions/workflows/ci.yml/badge.svg
[ci]: https://github.com/uber-go/goleak/actions/workflows/ci.yml
[cov-img]: https://codecov.io/gh/uber-go/goleak/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/goleak
[release]: https://go.dev/doc/devel/release#policy

22
vendor/go.uber.org/goleak/doc.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (c) 2018 Uber Technologies, Inc.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Package goleak is a Goroutine leak detector.
package goleak // import "go.uber.org/goleak"

22
vendor/go.uber.org/goleak/internal/stack/doc.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (c) 2017-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Package stack is used for parsing stacks from `runtime.Stack`.
package stack

56
vendor/go.uber.org/goleak/internal/stack/scan.go generated vendored Normal file
View File

@@ -0,0 +1,56 @@
// Copyright (c) 2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package stack
import (
"bufio"
"io"
)
// scanner provides a bufio.Scanner the ability to Unscan,
// which allows the current token to be read again
// after the next Scan.
type scanner struct {
*bufio.Scanner
unscanned bool
}
func newScanner(r io.Reader) *scanner {
return &scanner{Scanner: bufio.NewScanner(r)}
}
func (s *scanner) Scan() bool {
if s.unscanned {
s.unscanned = false
return true
}
return s.Scanner.Scan()
}
// Unscan stops the scanner from advancing its position
// for the next Scan.
//
// Bytes and Text will return the same token after next Scan
// that they do right now.
func (s *scanner) Unscan() {
s.unscanned = true
}

298
vendor/go.uber.org/goleak/internal/stack/stacks.go generated vendored Normal file
View File

@@ -0,0 +1,298 @@
// Copyright (c) 2017-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package stack
import (
"bytes"
"errors"
"fmt"
"io"
"runtime"
"strconv"
"strings"
)
const _defaultBufferSize = 64 * 1024 // 64 KiB
// Stack represents a single Goroutine's stack.
type Stack struct {
id int
state string // e.g. 'running', 'chan receive'
// The first function on the stack.
firstFunction string
// A set of all functions in the stack,
allFunctions map[string]struct{}
// Full, raw stack trace.
fullStack string
}
// ID returns the goroutine ID.
func (s Stack) ID() int {
return s.id
}
// State returns the Goroutine's state.
func (s Stack) State() string {
return s.state
}
// Full returns the full stack trace for this goroutine.
func (s Stack) Full() string {
return s.fullStack
}
// FirstFunction returns the name of the first function on the stack.
func (s Stack) FirstFunction() string {
return s.firstFunction
}
// HasFunction reports whether the stack has the given function
// anywhere in it.
func (s Stack) HasFunction(name string) bool {
_, ok := s.allFunctions[name]
return ok
}
func (s Stack) String() string {
return fmt.Sprintf(
"Goroutine %v in state %v, with %v on top of the stack:\n%s",
s.id, s.state, s.firstFunction, s.Full())
}
func getStacks(all bool) []Stack {
trace := getStackBuffer(all)
stacks, err := newStackParser(bytes.NewReader(trace)).Parse()
if err != nil {
// Well-formed stack traces should never fail to parse.
// If they do, it's a bug in this package.
// Panic so we can fix it.
panic(fmt.Sprintf("Failed to parse stack trace: %v\n%s", err, trace))
}
return stacks
}
type stackParser struct {
scan *scanner
stacks []Stack
errors []error
}
func newStackParser(r io.Reader) *stackParser {
return &stackParser{
scan: newScanner(r),
}
}
func (p *stackParser) Parse() ([]Stack, error) {
for p.scan.Scan() {
line := p.scan.Text()
// If we see the goroutine header, start a new stack.
if strings.HasPrefix(line, "goroutine ") {
stack, err := p.parseStack(line)
if err != nil {
p.errors = append(p.errors, err)
continue
}
p.stacks = append(p.stacks, stack)
}
}
p.errors = append(p.errors, p.scan.Err())
return p.stacks, errors.Join(p.errors...)
}
// parseStack parses a single stack trace from the given scanner.
// line is the first line of the stack trace, which should look like:
//
// goroutine 123 [runnable]:
func (p *stackParser) parseStack(line string) (Stack, error) {
id, state, err := parseGoStackHeader(line)
if err != nil {
return Stack{}, fmt.Errorf("parse header: %w", err)
}
// Read the rest of the stack trace.
var (
firstFunction string
fullStack bytes.Buffer
)
funcs := make(map[string]struct{})
for p.scan.Scan() {
line := p.scan.Text()
if strings.HasPrefix(line, "goroutine ") {
// If we see the goroutine header,
// it's the end of this stack.
// Unscan so the next Scan sees the same line.
p.scan.Unscan()
break
}
fullStack.WriteString(line)
fullStack.WriteByte('\n') // scanner trims the newline
if len(line) == 0 {
// Empty line usually marks the end of the stack
// but we don't want to have to rely on that.
// Just skip it.
continue
}
funcName, creator, err := parseFuncName(line)
if err != nil {
return Stack{}, fmt.Errorf("parse function: %w", err)
}
if !creator {
// A function is part of a goroutine's stack
// only if it's not a "created by" function.
//
// The creator function is part of a different stack.
// We don't care about it right now.
funcs[funcName] = struct{}{}
if firstFunction == "" {
firstFunction = funcName
}
}
// The function name followed by a line in the form:
//
// <tab>example.com/path/to/package/file.go:123 +0x123
//
// We don't care about the position so we can skip this line.
if p.scan.Scan() {
// Be defensive:
// Skip the line only if it starts with a tab.
bs := p.scan.Bytes()
if len(bs) > 0 && bs[0] == '\t' {
fullStack.Write(bs)
fullStack.WriteByte('\n')
} else {
// Put it back and let the next iteration handle it
// if it doesn't start with a tab.
p.scan.Unscan()
}
}
if creator {
// The "created by" line is the last line of the stack.
// We can stop parsing now.
//
// Note that if tracebackancestors=N is set,
// there may be more a traceback of the creator function
// following the "created by" line,
// but it should not be considered part of this stack.
// e.g.,
//
// created by testing.(*T).Run in goroutine 1
// /usr/lib/go/src/testing/testing.go:1648 +0x3ad
// [originating from goroutine 1]:
// testing.(*T).Run(...)
// /usr/lib/go/src/testing/testing.go:1649 +0x3ad
//
break
}
}
return Stack{
id: id,
state: state,
firstFunction: firstFunction,
allFunctions: funcs,
fullStack: fullStack.String(),
}, nil
}
// All returns the stacks for all running goroutines.
func All() []Stack {
return getStacks(true)
}
// Current returns the stack for the current goroutine.
func Current() Stack {
return getStacks(false)[0]
}
func getStackBuffer(all bool) []byte {
for i := _defaultBufferSize; ; i *= 2 {
buf := make([]byte, i)
if n := runtime.Stack(buf, all); n < i {
return buf[:n]
}
}
}
// Parses a single function from the given line.
// The line is in one of these formats:
//
// example.com/path/to/package.funcName(args...)
// example.com/path/to/package.(*typeName).funcName(args...)
// created by example.com/path/to/package.funcName
// created by example.com/path/to/package.funcName in goroutine [...]
//
// Also reports whether the line was a "created by" line.
func parseFuncName(line string) (name string, creator bool, err error) {
if after, ok := strings.CutPrefix(line, "created by "); ok {
// The function name is the part after "created by "
// and before " in goroutine [...]".
idx := strings.Index(after, " in goroutine")
if idx >= 0 {
after = after[:idx]
}
name = after
creator = true
} else if idx := strings.LastIndexByte(line, '('); idx >= 0 {
// The function name is the part before the last '('.
name = line[:idx]
}
if name == "" {
return "", false, fmt.Errorf("no function found: %q", line)
}
return name, creator, nil
}
// parseGoStackHeader parses a stack header that looks like:
// goroutine 643 [runnable]:\n
// And returns the goroutine ID, and the state.
func parseGoStackHeader(line string) (goroutineID int, state string, err error) {
// The scanner will have already trimmed the "\n",
// but we'll guard against it just in case.
//
// Trimming them separately makes them both optional.
line = strings.TrimSuffix(strings.TrimSuffix(line, ":"), "\n")
parts := strings.SplitN(line, " ", 3)
if len(parts) != 3 {
return 0, "", fmt.Errorf("unexpected format: %q", line)
}
id, err := strconv.Atoi(parts[1])
if err != nil {
return 0, "", fmt.Errorf("bad goroutine ID %q in line %q", parts[1], line)
}
state = strings.TrimSuffix(strings.TrimPrefix(parts[2], "["), "]")
return id, state, nil
}

108
vendor/go.uber.org/goleak/leaks.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (c) 2017 Uber Technologies, Inc.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package goleak
import (
"errors"
"fmt"
"go.uber.org/goleak/internal/stack"
)
// TestingT is the minimal subset of testing.TB that we use.
type TestingT interface {
Error(...interface{})
}
// filterStacks will filter any stacks excluded by the given opts.
// filterStacks modifies the passed in stacks slice.
func filterStacks(stacks []stack.Stack, skipID int, opts *opts) []stack.Stack {
filtered := stacks[:0]
for _, stack := range stacks {
// Always skip the running goroutine.
if stack.ID() == skipID {
continue
}
// Run any default or user-specified filters.
if opts.filter(stack) {
continue
}
filtered = append(filtered, stack)
}
return filtered
}
// Find looks for extra goroutines, and returns a descriptive error if
// any are found.
func Find(options ...Option) error {
cur := stack.Current().ID()
opts := buildOpts(options...)
if opts.cleanup != nil {
return errors.New("Cleanup can only be passed to VerifyNone or VerifyTestMain")
}
var stacks []stack.Stack
retry := true
for i := 0; retry; i++ {
stacks = filterStacks(stack.All(), cur, opts)
if len(stacks) == 0 {
return nil
}
retry = opts.retry(i)
}
return fmt.Errorf("found unexpected goroutines:\n%s", stacks)
}
type testHelper interface {
Helper()
}
// VerifyNone marks the given TestingT as failed if any extra goroutines are
// found by Find. This is a helper method to make it easier to integrate in
// tests by doing:
//
// defer VerifyNone(t)
//
// VerifyNone is currently incompatible with t.Parallel because it cannot
// associate specific goroutines with specific tests. Thus, non-leaking
// goroutines from other tests running in parallel could fail this check.
// If you need to run tests in parallel, use [VerifyTestMain] instead,
// which will verify that no leaking goroutines exist after ALL tests finish.
func VerifyNone(t TestingT, options ...Option) {
opts := buildOpts(options...)
var cleanup func(int)
cleanup, opts.cleanup = opts.cleanup, nil
if h, ok := t.(testHelper); ok {
// Mark this function as a test helper, if available.
h.Helper()
}
if err := Find(opts); err != nil {
t.Error(err)
}
if cleanup != nil {
cleanup(0)
}
}

198
vendor/go.uber.org/goleak/options.go generated vendored Normal file
View File

@@ -0,0 +1,198 @@
// Copyright (c) 2017-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package goleak
import (
"strings"
"time"
"go.uber.org/goleak/internal/stack"
)
// Option lets users specify custom verifications.
type Option interface {
apply(*opts)
}
// We retry up to 20 times if we can't find the goroutine that
// we are looking for. In between each attempt, we will sleep for
// a short while to let any running goroutines complete.
const _defaultRetries = 20
type opts struct {
filters []func(stack.Stack) bool
maxRetries int
maxSleep time.Duration
cleanup func(int)
}
// implement apply so that opts struct itself can be used as
// an Option.
func (o *opts) apply(opts *opts) {
opts.filters = o.filters
opts.maxRetries = o.maxRetries
opts.maxSleep = o.maxSleep
opts.cleanup = o.cleanup
}
// optionFunc lets us easily write options without a custom type.
type optionFunc func(*opts)
func (f optionFunc) apply(opts *opts) { f(opts) }
// IgnoreTopFunction ignores any goroutines where the specified function
// is at the top of the stack. The function name should be fully qualified,
// e.g., go.uber.org/goleak.IgnoreTopFunction
func IgnoreTopFunction(f string) Option {
return addFilter(func(s stack.Stack) bool {
return s.FirstFunction() == f
})
}
// IgnoreAnyFunction ignores goroutines where the specified function
// is present anywhere in the stack.
//
// The function name must be fully qualified, e.g.,
//
// go.uber.org/goleak.IgnoreAnyFunction
//
// For methods, the fully qualified form looks like:
//
// go.uber.org/goleak.(*MyType).MyMethod
func IgnoreAnyFunction(f string) Option {
return addFilter(func(s stack.Stack) bool {
return s.HasFunction(f)
})
}
// Cleanup sets up a cleanup function that will be executed at the
// end of the leak check.
// When passed to [VerifyTestMain], the exit code passed to cleanupFunc
// will be set to the exit code of TestMain.
// When passed to [VerifyNone], the exit code will be set to 0.
// This cannot be passed to [Find].
func Cleanup(cleanupFunc func(exitCode int)) Option {
return optionFunc(func(opts *opts) {
opts.cleanup = cleanupFunc
})
}
// IgnoreCurrent records all current goroutines when the option is created, and ignores
// them in any future Find/Verify calls.
func IgnoreCurrent() Option {
excludeIDSet := map[int]bool{}
for _, s := range stack.All() {
excludeIDSet[s.ID()] = true
}
return addFilter(func(s stack.Stack) bool {
return excludeIDSet[s.ID()]
})
}
func maxSleep(d time.Duration) Option {
return optionFunc(func(opts *opts) {
opts.maxSleep = d
})
}
func addFilter(f func(stack.Stack) bool) Option {
return optionFunc(func(opts *opts) {
opts.filters = append(opts.filters, f)
})
}
func buildOpts(options ...Option) *opts {
opts := &opts{
maxRetries: _defaultRetries,
maxSleep: 100 * time.Millisecond,
}
opts.filters = append(opts.filters,
isTestStack,
isSyscallStack,
isStdLibStack,
isTraceStack,
)
for _, option := range options {
option.apply(opts)
}
return opts
}
func (o *opts) filter(s stack.Stack) bool {
for _, filter := range o.filters {
if filter(s) {
return true
}
}
return false
}
func (o *opts) retry(i int) bool {
if i >= o.maxRetries {
return false
}
d := time.Duration(int(time.Microsecond) << uint(i))
if d > o.maxSleep {
d = o.maxSleep
}
time.Sleep(d)
return true
}
// isTestStack is a default filter installed to automatically skip goroutines
// that the testing package runs while the user's tests are running.
func isTestStack(s stack.Stack) bool {
// Until go1.7, the main goroutine ran RunTests, which started
// the test in a separate goroutine and waited for that test goroutine
// to end by waiting on a channel.
// Since go1.7, a separate goroutine is started to wait for signals.
// T.Parallel is for parallel tests, which are blocked until all serial
// tests have run with T.Parallel at the top of the stack.
// testing.runFuzzTests is for fuzz testing, it's blocked until the test
// function with all seed corpus have run.
// testing.runFuzzing is for fuzz testing, it's blocked until a failing
// input is found.
switch s.FirstFunction() {
case "testing.RunTests", "testing.(*T).Run", "testing.(*T).Parallel", "testing.runFuzzing", "testing.runFuzzTests":
// In pre1.7 and post-1.7, background goroutines started by the testing
// package are blocked waiting on a channel.
return strings.HasPrefix(s.State(), "chan receive")
}
return false
}
func isSyscallStack(s stack.Stack) bool {
// Typically runs in the background when code uses CGo:
// https://github.com/golang/go/issues/16714
return s.HasFunction("runtime.goexit") && strings.HasPrefix(s.State(), "syscall")
}
func isStdLibStack(s stack.Stack) bool {
// Importing os/signal starts a background goroutine.
// The name of the function at the top has changed between versions.
if f := s.FirstFunction(); f == "os/signal.signal_recv" || f == "os/signal.loop" {
return true
}
// Using signal.Notify will start a runtime goroutine.
return s.HasFunction("runtime.ensureSigM")
}

69
vendor/go.uber.org/goleak/testmain.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package goleak
import (
"fmt"
"io"
"os"
)
// Variables for stubbing in unit tests.
var (
_osExit = os.Exit
_osStderr io.Writer = os.Stderr
)
// TestingM is the minimal subset of testing.M that we use.
type TestingM interface {
Run() int
}
// VerifyTestMain can be used in a TestMain function for package tests to
// verify that there were no goroutine leaks.
// To use it, your TestMain function should look like:
//
// func TestMain(m *testing.M) {
// goleak.VerifyTestMain(m)
// }
//
// See https://golang.org/pkg/testing/#hdr-Main for more details.
//
// This will run all tests as per normal, and if they were successful, look
// for any goroutine leaks and fail the tests if any leaks were found.
func VerifyTestMain(m TestingM, options ...Option) {
exitCode := m.Run()
opts := buildOpts(options...)
var cleanup func(int)
cleanup, opts.cleanup = opts.cleanup, nil
if cleanup == nil {
cleanup = _osExit
}
defer func() { cleanup(exitCode) }()
if exitCode == 0 {
if err := Find(opts); err != nil {
fmt.Fprintf(_osStderr, "goleak: Errors on successful test run: %v\n", err)
exitCode = 1
}
}
}

30
vendor/go.uber.org/goleak/tracestack_new.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
// Copyright (c) 2021-2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//go:build go1.16
// +build go1.16
package goleak
import "go.uber.org/goleak/internal/stack"
func isTraceStack(s stack.Stack) bool {
return s.HasFunction("runtime.ReadTrace")
}

202
vendor/k8s.io/apiserver/LICENSE generated vendored
View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,54 +0,0 @@
/*
Copyright 2021 The Kubernetes 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 responsewriter
import (
"bufio"
"net"
"net/http"
)
var _ http.ResponseWriter = &FakeResponseWriter{}
// FakeResponseWriter implements http.ResponseWriter,
// it is used for testing purpose only
type FakeResponseWriter struct{}
func (fw *FakeResponseWriter) Header() http.Header { return http.Header{} }
func (fw *FakeResponseWriter) WriteHeader(code int) {}
func (fw *FakeResponseWriter) Write(bs []byte) (int, error) { return len(bs), nil }
// For HTTP2 an http.ResponseWriter object implements
// http.Flusher and http.CloseNotifier.
// It is used for testing purpose only
type FakeResponseWriterFlusherCloseNotifier struct {
*FakeResponseWriter
}
func (fw *FakeResponseWriterFlusherCloseNotifier) Flush() {}
func (fw *FakeResponseWriterFlusherCloseNotifier) CloseNotify() <-chan bool { return nil }
// For HTTP/1.x an http.ResponseWriter object implements
// http.Flusher, http.CloseNotifier and http.Hijacker.
// It is used for testing purpose only
type FakeResponseWriterFlusherCloseNotifierHijacker struct {
*FakeResponseWriterFlusherCloseNotifier
}
func (fw *FakeResponseWriterFlusherCloseNotifierHijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return nil, nil, nil
}

View File

@@ -1,180 +0,0 @@
/*
Copyright 2021 The Kubernetes 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 responsewriter
import (
"bufio"
"net"
"net/http"
)
// UserProvidedDecorator represensts a user (client that uses this package)
// provided decorator that wraps an inner http.ResponseWriter object.
// The user-provided decorator object must return the inner (decorated)
// http.ResponseWriter object via the Unwrap function.
type UserProvidedDecorator interface {
http.ResponseWriter
// Unwrap returns the inner http.ResponseWriter object associated
// with the user-provided decorator.
Unwrap() http.ResponseWriter
}
// WrapForHTTP1Or2 accepts a user-provided decorator of an "inner" http.responseWriter
// object and potentially wraps the user-provided decorator with a new http.ResponseWriter
// object that implements http.CloseNotifier, http.Flusher, and/or http.Hijacker by
// delegating to the user-provided decorator (if it implements the relevant method) or
// the inner http.ResponseWriter (otherwise), so that the returned http.ResponseWriter
// object implements the same subset of those interfaces as the inner http.ResponseWriter.
//
// This function handles the following three casses.
// - The inner ResponseWriter implements `http.CloseNotifier`, `http.Flusher`,
// and `http.Hijacker` (an HTTP/1.1 sever provides such a ResponseWriter).
// - The inner ResponseWriter implements `http.CloseNotifier` and `http.Flusher`
// but not `http.Hijacker` (an HTTP/2 server provides such a ResponseWriter).
// - All the other cases collapse to this one, in which the given ResponseWriter is returned.
//
// There are three applicable terms:
// - "outer": this is the ResponseWriter object returned by the WrapForHTTP1Or2 function.
// - "user-provided decorator" or "middle": this is the user-provided decorator
// that decorates an inner ResponseWriter object. A user-provided decorator
// implements the UserProvidedDecorator interface. A user-provided decorator
// may or may not implement http.CloseNotifier, http.Flusher or http.Hijacker.
// - "inner": the ResponseWriter that the user-provided decorator extends.
func WrapForHTTP1Or2(decorator UserProvidedDecorator) http.ResponseWriter {
// from go net/http documentation:
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations support Flusher
// Handlers should always test for this ability at runtime.
//
// The Hijacker interface is implemented by ResponseWriters that allow an HTTP handler
// to take over the connection.
// The default ResponseWriter for HTTP/1.x connections supports Hijacker, but HTTP/2 connections
// intentionally do not. ResponseWriter wrappers may also not support Hijacker.
// Handlers should always test for this ability at runtime
//
// The CloseNotifier interface is implemented by ResponseWriters which allow detecting
// when the underlying connection has gone away.
// Deprecated: the CloseNotifier interface predates Go's context package.
// New code should use Request.Context instead.
inner := decorator.Unwrap()
if innerNotifierFlusher, ok := inner.(CloseNotifierFlusher); ok {
// for HTTP/2 request, the default ResponseWriter object (http2responseWriter)
// implements Flusher and CloseNotifier.
outerHTTP2 := outerWithCloseNotifyAndFlush{
UserProvidedDecorator: decorator,
InnerCloseNotifierFlusher: innerNotifierFlusher,
}
if innerHijacker, hijackable := inner.(http.Hijacker); hijackable {
// for HTTP/1.x request the default implementation of ResponseWriter
// also implement CloseNotifier, Flusher and Hijacker
return &outerWithCloseNotifyFlushAndHijack{
outerWithCloseNotifyAndFlush: outerHTTP2,
InnerHijacker: innerHijacker,
}
}
return outerHTTP2
}
// we should never be here for either http/1.x or http2 request
return decorator
}
// CloseNotifierFlusher is a combination of http.CloseNotifier and http.Flusher
// This applies to both http/1.x and http2 requests.
type CloseNotifierFlusher interface {
http.CloseNotifier
http.Flusher
}
// GetOriginal goes through the chain of wrapped http.ResponseWriter objects
// and returns the original http.ResponseWriter object provided to the first
// request handler in the filter chain.
func GetOriginal(w http.ResponseWriter) http.ResponseWriter {
decorator, ok := w.(UserProvidedDecorator)
if !ok {
return w
}
inner := decorator.Unwrap()
if inner == w {
// infinite cycle here, we should never be here though.
panic("http.ResponseWriter decorator chain has a cycle")
}
return GetOriginal(inner)
}
//nolint:staticcheck // SA1019
var _ http.CloseNotifier = outerWithCloseNotifyAndFlush{}
var _ http.Flusher = outerWithCloseNotifyAndFlush{}
var _ http.ResponseWriter = outerWithCloseNotifyAndFlush{}
var _ UserProvidedDecorator = outerWithCloseNotifyAndFlush{}
// outerWithCloseNotifyAndFlush is the outer object that extends the
// user provied decorator with http.CloseNotifier and http.Flusher only.
type outerWithCloseNotifyAndFlush struct {
// UserProvidedDecorator is the user-provided object, it decorates
// an inner ResponseWriter object.
UserProvidedDecorator
// http.CloseNotifier and http.Flusher for the inner object
InnerCloseNotifierFlusher CloseNotifierFlusher
}
func (wr outerWithCloseNotifyAndFlush) CloseNotify() <-chan bool {
if notifier, ok := wr.UserProvidedDecorator.(http.CloseNotifier); ok {
return notifier.CloseNotify()
}
return wr.InnerCloseNotifierFlusher.CloseNotify()
}
func (wr outerWithCloseNotifyAndFlush) Flush() {
if flusher, ok := wr.UserProvidedDecorator.(http.Flusher); ok {
flusher.Flush()
return
}
wr.InnerCloseNotifierFlusher.Flush()
}
//lint:file-ignore SA1019 Keep supporting deprecated http.CloseNotifier
var _ http.CloseNotifier = outerWithCloseNotifyFlushAndHijack{}
var _ http.Flusher = outerWithCloseNotifyFlushAndHijack{}
var _ http.Hijacker = outerWithCloseNotifyFlushAndHijack{}
var _ http.ResponseWriter = outerWithCloseNotifyFlushAndHijack{}
var _ UserProvidedDecorator = outerWithCloseNotifyFlushAndHijack{}
// outerWithCloseNotifyFlushAndHijack is the outer object that extends the
// user-provided decorator with http.CloseNotifier, http.Flusher and http.Hijacker.
// This applies to http/1.x requests only.
type outerWithCloseNotifyFlushAndHijack struct {
outerWithCloseNotifyAndFlush
// http.Hijacker for the inner object
InnerHijacker http.Hijacker
}
func (wr outerWithCloseNotifyFlushAndHijack) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if hijacker, ok := wr.UserProvidedDecorator.(http.Hijacker); ok {
return hijacker.Hijack()
}
return wr.InnerHijacker.Hijack()
}

202
vendor/k8s.io/kubelet/LICENSE generated vendored
View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,5 +0,0 @@
rules:
# prevent exposing internal api in streaming packages
- selectorRegexp: k8s[.]io/kubernetes
allowedPrefixes:
- k8s.io/kubernetes/pkg/kubelet/cri

12
vendor/modules.txt vendored
View File

@@ -582,6 +582,10 @@ go.opentelemetry.io/proto/otlp/collector/trace/v1
go.opentelemetry.io/proto/otlp/common/v1
go.opentelemetry.io/proto/otlp/resource/v1
go.opentelemetry.io/proto/otlp/trace/v1
# go.uber.org/goleak v1.3.0
## explicit; go 1.20
go.uber.org/goleak
go.uber.org/goleak/internal/stack
# golang.org/x/crypto v0.36.0
## explicit; go 1.23.0
golang.org/x/crypto/cast5
@@ -825,9 +829,6 @@ k8s.io/apimachinery/pkg/version
k8s.io/apimachinery/pkg/watch
k8s.io/apimachinery/third_party/forked/golang/netutil
k8s.io/apimachinery/third_party/forked/golang/reflect
# k8s.io/apiserver v0.32.3
## explicit; go 1.23.0
k8s.io/apiserver/pkg/endpoints/responsewriter
# k8s.io/client-go v0.32.3
## explicit; go 1.23.0
k8s.io/client-go/features
@@ -864,11 +865,6 @@ k8s.io/klog/v2/internal/dbg
k8s.io/klog/v2/internal/serialize
k8s.io/klog/v2/internal/severity
k8s.io/klog/v2/internal/sloghandler
# k8s.io/kubelet v0.32.3
## explicit; go 1.23.0
k8s.io/kubelet/pkg/cri/streaming
k8s.io/kubelet/pkg/cri/streaming/portforward
k8s.io/kubelet/pkg/cri/streaming/remotecommand
# k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
## explicit; go 1.18
k8s.io/utils/clock