diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3739520..794f7c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,12 +11,12 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: "1.24" + go-version: "1.25" - uses: dominikh/staticcheck-action@v1.4.0 with: install-go: false - version: "2025.1" + version: "2025.1.1" test: runs-on: ${{ matrix.os }} @@ -29,7 +29,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: "1.24" + go-version: "1.25" - name: test run: sudo --preserve-env make test @@ -47,7 +47,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: "1.24" + go-version: "1.25" - name: Install protoc-gen-go run: | @@ -84,7 +84,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: "1.24" + go-version: "1.25" - name: e2e run: make test-e2e diff --git a/activator/Dockerfile b/activator/Dockerfile index 06c9296..7f279c1 100644 --- a/activator/Dockerfile +++ b/activator/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24 as gomod +FROM golang:1.25 as gomod WORKDIR /app ADD go.* /app @@ -7,7 +7,7 @@ ADD go.* /app RUN go mod download # we use fedora since it has a recent version of bpftool -FROM fedora:42 +FROM fedora:43 RUN dnf install -y llvm clang bpftool libbpf-devel golang RUN mkdir /headers diff --git a/activator/activator.go b/activator/activator.go index cc6fde8..fc3bfc8 100644 --- a/activator/activator.go +++ b/activator/activator.go @@ -152,8 +152,9 @@ func (s *Server) listen(ctx context.Context, port uint16) (int, error) { log.G(ctx).Debugf("listening on %s in ns %s", listener.Addr(), s.ns.Path()) - s.wg.Add(1) - go s.serve(ctx, listener, port) + s.wg.Go(func() { + s.serve(ctx, listener, port) + }) tcpAddr, ok := listener.Addr().(*net.TCPAddr) if !ok { @@ -183,7 +184,6 @@ func (s *Server) Stop(ctx context.Context) { } func (s *Server) serve(ctx context.Context, listener net.Listener, port uint16) { - defer s.wg.Done() wg := sync.WaitGroup{} for { @@ -203,12 +203,10 @@ func (s *Server) serve(ctx context.Context, listener net.Listener, port uint16) return } } else { - wg.Add(1) - go func() { + wg.Go(func() { log.G(ctx).Debug("accepting connection") s.handleConnection(ctx, conn, port) - wg.Done() - }() + }) } } } diff --git a/activator/activator_test.go b/activator/activator_test.go index eec8e69..d4b495a 100644 --- a/activator/activator_test.go +++ b/activator/activator_test.go @@ -134,9 +134,7 @@ func TestActivator(t *testing.T) { startServer(t, ctx, s, uint16(port), &tc) for i := 0; i < tc.parallelReqs; i++ { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { host := "127.0.0.1" if tc.ipv6 { host = "[::1]" @@ -159,7 +157,7 @@ func TestActivator(t *testing.T) { assert.Equal(t, tc.expectedCode, resp.StatusCode) assert.Equal(t, tc.expectedBody, string(b)) t.Log(string(b)) - }() + }) } wg.Wait() var key uint16 diff --git a/activator/bpf_bpfeb.o b/activator/bpf_bpfeb.o index 99a6f5a..77e9a1f 100644 Binary files a/activator/bpf_bpfeb.o and b/activator/bpf_bpfeb.o differ diff --git a/activator/bpf_bpfel.o b/activator/bpf_bpfel.o index 1232e42..07a9f60 100644 Binary files a/activator/bpf_bpfel.o and b/activator/bpf_bpfel.o differ diff --git a/cmd/freezer/Dockerfile b/cmd/freezer/Dockerfile index f4c0d46..c16b86d 100644 --- a/cmd/freezer/Dockerfile +++ b/cmd/freezer/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.24 AS builder +FROM --platform=$BUILDPLATFORM golang:1.25 AS builder WORKDIR /workspace COPY go.mod go.mod diff --git a/cmd/installer/Dockerfile b/cmd/installer/Dockerfile index 717a972..f50e64b 100644 --- a/cmd/installer/Dockerfile +++ b/cmd/installer/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.24 AS builder +FROM --platform=$BUILDPLATFORM golang:1.25 AS builder WORKDIR /workspace COPY go.mod go.mod diff --git a/cmd/manager/Dockerfile b/cmd/manager/Dockerfile index 29ba222..77b474e 100644 --- a/cmd/manager/Dockerfile +++ b/cmd/manager/Dockerfile @@ -1,6 +1,6 @@ ARG CRIU_IMAGE=ghcr.io/ctrox/zeropod-criu:116991736c7641258a5b7f53f5079b90fc80b99e -FROM --platform=$BUILDPLATFORM golang:1.24 AS builder +FROM --platform=$BUILDPLATFORM golang:1.25 AS builder WORKDIR /workspace COPY go.mod go.mod diff --git a/e2e/Dockerfile b/e2e/Dockerfile index 06efe34..0d0c4f3 100644 --- a/e2e/Dockerfile +++ b/e2e/Dockerfile @@ -1,4 +1,4 @@ -FROM docker:28.3.3-cli +FROM docker:29.1.2-cli RUN apk add --update go make iptables WORKDIR /app diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index f2745e5..e3e1652 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -33,6 +33,7 @@ func TestE2E(t *testing.T) { c := &http.Client{ Timeout: time.Second * 10, } + defaultScaleDownAfter := scaleDownAfter(time.Second) cases := map[string]struct { pod *corev1.Pod @@ -53,7 +54,7 @@ func TestE2E(t *testing.T) { // they should leave enough headroom but the tests could be flaky // because of that. "without pre-dump": { - pod: testPod(scaleDownAfter(time.Second)), + pod: testPod(defaultScaleDownAfter), parallelReqs: 1, sequentialReqs: 1, preDump: false, @@ -61,7 +62,7 @@ func TestE2E(t *testing.T) { waitScaledDown: true, }, "with pre-dump": { - pod: testPod(preDump(true), scaleDownAfter(time.Second)), + pod: testPod(preDump(true), defaultScaleDownAfter), parallelReqs: 1, sequentialReqs: 1, preDump: true, @@ -74,7 +75,7 @@ func TestE2E(t *testing.T) { // that the socket tracking does not work as it means the container // has checkpointed. "socket tracking": { - pod: testPod(scaleDownAfter(time.Second)), + pod: testPod(defaultScaleDownAfter), parallelReqs: 1, sequentialReqs: 20, keepAlive: false, @@ -98,7 +99,7 @@ func TestE2E(t *testing.T) { expectRunning: true, }, "pod with multiple containers": { - pod: testPod(agnContainer("c1", 8080), agnContainer("c2", 8081), scaleDownAfter(time.Second)), + pod: testPod(agnContainer("c1", 8080), agnContainer("c2", 8081), defaultScaleDownAfter), svc: testService(8081), parallelReqs: 1, sequentialReqs: 1, @@ -112,6 +113,13 @@ func TestE2E(t *testing.T) { agnContainer("c3", 8082), containerNamesAnnotation("c1,c3"), portsAnnotation("c1=8080;c3=8082"), + readinessProbe(&corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.FromInt(8082), + }, + }, + }, 2), ), svc: testService(8082), parallelReqs: 1, @@ -119,14 +127,14 @@ func TestE2E(t *testing.T) { maxReqDuration: time.Second, }, "parallel requests": { - pod: testPod(scaleDownAfter(time.Second)), + pod: testPod(defaultScaleDownAfter), parallelReqs: 10, sequentialReqs: 5, maxReqDuration: time.Second, waitScaledDown: true, }, "parallel requests with keepalive": { - pod: testPod(scaleDownAfter(time.Second)), + pod: testPod(defaultScaleDownAfter), parallelReqs: 10, sequentialReqs: 5, keepAlive: true, @@ -199,7 +207,6 @@ func TestE2E(t *testing.T) { } for name, tc := range cases { - tc := tc t.Run(name, func(t *testing.T) { if tc.preDump && runtime.GOARCH == "arm64" { t.Skip("skipping pre-dump test as it's not supported on arm64") @@ -341,7 +348,7 @@ func TestE2E(t *testing.T) { t.Run("socket tracker ignores probe", func(t *testing.T) { pod := testPod( - scaleDownAfter(time.Second*5), + defaultScaleDownAfter, // we use agn as it uses a v6 TCP socket so we can test ipv4 mapped v6 addresses agnContainer("agn", 8080), livenessProbe(&corev1.Probe{ diff --git a/e2e/setup_test.go b/e2e/setup_test.go index a0cdce5..7d73487 100644 --- a/e2e/setup_test.go +++ b/e2e/setup_test.go @@ -437,6 +437,12 @@ func livenessProbe(probe *corev1.Probe) podOption { } } +func readinessProbe(probe *corev1.Probe, index int) podOption { + return func(p *pod) { + p.spec.Containers[index].StartupProbe = probe + } +} + func disableDataMigration() podOption { return annotations(map[string]string{ shim.DisableMigrateDataAnnotationKey: "true", @@ -656,7 +662,11 @@ func waitForService(t testing.TB, ctx context.Context, c client.Client, svc *cor return false } - return len(endpointSliceList.Items[0].Endpoints) == replicas + if len(endpointSliceList.Items[0].Endpoints) != replicas { + return false + } + + return endpointsReady(endpointSliceList.Items[0].Endpoints) }, time.Minute, time.Second, "waiting for service endpoints to be ready") { endpointSliceList := &discoveryv1.EndpointSliceList{} if err := c.List(ctx, endpointSliceList, client.MatchingLabels{"kubernetes.io/service-name": svc.Name}); err == nil { @@ -672,6 +682,16 @@ func waitForService(t testing.TB, ctx context.Context, c client.Client, svc *cor time.Sleep(time.Millisecond * 500) } +func endpointsReady(endpoints []discoveryv1.Endpoint) bool { + ready := 0 + for _, endpoint := range endpoints { + if endpoint.Conditions.Ready != nil && *endpoint.Conditions.Ready { + ready++ + } + } + return ready == len(endpoints) +} + func cordonNode(t testing.TB, ctx context.Context, client client.Client, name string) (uncordon func()) { node := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: name}} retry.RetryOnConflict(retry.DefaultRetry, func() error { diff --git a/go.mod b/go.mod index ca7bf3c..f1b886f 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ module github.com/ctrox/zeropod -go 1.24.0 +go 1.25.0 require ( github.com/checkpoint-restore/go-criu/v7 v7.2.0 github.com/cilium/ebpf v0.19.0 github.com/containerd/cgroups/v3 v3.0.5 github.com/containerd/containerd/api v1.9.0 - github.com/containerd/containerd/v2 v2.1.4 + github.com/containerd/containerd/v2 v2.1.5 github.com/containerd/errdefs v1.0.0 github.com/containerd/errdefs/pkg v0.3.0 github.com/containerd/fifo v1.1.0 @@ -21,7 +21,7 @@ require ( github.com/fsnotify/fsnotify v1.9.0 github.com/go-logr/logr v1.4.3 github.com/klauspost/compress v1.18.0 - github.com/mholt/archives v0.1.4 + github.com/mholt/archives v0.1.5 github.com/moby/sys/userns v0.1.0 github.com/opencontainers/runtime-spec v1.2.1 github.com/pelletier/go-toml/v2 v2.2.4 @@ -112,7 +112,7 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/nwaples/rardecode/v2 v2.1.1 // indirect + github.com/nwaples/rardecode/v2 v2.2.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/selinux v1.12.0 // indirect @@ -121,7 +121,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect - github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.7 // indirect github.com/ulikunitz/xz v0.5.15 // indirect @@ -141,9 +141,9 @@ require ( golang.org/x/mod v0.27.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.16.0 // indirect + golang.org/x/sync v0.17.0 // indirect golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.36.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 9beb361..1ebe6d0 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= -github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ= -github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/containerd/v2 v2.1.5 h1:pWSmPxUszaLZKQPvOx27iD4iH+aM6o0BoN9+hg77cro= +github.com/containerd/containerd/v2 v2.1.5/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -267,8 +267,8 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= -github.com/mholt/archives v0.1.4 h1:sU+/lLNgafUontWFv3AVwO8VUWye3rrtN6hgC2dU11c= -github.com/mholt/archives v0.1.4/go.mod h1:I2ia+SQTtQHej9w1GZM/mz7qfdgQv+BHr3hEKqDcGuk= +github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= +github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= @@ -299,8 +299,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nwaples/rardecode/v2 v2.1.1 h1:OJaYalXdliBUXPmC8CZGQ7oZDxzX1/5mQmgn0/GASew= -github.com/nwaples/rardecode/v2 v2.1.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= +github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A= +github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -350,8 +350,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -504,8 +504,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -545,8 +545,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=