Support port-forwarding directly to Tart Guest Agent (#489)

This commit is contained in:
edi-oai
2026-09-08 18:40:52 +01:00
committed by GitHub
parent 792324b35b
commit 01bf2d72a9
20 changed files with 500 additions and 199 deletions
+3
View File
@@ -83,6 +83,9 @@ linters:
# Avoid unrelated style churn for now # Avoid unrelated style churn for now
- funcorder - funcorder
# It's OK to have dynamic errors since we're not a library
- err113
issues: issues:
# Don't hide multiple issues that belong to one class since GitHub annotations can handle them all nicely. # Don't hide multiple issues that belong to one class since GitHub annotations can handle them all nicely.
max-issues-per-linter: 0 max-issues-per-linter: 0
+15 -3
View File
@@ -380,12 +380,17 @@ paths:
type: string type: string
get: get:
summary: "Port-forward to a VM using WebSocket protocol" summary: "Port-forward to a VM using WebSocket protocol"
description: |
Connect to a VM's TCP port, a declared host process, or its Tart Guest Agent.
Specify exactly one of `port`, `hostProcess`, or `target=tart-guest-agent`.
Connecting to Tart Guest Agent requires either the `compute:write` or `compute:connect:tart-guest-agent` role.
tags: tags:
- vms - vms
parameters: parameters:
- in: query - in: query
name: port name: port
description: VM's TCP port number to connect to description: VM's TCP port number to connect to; mutually exclusive with `hostProcess` and `target`.
schema: schema:
type: integer type: integer
minimum: 1 minimum: 1
@@ -393,10 +398,17 @@ paths:
required: false required: false
- in: query - in: query
name: hostProcess name: hostProcess
description: Name of a host process declared in the VM's `hostProcesses` field; mutually exclusive with `port`. description: Name of a host process declared in the VM's `hostProcesses` field; mutually exclusive with `port` and `target`.
schema: schema:
type: string type: string
required: false required: false
- in: query
name: target
description: Forward to the specified target; mutually exclusive with `port` and `hostProcess`.
schema:
type: string
enum: [tart-guest-agent]
required: false
- in: query - in: query
name: wait name: wait
description: Duration in seconds to wait for the VM to transition into "running" state if not already running. description: Duration in seconds to wait for the VM to transition into "running" state if not already running.
@@ -420,7 +432,7 @@ paths:
type: string type: string
responses: responses:
'400': '400':
description: Invalid or ambiguous port-forward target specified description: Invalid query parameter or ambiguous port-forward target specified
'404': '404':
description: VM or requested host process doesn't exist description: VM or requested host process doesn't exist
'503': '503':
+19 -20
View File
@@ -9,6 +9,7 @@ require (
github.com/avast/retry-go/v4 v4.7.0 github.com/avast/retry-go/v4 v4.7.0
github.com/avast/retry-go/v5 v5.0.0 github.com/avast/retry-go/v5 v5.0.0
github.com/cirruslabs/chacha v0.16.3 github.com/cirruslabs/chacha v0.16.3
github.com/cirruslabs/tart-guest-agent v0.14.2
github.com/coder/websocket v1.8.14 github.com/coder/websocket v1.8.14
github.com/deckarep/golang-set/v2 v2.8.0 github.com/deckarep/golang-set/v2 v2.8.0
github.com/dgraph-io/badger/v3 v3.2103.5 github.com/dgraph-io/badger/v3 v3.2103.5
@@ -22,36 +23,36 @@ require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gosuri/uitable v0.0.4 github.com/gosuri/uitable v0.0.4
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.8.0 github.com/hashicorp/go-version v1.9.0
github.com/manifoldco/promptui v0.9.0 github.com/manifoldco/promptui v0.9.0
github.com/mitchellh/go-grpc-net-conn v0.0.0-20200427190222-eb030e4876f0 github.com/mitchellh/go-grpc-net-conn v0.0.0-20200427190222-eb030e4876f0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.83 github.com/pterm/pterm v0.12.83
github.com/puzpuzpuz/xsync/v4 v4.4.0 github.com/puzpuzpuz/xsync/v4 v4.5.0
github.com/samber/lo v1.53.0 github.com/samber/lo v1.53.0
github.com/samber/mo v1.16.0 github.com/samber/mo v1.16.0
github.com/sethvargo/go-password v0.3.1 github.com/sethvargo/go-password v0.3.1
github.com/shirou/gopsutil/v4 v4.26.2 github.com/shirou/gopsutil/v4 v4.26.2
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/spf13/cobra v1.10.2 github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.12.0
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0
go.opentelemetry.io/otel v1.42.0 go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0
go.opentelemetry.io/otel/metric v1.42.0 go.opentelemetry.io/otel/metric v1.44.0
go.opentelemetry.io/otel/sdk/metric v1.42.0 go.opentelemetry.io/otel/sdk/metric v1.44.0
go.uber.org/zap v1.27.1 go.uber.org/zap v1.28.0
golang.org/x/crypto v0.49.0 golang.org/x/crypto v0.51.0
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa
golang.org/x/exp/jsonrpc2 v0.0.0-20260718201538-764159d718ef golang.org/x/exp/jsonrpc2 v0.0.0-20260718201538-764159d718ef
golang.org/x/net v0.52.0 golang.org/x/net v0.55.0
golang.org/x/sync v0.20.0 golang.org/x/sync v0.22.0
golang.org/x/sys v0.46.0 golang.org/x/sys v0.47.0
golang.org/x/term v0.41.0 golang.org/x/term v0.43.0
golang.org/x/text v0.35.0 golang.org/x/text v0.37.0
google.golang.org/grpc v1.79.3 google.golang.org/grpc v1.83.0
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.12
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.1 howett.net/plist v1.0.1
@@ -71,9 +72,8 @@ require (
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/base64x v0.1.6 // indirect
github.com/containerd/console v1.0.5 // indirect github.com/containerd/console v1.0.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/ebitengine/purego v0.10.0 // indirect github.com/ebitengine/purego v0.10.1 // indirect
github.com/fatih/color v1.18.0 // indirect github.com/fatih/color v1.18.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
@@ -125,7 +125,6 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid/v2 v2.1.1 // indirect github.com/oklog/ulid/v2 v2.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect
@@ -139,8 +138,8 @@ require (
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go.opencensus.io v0.22.5 // indirect go.opencensus.io v0.22.5 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/sdk v1.42.0 // indirect go.opentelemetry.io/otel/sdk v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
+42 -40
View File
@@ -47,6 +47,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWs
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cirruslabs/chacha v0.16.3 h1:rOxn+qXsF3N9tQDpYxJ99e1ZP7G+IsFpzXd47IfSVIg= github.com/cirruslabs/chacha v0.16.3 h1:rOxn+qXsF3N9tQDpYxJ99e1ZP7G+IsFpzXd47IfSVIg=
github.com/cirruslabs/chacha v0.16.3/go.mod h1:6rPd7APYL3lPLA6L9tP5KAwx3l0nYjFA+1o2amYoICI= github.com/cirruslabs/chacha v0.16.3/go.mod h1:6rPd7APYL3lPLA6L9tP5KAwx3l0nYjFA+1o2amYoICI=
github.com/cirruslabs/tart-guest-agent v0.14.2 h1:m3pKQ7NYTVKb5whX00AhOXOUOfUXArqLApHWjIWjyL4=
github.com/cirruslabs/tart-guest-agent v0.14.2/go.mod h1:zB/fw/qgmS/Ah0idRH2PlYhbtwlQ1/7CeHHqzCigWlM=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
@@ -78,8 +80,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
@@ -202,8 +204,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -263,8 +265,6 @@ github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -277,8 +277,8 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA= github.com/pterm/pterm v0.12.83 h1:ie+YmGmA727VuhxBlyGr74Ks+7McV6kT99IB8EU80aA=
github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk= github.com/pterm/pterm v0.12.83/go.mod h1:xlgc6bFWyJIMtmLJvGim+L7jhSReilOlOnodeIYe4Tk=
github.com/puzpuzpuz/xsync/v4 v4.4.0 h1:vlSN6/CkEY0pY8KaB0yqo/pCLZvp9nhdbBdjipT4gWo= github.com/puzpuzpuz/xsync/v4 v4.5.0 h1:vOSWu6b57/emh+L/Cw0BeQfvxa/cogFywXHeGUxQxAg=
github.com/puzpuzpuz/xsync/v4 v4.4.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/puzpuzpuz/xsync/v4 v4.5.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
@@ -327,8 +327,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
@@ -357,22 +357,24 @@ go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0/go.mod h1:WB2cS9y+AwqqKhoo9gw6/ZxlSjFBUQGZ8BQOaD3FVXM= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.67.0/go.mod h1:WB2cS9y+AwqqKhoo9gw6/ZxlSjFBUQGZ8BQOaD3FVXM=
go.opentelemetry.io/contrib/propagators/b3 v1.42.0 h1:B2Pew5ufEtgkjLF+tSkXjgYZXQr9m7aCm1wLKB0URbU= go.opentelemetry.io/contrib/propagators/b3 v1.42.0 h1:B2Pew5ufEtgkjLF+tSkXjgYZXQr9m7aCm1wLKB0URbU=
go.opentelemetry.io/contrib/propagators/b3 v1.42.0/go.mod h1:iPgUcSEF5DORW6+yNbdw/YevUy+QqJ508ncjhrRSCjc= go.opentelemetry.io/contrib/propagators/b3 v1.42.0/go.mod h1:iPgUcSEF5DORW6+yNbdw/YevUy+QqJ508ncjhrRSCjc=
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0 h1:H7O6RlGOMTizyl3R08Kn5pdM06bnH8oscSj7o11tmLA= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0 h1:H7O6RlGOMTizyl3R08Kn5pdM06bnH8oscSj7o11tmLA=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0/go.mod h1:mBFWu/WOVDkWWsR7Tx7h6EpQB8wsv7P0Yrh0Pb7othc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0/go.mod h1:mBFWu/WOVDkWWsR7Tx7h6EpQB8wsv7P0Yrh0Pb7othc=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -381,8 +383,8 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y= golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
@@ -392,8 +394,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
@@ -419,8 +421,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -430,8 +432,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -454,22 +456,22 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -486,8 +488,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -500,10 +502,10 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+6
View File
@@ -74,6 +74,12 @@ func (controller *Controller) rpcWatch(ctx *gin.Context) responder.Responder {
Name: hostProcess.Name, Name: hostProcess.Name,
} }
} }
if tartGuestAgent := target.GetTartGuestAgent(); tartGuestAgent != nil {
watchInstruction.PortForwardAction.Target.TartGuestAgent = &v1.PortForwardTargetTartGuestAgent{
VMUID: tartGuestAgent.VmUid,
}
}
} else { } else {
watchInstruction.PortForwardAction.VMUID = typedAction.PortForwardAction.VmUid watchInstruction.PortForwardAction.VMUID = typedAction.PortForwardAction.VmUid
watchInstruction.PortForwardAction.Port = uint16(typedAction.PortForwardAction.Port) watchInstruction.PortForwardAction.Port = uint16(typedAction.PortForwardAction.Port)
+1
View File
@@ -212,6 +212,7 @@ func (controller *Controller) newSSHExecSession(
vm.UID, vm.UID,
22, 22,
"", "",
"",
) )
if err != nil { if err != nil {
return nil, err return nil, err
+29 -8
View File
@@ -13,6 +13,7 @@ import (
"github.com/cirruslabs/orchard/internal/netconncancel" "github.com/cirruslabs/orchard/internal/netconncancel"
"github.com/cirruslabs/orchard/internal/proxy" "github.com/cirruslabs/orchard/internal/proxy"
"github.com/cirruslabs/orchard/internal/responder" "github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/pkg/client"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1" v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/cirruslabs/orchard/rpc" "github.com/cirruslabs/orchard/rpc"
"github.com/coder/websocket" "github.com/coder/websocket"
@@ -27,15 +28,27 @@ import (
var errPortForwardRequest = errors.New("failed to request port forwarding") var errPortForwardRequest = errors.New("failed to request port forwarding")
func (controller *Controller) portForwardVM(ctx *gin.Context) responder.Responder { func (controller *Controller) portForwardVM(ctx *gin.Context) responder.Responder {
if responder := controller.authorizeAny(ctx, v1.ServiceAccountRoleComputeWrite,
v1.ServiceAccountRoleComputeConnect); responder != nil {
return responder
}
// Retrieve and parse path and query parameters // Retrieve and parse path and query parameters
name := ctx.Param("name") name := ctx.Param("name")
portRaw := ctx.Query("port") portRaw := ctx.Query("port")
hostProcess := ctx.Query("hostProcess") hostProcess := ctx.Query("hostProcess")
target := client.PortForwardTarget(ctx.Query("target"))
if target != "" {
if err := target.Validate(); err != nil {
return responder.JSON(http.StatusBadRequest, NewErrorResponse("%v", err))
}
if portRaw != "" || hostProcess != "" {
return responder.JSON(http.StatusBadRequest,
NewErrorResponse("target cannot be combined with port or hostProcess"))
}
if responder := controller.authorizeAny(ctx, v1.ServiceAccountRoleComputeWrite,
v1.ServiceAccountRoleComputeConnectTartGuestAgent); responder != nil {
return responder
}
} else if responder := controller.authorizeAny(ctx, v1.ServiceAccountRoleComputeWrite,
v1.ServiceAccountRoleComputeConnect); responder != nil {
return responder
}
// Host process connections require an additional role // Host process connections require an additional role
var port uint64 var port uint64
@@ -51,7 +64,7 @@ func (controller *Controller) portForwardVM(ctx *gin.Context) responder.Responde
if portRaw != "" { if portRaw != "" {
return responder.Code(http.StatusBadRequest) return responder.Code(http.StatusBadRequest)
} }
} else { } else if target == "" {
// VM port forwarding requires a valid non-zero TCP port // VM port forwarding requires a valid non-zero TCP port
port, err = strconv.ParseUint(portRaw, 10, 16) port, err = strconv.ParseUint(portRaw, 10, 16)
if err != nil || port < 1 || port > 65535 { if err != nil || port < 1 || port > 65535 {
@@ -83,7 +96,7 @@ func (controller *Controller) portForwardVM(ctx *gin.Context) responder.Responde
} }
// Commence port forwarding // Commence port forwarding
return controller.portForward(ctx, waitContext, vm.Worker, vm.UID, uint32(port), hostProcess) return controller.portForward(ctx, waitContext, vm.Worker, vm.UID, uint32(port), hostProcess, target)
} }
func (controller *Controller) portForward( func (controller *Controller) portForward(
@@ -93,6 +106,7 @@ func (controller *Controller) portForward(
vmUID string, vmUID string,
port uint32, port uint32,
hostProcess string, hostProcess string,
target client.PortForwardTarget,
) responder.Responder { ) responder.Responder {
// Request and wait for a connection with a worker // Request and wait for a connection with a worker
rendezvousConn, err := retry.NewWithData[net.Conn]( rendezvousConn, err := retry.NewWithData[net.Conn](
@@ -102,7 +116,7 @@ func (controller *Controller) portForward(
retry.Attempts(0), retry.Attempts(0),
retry.LastErrorOnly(true), retry.LastErrorOnly(true),
).Do(func() (net.Conn, error) { ).Do(func() (net.Conn, error) {
return controller.portForwardConnection(ctx, notifyContext, workerName, vmUID, port, hostProcess) return controller.portForwardConnection(ctx, notifyContext, workerName, vmUID, port, hostProcess, target)
}) })
if err != nil { if err != nil {
if errors.Is(err, errPortForwardRequest) { if errors.Is(err, errPortForwardRequest) {
@@ -226,6 +240,7 @@ func (controller *Controller) portForwardConnection(
vmUID string, vmUID string,
port uint32, port uint32,
hostProcess string, hostProcess string,
target client.PortForwardTarget,
) (net.Conn, error) { ) (net.Conn, error) {
// Create a rendezvous connection point // Create a rendezvous connection point
rendezvousCtx, rendezvousCtxCancel := context.WithCancel(ctx) rendezvousCtx, rendezvousCtxCancel := context.WithCancel(ctx)
@@ -251,6 +266,12 @@ func (controller *Controller) portForwardConnection(
}, },
}, },
} }
} else if target == client.PortForwardTargetTartGuestAgent {
portForwardAction.Target = &rpc.WatchInstruction_PortForward_Target{
Value: &rpc.WatchInstruction_PortForward_Target_TartGuestAgent_{
TartGuestAgent: &rpc.WatchInstruction_PortForward_Target_TartGuestAgent{VmUid: vmUID},
},
}
} else { } else {
portForwardAction.VmUid = vmUID portForwardAction.VmUid = vmUID
portForwardAction.Port = port portForwardAction.Port = port
@@ -50,5 +50,5 @@ func (controller *Controller) portForwardWorker(ctx *gin.Context) responder.Resp
} }
// Commence port-forwarding // Commence port-forwarding
return controller.portForward(ctx, waitContext, worker.Name, "", uint32(port), "") return controller.portForward(ctx, waitContext, worker.Name, "", uint32(port), "", "")
} }
+53
View File
@@ -0,0 +1,53 @@
//go:build darwin
package tests_test
import (
"context"
"net"
"testing"
"time"
"github.com/cirruslabs/orchard/internal/tests/devcontroller"
"github.com/cirruslabs/orchard/internal/tests/platformdependent"
"github.com/cirruslabs/orchard/pkg/client"
guestagent "github.com/cirruslabs/tart-guest-agent/pkg/v1"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func TestTartGuestAgentPortForward(t *testing.T) {
devClient, _, _ := devcontroller.StartIntegrationTestEnvironment(t)
// Create a VM with Tart Guest Agent pre-installed
vmName := "test-guest-agent-" + uuid.NewString()
require.NoError(t, devClient.VMs().Create(t.Context(), platformdependent.VM(vmName)))
// Configure the Tart Guest Agent client to connect through Orchard's port-forwarding API
agentConn, err := grpc.NewClient("passthrough:///tart-guest-agent",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
// Keep the stream alive beyond gRPC's temporary dial context
return devClient.VMs().PortForwardTarget(t.Context(), vmName,
client.PortForwardTargetTartGuestAgent, 120)
}),
)
require.NoError(t, err)
defer agentConn.Close()
// Perform IP resolution through the forwarded connection
agentClient := guestagent.NewAgentClient(agentConn)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
rpcContext, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
response, err := agentClient.ResolveIP(rpcContext, &guestagent.ResolveIPRequest{},
grpc.WaitForReady(true))
require.NoError(collect, err)
require.NotNil(collect, net.ParseIP(response.GetIp()))
}, 2*time.Minute, time.Second)
}
+2 -19
View File
@@ -14,6 +14,7 @@ import (
"time" "time"
"github.com/avast/retry-go/v4" "github.com/avast/retry-go/v4"
"github.com/cirruslabs/orchard/internal/worker/socketalias"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1" v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
) )
@@ -45,7 +46,7 @@ func NewProcess(
// Route the Tart control socket through the runtime directory as well // Route the Tart control socket through the runtime directory as well
// to work around macOS's 104-byte Unix-domain socket limit // to work around macOS's 104-byte Unix-domain socket limit
controlSocket, err = shortenControlSocketPath(runtimeDir, controlSocket) controlSocket, err = socketalias.Create(runtimeDir, controlSocket)
if err != nil { if err != nil {
return nil, errors.Join(err, os.RemoveAll(runtimeDir)) return nil, errors.Join(err, os.RemoveAll(runtimeDir))
} }
@@ -108,24 +109,6 @@ func NewProcess(
return process, nil return process, nil
} }
func shortenControlSocketPath(runtimeDir string, controlSocketPath string) (string, error) {
// Make the symlink target absolute because a relative TART_HOME would
// otherwise be resolved from runtimeDir, breaking the symlink
absoluteControlSocketPath, err := filepath.Abs(controlSocketPath)
if err != nil {
return "", err
}
// Place the VM's control socket alias in the runtime directory
aliasControlSocketPath := filepath.Join(runtimeDir, "vm.sock")
if err := os.Symlink(absoluteControlSocketPath, aliasControlSocketPath); err != nil {
return "", err
}
return aliasControlSocketPath, nil
}
func (process *Process) Dial(ctx context.Context) (net.Conn, error) { func (process *Process) Dial(ctx context.Context) (net.Conn, error) {
var dialer net.Dialer var dialer net.Dialer
@@ -1,58 +0,0 @@
//nolint:noctx,testpackage,usetesting // Preserve the socket-path tests and their required short /var/tmp paths.
package hostprocess
import (
"net"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestShortenControlSocketPath(t *testing.T) {
// Create a control socket path that exceeds macOS's 104-byte limit
const controlSocketName = "control.sock"
vmDir := filepath.Join(t.TempDir(), strings.Repeat("v", 104))
require.NoError(t, os.MkdirAll(vmDir, 0o700))
controlSocketPath := filepath.Join(vmDir, controlSocketName)
require.Greater(t, len(controlSocketPath), 104)
// Create Orchard's short per-process runtime directory
runtimeDir, err := os.MkdirTemp("/var/tmp", "orchard-hp-test-")
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(runtimeDir))
})
// Shorten the control socket path and verify the resulting alias
shortControlSocketPath, err := shortenControlSocketPath(runtimeDir, controlSocketPath)
require.NoError(t, err)
require.Equal(t, filepath.Join(runtimeDir, "vm.sock"), shortControlSocketPath)
require.Less(t, len(shortControlSocketPath), 104)
// Model Tart binding the socket relative to the long VM directory
t.Chdir(vmDir)
listener, err := net.Listen("unix", controlSocketName)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, listener.Close())
})
// Model the host process running from Orchard's runtime directory
t.Chdir(runtimeDir)
// Verify connecting through the long absolute path fails
connection, err := net.Dial("unix", controlSocketPath)
require.Error(t, err)
require.Nil(t, connection)
// Connect to Tart's control socket through Orchard's short alias
connection, err = net.Dial("unix", shortControlSocketPath)
require.NoError(t, err)
require.NoError(t, connection.Close())
}
+14 -8
View File
@@ -67,7 +67,7 @@ func (worker *Worker) watchRPC(ctx context.Context, operationCtx context.Context
} }
} }
//nolint:nestif,protogetter // Preserve the original host-process forwarding implementation. //nolint:nestif,protogetter // Preserve the original forwarding implementation.
func (worker *Worker) handlePortForward( func (worker *Worker) handlePortForward(
ctx context.Context, ctx context.Context,
client rpc.ControllerClient, client rpc.ControllerClient,
@@ -98,17 +98,18 @@ func (worker *Worker) handlePortForward(
return return
} }
// Retrieve the typed host process target, it's the only possible target right now switch {
hostProcess := target.GetHostProcess() case target.GetTartGuestAgent() != nil:
if hostProcess == nil || hostProcess.VmUid == "" || hostProcess.Name == "" { targetConn, err = worker.dialTartGuestAgent(subCtx, target.GetTartGuestAgent().VmUid)
case target.GetHostProcess() != nil:
targetConn, err = worker.dialHostProcess(subCtx, target.GetHostProcess().VmUid,
target.GetHostProcess().Name)
default:
worker.logger.Warn("port forwarding failed: invalid or unsupported target") worker.logger.Warn("port forwarding failed: invalid or unsupported target")
return return
} }
// Dial host process
targetConn, err = worker.dialHostProcess(ctx, hostProcess.VmUid, hostProcess.Name)
if err != nil { if err != nil {
worker.logger.Warnf("port forwarding failed: failed to connect to host process: %v", err) worker.logger.Warnf("port forwarding failed: %v", err)
return return
} }
} else { } else {
@@ -155,6 +156,11 @@ func (worker *Worker) handlePortForward(
} }
} }
// Close the target on return or cancellation to unblock pending reads and writes
defer targetConn.Close()
stopClosing := context.AfterFunc(subCtx, func() { _ = targetConn.Close() })
defer stopClosing()
// Proxy bytes // Proxy bytes
grpcConn := &grpc_net_conn.Conn{ grpcConn := &grpc_net_conn.Conn{
Stream: stream, Stream: stream,
+51 -5
View File
@@ -6,6 +6,7 @@ import (
"net" "net"
"github.com/cirruslabs/orchard/internal/proxy" "github.com/cirruslabs/orchard/internal/proxy"
"github.com/cirruslabs/orchard/internal/worker/socketalias"
"github.com/cirruslabs/orchard/internal/worker/vmmanager" "github.com/cirruslabs/orchard/internal/worker/vmmanager"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1" v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/samber/lo" "github.com/samber/lo"
@@ -48,6 +49,11 @@ func (worker *Worker) handlePortForwardV2(ctx context.Context, portForward *v1.P
errorMessage = fmt.Sprintf("port-forwarding failed: %v", err) errorMessage = fmt.Sprintf("port-forwarding failed: %v", err)
worker.logger.Warn(errorMessage) worker.logger.Warn(errorMessage)
} else {
// Close the target on return or cancellation to unblock pending reads and writes
defer vmConn.Close()
stopClosing := context.AfterFunc(ctx, func() { _ = vmConn.Close() })
defer stopClosing()
} }
// Respond // Respond
@@ -82,13 +88,14 @@ func (worker *Worker) handlePortForwardV2Inner(
return nil, fmt.Errorf("target and legacy fields are mutually exclusive") return nil, fmt.Errorf("target and legacy fields are mutually exclusive")
} }
// Retrieve the typed host process target, it's the only possible target right now switch {
if target.HostProcess == nil || target.HostProcess.VMUID == "" || target.HostProcess.Name == "" { case target.TartGuestAgent != nil:
return worker.dialTartGuestAgent(ctx, target.TartGuestAgent.VMUID)
case target.HostProcess != nil:
return worker.dialHostProcess(ctx, target.HostProcess.VMUID, target.HostProcess.Name)
default:
return nil, fmt.Errorf("invalid or unsupported target") return nil, fmt.Errorf("invalid or unsupported target")
} }
// Dial host process
return worker.dialHostProcess(ctx, target.HostProcess.VMUID, target.HostProcess.Name)
} }
var host string var host string
@@ -133,6 +140,40 @@ func (worker *Worker) handlePortForwardV2Inner(
return vmConn, nil return vmConn, nil
} }
//nolint:err113,perfsprint // runtime and VM state failures are reported to the port-forward caller
func (worker *Worker) dialTartGuestAgent(ctx context.Context, vmUID string) (net.Conn, error) {
// Validate the VM UID and runtime
if vmUID == "" {
return nil, fmt.Errorf("invalid Tart Guest Agent target: VM UID is required")
}
if worker.runtime.ID() != v1.RuntimeTart || worker.runtime.Synthetic() {
return nil, fmt.Errorf("forwarding to Tart Guest Agent requires the Tart runtime")
}
// Find the running VM
vm, err := worker.findVMByUID(vmUID)
if err != nil {
return nil, err
}
if !vm.Running() {
return nil, fmt.Errorf("VM with UID %q is not running", vmUID)
}
// Connect to Tart Guest Agent through the VM's control socket
path, err := vm.OnDiskName().ControlSocketPath()
if err != nil {
return nil, err
}
conn, err := socketalias.DialContext(ctx, path)
if err != nil {
return nil, fmt.Errorf("failed to connect to Tart Guest Agent: %w", err)
}
return conn, nil
}
func (worker *Worker) handleGetIPV2(ctx context.Context, resolveIP *v1.ResolveIPAction) { func (worker *Worker) handleGetIPV2(ctx context.Context, resolveIP *v1.ResolveIPAction) {
var errorMessage string var errorMessage string
@@ -192,7 +233,12 @@ func (worker *Worker) findVMByUID(uid string) (vmmanager.VM, error) {
return vm, nil return vm, nil
} }
//nolint:err113,perfsprint // Use descriptive errors for host process validation
func (worker *Worker) dialHostProcess(ctx context.Context, vmUID string, name string) (net.Conn, error) { func (worker *Worker) dialHostProcess(ctx context.Context, vmUID string, name string) (net.Conn, error) {
if vmUID == "" || name == "" {
return nil, fmt.Errorf("invalid host process target: VM UID and name are required")
}
vm, err := worker.findVMByUID(vmUID) vm, err := worker.findVMByUID(vmUID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -0,0 +1,67 @@
package socketalias
import (
"context"
"errors"
"net"
"os"
"path/filepath"
)
// baseDirectory is the parent directory for transient socket aliases.
//
// To keep the Unix socket path short enough and fit the platform limit,
// we're specifically requesting "/var/tmp" instead of "/var/folders/.../T/".
const baseDirectory = "/var/tmp"
// Create creates a short Unix socket alias in runtimeDir that points to socketPath.
//
// The alias remains valid until runtimeDir is removed.
func Create(runtimeDir string, socketPath string) (string, error) {
// Make the symlink target absolute because a relative TART_HOME would
// otherwise be resolved from runtimeDir, breaking the symlink
absoluteSocketPath, err := filepath.Abs(socketPath)
if err != nil {
return "", err
}
// Place the VM's control socket alias in the runtime directory
aliasSocketPath := filepath.Join(runtimeDir, "vm.sock")
if err := os.Symlink(absoluteSocketPath, aliasSocketPath); err != nil {
return "", err
}
return aliasSocketPath, nil
}
// DialContext connects to socketPath through a short transient alias.
func DialContext(ctx context.Context, socketPath string) (net.Conn, error) {
// Create a directory where the transient Unix socket alias will live
runtimeDir, err := os.MkdirTemp(baseDirectory, "orchard-socket-")
if err != nil {
return nil, err
}
// Route the socket through the short runtime directory
aliasSocketPath, err := Create(runtimeDir, socketPath)
if err != nil {
return nil, errors.Join(err, os.RemoveAll(runtimeDir))
}
// Connect through the short alias
var dialer net.Dialer
conn, err := dialer.DialContext(ctx, "unix", aliasSocketPath)
// Remove the transient alias after the connection attempt
cleanupErr := os.RemoveAll(runtimeDir)
if err != nil {
return nil, errors.Join(err, cleanupErr)
}
// Avoid returning a live connection when its alias cannot be removed
if cleanupErr != nil {
return nil, errors.Join(cleanupErr, conn.Close())
}
return conn, nil
}
@@ -0,0 +1,49 @@
package socketalias_test
import (
"net"
"os"
"path/filepath"
"strings"
"testing"
"github.com/cirruslabs/orchard/internal/worker/socketalias"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func TestDialContextWithLongPath(t *testing.T) {
const (
socketName = "control.sock"
unixSocketPathLimit = len(unix.RawSockaddrUnix{}.Path)
doubleUnixSocketPathLimit = 2 * unixSocketPathLimit
)
// Create the socket at an overlong absolute path without passing that path to bind
originalWorkingDirectory, err := os.Getwd()
require.NoError(t, err)
socketDir := filepath.Join(t.TempDir(), strings.Repeat("x", doubleUnixSocketPathLimit))
require.NoError(t, os.MkdirAll(socketDir, 0o700))
socketPath := filepath.Join(socketDir, socketName)
t.Chdir(socketDir)
listener, err := (&net.ListenConfig{}).Listen(t.Context(), "unix", socketName)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, listener.Close())
})
t.Chdir(originalWorkingDirectory)
// Verify that connecting through the long absolute path fails
connection, err := (&net.Dialer{}).DialContext(t.Context(), "unix", socketPath)
require.Error(t, err)
require.Nil(t, connection)
// Verify that connecting through a short transient alias succeeds
connection, err = socketalias.DialContext(t.Context(), socketPath)
require.NoError(t, err)
require.NoError(t, connection.Close())
}
+28
View File
@@ -13,6 +13,21 @@ import (
"github.com/coder/websocket" "github.com/coder/websocket"
) )
type PortForwardTarget string
const (
PortForwardTargetTartGuestAgent PortForwardTarget = "tart-guest-agent"
)
func (target PortForwardTarget) Validate() error {
switch target {
case PortForwardTargetTartGuestAgent:
return nil
default:
return fmt.Errorf("unsupported port-forward target %q", target)
}
}
type VMsService struct { type VMsService struct {
client *Client client *Client
} }
@@ -182,6 +197,19 @@ func (service *VMsService) PortForwardHostProcess(
}) })
} }
func (service *VMsService) PortForwardTarget(
ctx context.Context,
name string,
target PortForwardTarget,
waitSeconds uint16,
) (net.Conn, error) {
return service.client.wsRequest(ctx, fmt.Sprintf("vms/%s/port-forward", url.PathEscape(name)),
map[string]string{
"target": string(target),
"wait": strconv.FormatUint(uint64(waitSeconds), 10),
})
}
func (service *VMsService) Exec( func (service *VMsService) Exec(
ctx context.Context, ctx context.Context,
name string, name string,
+11 -7
View File
@@ -10,13 +10,14 @@ var ErrUnsupportedServiceAccountRole = errors.New("unsupported service account r
type ServiceAccountRole string type ServiceAccountRole string
const ( const (
ServiceAccountRoleComputeRead ServiceAccountRole = "compute:read" ServiceAccountRoleComputeRead ServiceAccountRole = "compute:read"
ServiceAccountRoleComputeWrite ServiceAccountRole = "compute:write" ServiceAccountRoleComputeWrite ServiceAccountRole = "compute:write"
ServiceAccountRoleComputeConnect ServiceAccountRole = "compute:connect" ServiceAccountRoleComputeConnect ServiceAccountRole = "compute:connect"
ServiceAccountRoleHostProcessWrite ServiceAccountRole = "host-process:write" ServiceAccountRoleComputeConnectTartGuestAgent ServiceAccountRole = "compute:connect:tart-guest-agent"
ServiceAccountRoleHostProcessConnect ServiceAccountRole = "host-process:connect" ServiceAccountRoleHostProcessWrite ServiceAccountRole = "host-process:write"
ServiceAccountRoleAdminRead ServiceAccountRole = "admin:read" ServiceAccountRoleHostProcessConnect ServiceAccountRole = "host-process:connect"
ServiceAccountRoleAdminWrite ServiceAccountRole = "admin:write" ServiceAccountRoleAdminRead ServiceAccountRole = "admin:read"
ServiceAccountRoleAdminWrite ServiceAccountRole = "admin:write"
) )
func NewServiceAccountRole(name string) (ServiceAccountRole, error) { func NewServiceAccountRole(name string) (ServiceAccountRole, error) {
@@ -27,6 +28,8 @@ func NewServiceAccountRole(name string) (ServiceAccountRole, error) {
return ServiceAccountRoleComputeWrite, nil return ServiceAccountRoleComputeWrite, nil
case string(ServiceAccountRoleComputeConnect): case string(ServiceAccountRoleComputeConnect):
return ServiceAccountRoleComputeConnect, nil return ServiceAccountRoleComputeConnect, nil
case string(ServiceAccountRoleComputeConnectTartGuestAgent):
return ServiceAccountRoleComputeConnectTartGuestAgent, nil
case string(ServiceAccountRoleHostProcessWrite): case string(ServiceAccountRoleHostProcessWrite):
return ServiceAccountRoleHostProcessWrite, nil return ServiceAccountRoleHostProcessWrite, nil
case string(ServiceAccountRoleHostProcessConnect): case string(ServiceAccountRoleHostProcessConnect):
@@ -45,6 +48,7 @@ func AllServiceAccountRoles() []ServiceAccountRole {
ServiceAccountRoleComputeRead, ServiceAccountRoleComputeRead,
ServiceAccountRoleComputeWrite, ServiceAccountRoleComputeWrite,
ServiceAccountRoleComputeConnect, ServiceAccountRoleComputeConnect,
ServiceAccountRoleComputeConnectTartGuestAgent,
ServiceAccountRoleHostProcessWrite, ServiceAccountRoleHostProcessWrite,
ServiceAccountRoleHostProcessConnect, ServiceAccountRoleHostProcessConnect,
ServiceAccountRoleAdminRead, ServiceAccountRoleAdminRead,
+6 -1
View File
@@ -15,7 +15,8 @@ type PortForwardAction struct {
} }
type PortForwardTarget struct { type PortForwardTarget struct {
HostProcess *PortForwardTargetHostProcess `json:"hostProcess,omitempty"` HostProcess *PortForwardTargetHostProcess `json:"hostProcess,omitempty"`
TartGuestAgent *PortForwardTargetTartGuestAgent `json:"tartGuestAgent,omitempty"`
} }
type PortForwardTargetHostProcess struct { type PortForwardTargetHostProcess struct {
@@ -23,6 +24,10 @@ type PortForwardTargetHostProcess struct {
Name string `json:"name"` Name string `json:"name"`
} }
type PortForwardTargetTartGuestAgent struct {
VMUID string `json:"vmUID"`
}
type SyncVMsAction struct { type SyncVMsAction struct {
// nothing for now // nothing for now
} }
+96 -28
View File
@@ -224,7 +224,7 @@ type WatchInstruction_PortForward struct {
VmUid string `protobuf:"bytes,2,opt,name=vm_uid,json=vmUid,proto3" json:"vm_uid,omitempty"` VmUid string `protobuf:"bytes,2,opt,name=vm_uid,json=vmUid,proto3" json:"vm_uid,omitempty"`
Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"`
// Typed alternative to the legacy port-forwarding destination, // Typed alternative to the legacy port-forwarding destination,
// adding support for host processes // adding support for host processes and Tart Guest Agent
Target *WatchInstruction_PortForward_Target `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` Target *WatchInstruction_PortForward_Target `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -384,6 +384,7 @@ type WatchInstruction_PortForward_Target struct {
// Types that are valid to be assigned to Value: // Types that are valid to be assigned to Value:
// //
// *WatchInstruction_PortForward_Target_HostProcess_ // *WatchInstruction_PortForward_Target_HostProcess_
// *WatchInstruction_PortForward_Target_TartGuestAgent_
Value isWatchInstruction_PortForward_Target_Value `protobuf_oneof:"value"` Value isWatchInstruction_PortForward_Target_Value `protobuf_oneof:"value"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -435,6 +436,15 @@ func (x *WatchInstruction_PortForward_Target) GetHostProcess() *WatchInstruction
return nil return nil
} }
func (x *WatchInstruction_PortForward_Target) GetTartGuestAgent() *WatchInstruction_PortForward_Target_TartGuestAgent {
if x != nil {
if x, ok := x.Value.(*WatchInstruction_PortForward_Target_TartGuestAgent_); ok {
return x.TartGuestAgent
}
}
return nil
}
type isWatchInstruction_PortForward_Target_Value interface { type isWatchInstruction_PortForward_Target_Value interface {
isWatchInstruction_PortForward_Target_Value() isWatchInstruction_PortForward_Target_Value()
} }
@@ -443,9 +453,16 @@ type WatchInstruction_PortForward_Target_HostProcess_ struct {
HostProcess *WatchInstruction_PortForward_Target_HostProcess `protobuf:"bytes,1,opt,name=host_process,json=hostProcess,proto3,oneof"` HostProcess *WatchInstruction_PortForward_Target_HostProcess `protobuf:"bytes,1,opt,name=host_process,json=hostProcess,proto3,oneof"`
} }
type WatchInstruction_PortForward_Target_TartGuestAgent_ struct {
TartGuestAgent *WatchInstruction_PortForward_Target_TartGuestAgent `protobuf:"bytes,2,opt,name=tart_guest_agent,json=tartGuestAgent,proto3,oneof"`
}
func (*WatchInstruction_PortForward_Target_HostProcess_) isWatchInstruction_PortForward_Target_Value() { func (*WatchInstruction_PortForward_Target_HostProcess_) isWatchInstruction_PortForward_Target_Value() {
} }
func (*WatchInstruction_PortForward_Target_TartGuestAgent_) isWatchInstruction_PortForward_Target_Value() {
}
// Forward the byte stream to a host process, identified by // Forward the byte stream to a host process, identified by
// a VM UID and a host process name // a VM UID and a host process name
type WatchInstruction_PortForward_Target_HostProcess struct { type WatchInstruction_PortForward_Target_HostProcess struct {
@@ -500,25 +517,73 @@ func (x *WatchInstruction_PortForward_Target_HostProcess) GetName() string {
return "" return ""
} }
// Forward the byte stream to the Tart Guest Agent of a VM
type WatchInstruction_PortForward_Target_TartGuestAgent struct {
state protoimpl.MessageState `protogen:"open.v1"`
VmUid string `protobuf:"bytes,1,opt,name=vm_uid,json=vmUid,proto3" json:"vm_uid,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WatchInstruction_PortForward_Target_TartGuestAgent) Reset() {
*x = WatchInstruction_PortForward_Target_TartGuestAgent{}
mi := &file_orchard_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WatchInstruction_PortForward_Target_TartGuestAgent) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WatchInstruction_PortForward_Target_TartGuestAgent) ProtoMessage() {}
func (x *WatchInstruction_PortForward_Target_TartGuestAgent) ProtoReflect() protoreflect.Message {
mi := &file_orchard_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WatchInstruction_PortForward_Target_TartGuestAgent.ProtoReflect.Descriptor instead.
func (*WatchInstruction_PortForward_Target_TartGuestAgent) Descriptor() ([]byte, []int) {
return file_orchard_proto_rawDescGZIP(), []int{0, 0, 0, 1}
}
func (x *WatchInstruction_PortForward_Target_TartGuestAgent) GetVmUid() string {
if x != nil {
return x.VmUid
}
return ""
}
var File_orchard_proto protoreflect.FileDescriptor var File_orchard_proto protoreflect.FileDescriptor
const file_orchard_proto_rawDesc = "" + const file_orchard_proto_rawDesc = "" +
"\n" + "\n" +
"\rorchard.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xfe\x04\n" + "\rorchard.proto\x1a\x1bgoogle/protobuf/empty.proto\"\x88\x06\n" +
"\x10WatchInstruction\x12O\n" + "\x10WatchInstruction\x12O\n" +
"\x13port_forward_action\x18\x01 \x01(\v2\x1d.WatchInstruction.PortForwardH\x00R\x11portForwardAction\x12C\n" + "\x13port_forward_action\x18\x01 \x01(\v2\x1d.WatchInstruction.PortForwardH\x00R\x11portForwardAction\x12C\n" +
"\x0fsync_vms_action\x18\x02 \x01(\v2\x19.WatchInstruction.SyncVMsH\x00R\rsyncVmsAction\x12I\n" + "\x0fsync_vms_action\x18\x02 \x01(\v2\x19.WatchInstruction.SyncVMsH\x00R\rsyncVmsAction\x12I\n" +
"\x11resolve_ip_action\x18\x03 \x01(\v2\x1b.WatchInstruction.ResolveIPH\x00R\x0fresolveIpAction\x1a\xb5\x02\n" + "\x11resolve_ip_action\x18\x03 \x01(\v2\x1b.WatchInstruction.ResolveIPH\x00R\x0fresolveIpAction\x1a\xbf\x03\n" +
"\vPortForward\x12\x18\n" + "\vPortForward\x12\x18\n" +
"\asession\x18\x01 \x01(\tR\asession\x12\x15\n" + "\asession\x18\x01 \x01(\tR\asession\x12\x15\n" +
"\x06vm_uid\x18\x02 \x01(\tR\x05vmUid\x12\x12\n" + "\x06vm_uid\x18\x02 \x01(\tR\x05vmUid\x12\x12\n" +
"\x04port\x18\x03 \x01(\rR\x04port\x12<\n" + "\x04port\x18\x03 \x01(\rR\x04port\x12<\n" +
"\x06target\x18\x04 \x01(\v2$.WatchInstruction.PortForward.TargetR\x06target\x1a\xa2\x01\n" + "\x06target\x18\x04 \x01(\v2$.WatchInstruction.PortForward.TargetR\x06target\x1a\xac\x02\n" +
"\x06Target\x12U\n" + "\x06Target\x12U\n" +
"\fhost_process\x18\x01 \x01(\v20.WatchInstruction.PortForward.Target.HostProcessH\x00R\vhostProcess\x1a8\n" + "\fhost_process\x18\x01 \x01(\v20.WatchInstruction.PortForward.Target.HostProcessH\x00R\vhostProcess\x12_\n" +
"\x10tart_guest_agent\x18\x02 \x01(\v23.WatchInstruction.PortForward.Target.TartGuestAgentH\x00R\x0etartGuestAgent\x1a8\n" +
"\vHostProcess\x12\x15\n" + "\vHostProcess\x12\x15\n" +
"\x06vm_uid\x18\x01 \x01(\tR\x05vmUid\x12\x12\n" + "\x06vm_uid\x18\x01 \x01(\tR\x05vmUid\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04nameB\a\n" + "\x04name\x18\x02 \x01(\tR\x04name\x1a'\n" +
"\x0eTartGuestAgent\x12\x15\n" +
"\x06vm_uid\x18\x01 \x01(\tR\x05vmUidB\a\n" +
"\x05value\x1a\t\n" + "\x05value\x1a\t\n" +
"\aSyncVMs\x1a<\n" + "\aSyncVMs\x1a<\n" +
"\tResolveIP\x12\x18\n" + "\tResolveIP\x12\x18\n" +
@@ -548,17 +613,18 @@ func file_orchard_proto_rawDescGZIP() []byte {
return file_orchard_proto_rawDescData return file_orchard_proto_rawDescData
} }
var file_orchard_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_orchard_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_orchard_proto_goTypes = []any{ var file_orchard_proto_goTypes = []any{
(*WatchInstruction)(nil), // 0: WatchInstruction (*WatchInstruction)(nil), // 0: WatchInstruction
(*PortForwardData)(nil), // 1: PortForwardData (*PortForwardData)(nil), // 1: PortForwardData
(*ResolveIPResult)(nil), // 2: ResolveIPResult (*ResolveIPResult)(nil), // 2: ResolveIPResult
(*WatchInstruction_PortForward)(nil), // 3: WatchInstruction.PortForward (*WatchInstruction_PortForward)(nil), // 3: WatchInstruction.PortForward
(*WatchInstruction_SyncVMs)(nil), // 4: WatchInstruction.SyncVMs (*WatchInstruction_SyncVMs)(nil), // 4: WatchInstruction.SyncVMs
(*WatchInstruction_ResolveIP)(nil), // 5: WatchInstruction.ResolveIP (*WatchInstruction_ResolveIP)(nil), // 5: WatchInstruction.ResolveIP
(*WatchInstruction_PortForward_Target)(nil), // 6: WatchInstruction.PortForward.Target (*WatchInstruction_PortForward_Target)(nil), // 6: WatchInstruction.PortForward.Target
(*WatchInstruction_PortForward_Target_HostProcess)(nil), // 7: WatchInstruction.PortForward.Target.HostProcess (*WatchInstruction_PortForward_Target_HostProcess)(nil), // 7: WatchInstruction.PortForward.Target.HostProcess
(*emptypb.Empty)(nil), // 8: google.protobuf.Empty (*WatchInstruction_PortForward_Target_TartGuestAgent)(nil), // 8: WatchInstruction.PortForward.Target.TartGuestAgent
(*emptypb.Empty)(nil), // 9: google.protobuf.Empty
} }
var file_orchard_proto_depIdxs = []int32{ var file_orchard_proto_depIdxs = []int32{
3, // 0: WatchInstruction.port_forward_action:type_name -> WatchInstruction.PortForward 3, // 0: WatchInstruction.port_forward_action:type_name -> WatchInstruction.PortForward
@@ -566,17 +632,18 @@ var file_orchard_proto_depIdxs = []int32{
5, // 2: WatchInstruction.resolve_ip_action:type_name -> WatchInstruction.ResolveIP 5, // 2: WatchInstruction.resolve_ip_action:type_name -> WatchInstruction.ResolveIP
6, // 3: WatchInstruction.PortForward.target:type_name -> WatchInstruction.PortForward.Target 6, // 3: WatchInstruction.PortForward.target:type_name -> WatchInstruction.PortForward.Target
7, // 4: WatchInstruction.PortForward.Target.host_process:type_name -> WatchInstruction.PortForward.Target.HostProcess 7, // 4: WatchInstruction.PortForward.Target.host_process:type_name -> WatchInstruction.PortForward.Target.HostProcess
8, // 5: Controller.Watch:input_type -> google.protobuf.Empty 8, // 5: WatchInstruction.PortForward.Target.tart_guest_agent:type_name -> WatchInstruction.PortForward.Target.TartGuestAgent
1, // 6: Controller.PortForward:input_type -> PortForwardData 9, // 6: Controller.Watch:input_type -> google.protobuf.Empty
2, // 7: Controller.ResolveIP:input_type -> ResolveIPResult 1, // 7: Controller.PortForward:input_type -> PortForwardData
0, // 8: Controller.Watch:output_type -> WatchInstruction 2, // 8: Controller.ResolveIP:input_type -> ResolveIPResult
1, // 9: Controller.PortForward:output_type -> PortForwardData 0, // 9: Controller.Watch:output_type -> WatchInstruction
8, // 10: Controller.ResolveIP:output_type -> google.protobuf.Empty 1, // 10: Controller.PortForward:output_type -> PortForwardData
8, // [8:11] is the sub-list for method output_type 9, // 11: Controller.ResolveIP:output_type -> google.protobuf.Empty
5, // [5:8] is the sub-list for method input_type 9, // [9:12] is the sub-list for method output_type
5, // [5:5] is the sub-list for extension type_name 6, // [6:9] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension extendee 6, // [6:6] is the sub-list for extension type_name
0, // [0:5] is the sub-list for field type_name 6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
} }
func init() { file_orchard_proto_init() } func init() { file_orchard_proto_init() }
@@ -591,6 +658,7 @@ func file_orchard_proto_init() {
} }
file_orchard_proto_msgTypes[6].OneofWrappers = []any{ file_orchard_proto_msgTypes[6].OneofWrappers = []any{
(*WatchInstruction_PortForward_Target_HostProcess_)(nil), (*WatchInstruction_PortForward_Target_HostProcess_)(nil),
(*WatchInstruction_PortForward_Target_TartGuestAgent_)(nil),
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
@@ -598,7 +666,7 @@ func file_orchard_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_orchard_proto_rawDesc), len(file_orchard_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_orchard_proto_rawDesc), len(file_orchard_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 8, NumMessages: 9,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },
+7 -1
View File
@@ -27,8 +27,14 @@ message WatchInstruction {
string name = 2; string name = 2;
} }
// Forward the byte stream to the Tart Guest Agent running inside the VM
message TartGuestAgent {
string vm_uid = 1;
}
oneof value { oneof value {
HostProcess host_process = 1; HostProcess host_process = 1;
TartGuestAgent tart_guest_agent = 2;
} }
} }
@@ -40,7 +46,7 @@ message WatchInstruction {
uint32 port = 3; uint32 port = 3;
// Typed alternative to the legacy port-forwarding destination, // Typed alternative to the legacy port-forwarding destination,
// adding support for host processes // adding support for host processes and Tart Guest Agent
Target target = 4; Target target = 4;
} }
message SyncVMs { message SyncVMs {