Compare commits

...
Author SHA1 Message Date
Fedor Korotkov 15754bcc8d Add FD-based network mode 2026-02-25 09:17:51 -05:00
Nikolay Edigaryev 37b8219579 Switch to github.com/open-telemetry/opentelemetry-swift fork (#1186)
* Switch to github.com/open-telemetry/opentelemetry-swift fork

* Use cirruslabs-owned fork
2026-01-29 16:54:57 +00:00
Nikolay Edigaryev f1aa591935 OpenTelemetry: set default resources, service.name and service.version (#1184)
* OpenTelemetry: set default resources, service.name and service.version

* Ensure that service name and version resources are set
2026-01-27 16:17:05 +01:00
11 changed files with 281 additions and 42 deletions
+4 -4
View File
@@ -1,5 +1,5 @@
{
"originHash" : "3a442857650816096c3d714a7dfa5847651ee6427bf098ae7fdbb6578509ddc6",
"originHash" : "0da1cc30fa3c41e8c2e2edcdd55706549908275a54f681203e0eeade279deab9",
"pins" : [
{
"identity" : "antlr4",
@@ -49,10 +49,10 @@
{
"identity" : "opentelemetry-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/open-telemetry/opentelemetry-swift",
"location" : "https://github.com/cirruslabs/opentelemetry-swift",
"state" : {
"branch" : "main",
"revision" : "a9b620766cced177bfc4089bbc47860859550473"
"branch" : "use-feedback-handler",
"revision" : "2040f383e2a8b0a568a7617d147f8f1e23abb298"
}
},
{
+2 -1
View File
@@ -25,7 +25,7 @@ let package = Package(
.package(url: "https://github.com/jozefizso/swift-xattr", from: "3.0.0"),
.package(url: "https://github.com/grpc/grpc-swift.git", .upToNextMajor(from: "1.27.0")),
.package(url: "https://buf.build/gen/swift/git/1.27.1-20260114140118-bd09c26a260f.1/cirruslabs_tart-guest-agent_grpc_swift.git", branch: "main"),
.package(url: "https://github.com/open-telemetry/opentelemetry-swift", branch: "main"),
.package(url: "https://github.com/cirruslabs/opentelemetry-swift", branch: "use-feedback-handler"),
.package(url: "https://github.com/open-telemetry/opentelemetry-swift-core", from: "2.3.0"),
],
@@ -48,6 +48,7 @@ let package = Package(
.product(name: "OpenTelemetryApi", package: "opentelemetry-swift-core"),
.product(name: "OpenTelemetrySdk", package: "opentelemetry-swift-core"),
.product(name: "OpenTelemetryProtocolExporterHTTP", package: "opentelemetry-swift"),
.product(name: "ResourceExtension", package: "opentelemetry-swift"),
], exclude: [
"OCI/Reference/Makefile",
"OCI/Reference/Reference.g4",
+17 -1
View File
@@ -243,6 +243,13 @@ struct Run: AsyncParsableCommand {
@Flag(help: ArgumentHelp("Restrict network access to the host-only network"))
var netHost: Bool = false
@Option(help: ArgumentHelp("Use externally managed connected datagram socket file descriptor for VM networking (e.g. --net-fd=3)", discussion: """
This option allows integrating Tart with externally launched networking helpers.
The provided file descriptor must reference a connected datagram socket.
""", valueName: "fd", visibility: .hidden))
var netFd: Int32?
@Option(help: ArgumentHelp("Set the root disk options (e.g. --root-disk-opts=\"ro\" or --root-disk-opts=\"caching=cached,sync=none\")",
discussion: """
Options are comma-separated and are as follows:
@@ -295,14 +302,19 @@ struct Run: AsyncParsableCommand {
netSoftnet = true
}
if let netFd = netFd, netFd < 0 {
throw ValidationError("--net-fd must be greater than or equal to 0")
}
// Check that no more than one network option is specified
var netFlags = 0
if netBridged.count > 0 { netFlags += 1 }
if netSoftnet { netFlags += 1 }
if netHost { netFlags += 1 }
if netFd != nil { netFlags += 1 }
if netFlags > 1 {
throw ValidationError("--net-bridged, --net-softnet and --net-host are mutually exclusive")
throw ValidationError("--net-bridged, --net-softnet, --net-host and --net-fd are mutually exclusive")
}
if graphics && noGraphics {
@@ -620,6 +632,10 @@ struct Run: AsyncParsableCommand {
}
func userSpecifiedNetwork(vmDir: VMDirectory) throws -> Network? {
if let netFd = netFd {
return try NetworkFD(fd: netFd)
}
var softnetExtraArguments: [String] = []
if let netSoftnetAllow = netSoftnetAllow {
+74
View File
@@ -0,0 +1,74 @@
import Darwin
import Foundation
import Semaphore
import Virtualization
class NetworkFD: Network {
private let fd: Int32
init(fd: Int32) throws {
self.fd = fd
try Self.validateFD(fd)
try Self.validateSocketType(fd)
try Self.validateConnected(fd)
}
func attachments() -> [VZNetworkDeviceAttachment] {
[VZFileHandleNetworkDeviceAttachment(fileHandle: FileHandle(fileDescriptor: fd))]
}
func run(_ sema: AsyncSemaphore) throws {
// no-op, only used for Softnet
}
func stop() async throws {
// no-op, only used for Softnet
}
private static func validateFD(_ fd: Int32) throws {
if fcntl(fd, F_GETFD) == -1 {
throw RuntimeError.VMConfigurationError(
"invalid --net-fd \(fd): file descriptor is not open (\(errnoDescription(errno)))"
)
}
}
private static func validateSocketType(_ fd: Int32) throws {
var socketType: Int32 = 0
var optionLength = socklen_t(MemoryLayout<Int32>.size)
if getsockopt(fd, SOL_SOCKET, SO_TYPE, &socketType, &optionLength) == -1 {
throw RuntimeError.VMConfigurationError(
"invalid --net-fd \(fd): file descriptor must reference a socket (\(errnoDescription(errno)))"
)
}
if socketType != SOCK_DGRAM {
throw RuntimeError.VMConfigurationError(
"invalid --net-fd \(fd): expected SOCK_DGRAM socket, got \(socketType)"
)
}
}
private static func validateConnected(_ fd: Int32) throws {
var address = sockaddr_storage()
var addressLength = socklen_t(MemoryLayout<sockaddr_storage>.size)
let result = withUnsafeMutablePointer(to: &address) { pointer in
pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPointer in
getpeername(fd, sockaddrPointer, &addressLength)
}
}
if result == -1 {
throw RuntimeError.VMConfigurationError(
"invalid --net-fd \(fd): socket must be connected (\(errnoDescription(errno)))"
)
}
}
private static func errnoDescription(_ code: CInt) -> String {
String(cString: strerror(code))
}
}
+12 -1
View File
@@ -2,6 +2,7 @@ import Foundation
import OpenTelemetryApi
import OpenTelemetrySdk
import OpenTelemetryProtocolExporterHttp
import ResourceExtension
class OTel {
let tracerProvider: TracerProviderSdk?
@@ -19,6 +20,13 @@ class OTel {
return nil
}
var resource = DefaultResources().get()
resource.merge(other: Resource(attributes: [
SemanticConventions.Service.name.rawValue: .string("tart"),
SemanticConventions.Service.version.rawValue: .string(CI.version)
]))
let spanExporter: SpanExporter
if let endpointRaw = ProcessInfo.processInfo.environment["OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"],
let endpoint = URL(string: endpointRaw) {
@@ -27,7 +35,10 @@ class OTel {
spanExporter = OtlpHttpTraceExporter()
}
let spanProcessor = SimpleSpanProcessor(spanExporter: spanExporter)
let tracerProvider = TracerProviderBuilder().add(spanProcessor: spanProcessor).build()
let tracerProvider = TracerProviderBuilder()
.add(spanProcessor: spanProcessor)
.with(resource: resource)
.build()
OpenTelemetry.registerTracerProvider(tracerProvider: tracerProvider)
+83
View File
@@ -0,0 +1,83 @@
import Darwin
import Foundation
import XCTest
@testable import tart
final class NetworkFDTests: XCTestCase {
func testAcceptsConnectedDatagramSocket() throws {
let (fdLeft, fdRight) = try makeDatagramSocketPair()
defer {
_ = close(fdLeft)
_ = close(fdRight)
}
let network = try NetworkFD(fd: fdLeft)
XCTAssertEqual(network.attachments().count, 1)
}
func testRejectsClosedFileDescriptor() throws {
let (fdLeft, fdRight) = try makeDatagramSocketPair()
defer { _ = close(fdRight) }
_ = close(fdLeft)
XCTAssertThrowsError(try NetworkFD(fd: fdLeft)) { error in
self.assertVMConfigurationError(error, contains: "file descriptor is not open")
}
}
func testRejectsNonSocketFileDescriptor() throws {
let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
XCTAssertTrue(FileManager.default.createFile(atPath: fileURL.path, contents: Data()))
defer { try? FileManager.default.removeItem(at: fileURL) }
let fd = open(fileURL.path, O_RDONLY)
XCTAssertGreaterThanOrEqual(fd, 0)
defer { _ = close(fd) }
XCTAssertThrowsError(try NetworkFD(fd: fd)) { error in
self.assertVMConfigurationError(error, contains: "must reference a socket")
}
}
func testRejectsUnconnectedDatagramSocket() throws {
let fd = socket(AF_UNIX, SOCK_DGRAM, 0)
XCTAssertGreaterThanOrEqual(fd, 0)
defer { _ = close(fd) }
XCTAssertThrowsError(try NetworkFD(fd: fd)) { error in
self.assertVMConfigurationError(error, contains: "socket must be connected")
}
}
private func makeDatagramSocketPair() throws -> (Int32, Int32) {
var fds: [Int32] = [-1, -1]
let result = socketpair(AF_UNIX, SOCK_DGRAM, 0, &fds)
if result == -1 {
throw RuntimeError.VMConfigurationError("failed to create a datagram socketpair for tests")
}
return (fds[0], fds[1])
}
private func assertVMConfigurationError(
_ error: Error,
contains expectedSubstring: String,
file: StaticString = #filePath,
line: UInt = #line
) {
guard case RuntimeError.VMConfigurationError(let message) = error else {
XCTFail("Expected RuntimeError.VMConfigurationError, got \(error)", file: file, line: line)
return
}
XCTAssertTrue(
message.contains(expectedSubstring),
"Expected message to contain \"\(expectedSubstring)\", got \"\(message)\"",
file: file,
line: line
)
}
}
@@ -0,0 +1,42 @@
import XCTest
@testable import tart
final class RunNetworkValidationTests: XCTestCase {
func testNetFdRejectsNegativeValue() throws {
XCTAssertThrowsError(try Run.parseAsRoot(["unused", "--net-fd=-1"])) { error in
self.assertError(error, contains: "--net-fd must be greater than or equal to 0")
}
}
func testNetFdConflictsWithNetBridged() throws {
XCTAssertThrowsError(try Run.parseAsRoot(["unused", "--net-fd", "3", "--net-bridged=en0"])) { error in
self.assertError(error, contains: "--net-bridged, --net-softnet, --net-host and --net-fd are mutually exclusive")
}
}
func testNetFdConflictsWithNetSoftnet() throws {
XCTAssertThrowsError(try Run.parseAsRoot(["unused", "--net-fd", "3", "--net-softnet"])) { error in
self.assertError(error, contains: "--net-bridged, --net-softnet, --net-host and --net-fd are mutually exclusive")
}
}
func testNetFdConflictsWithNetHost() throws {
XCTAssertThrowsError(try Run.parseAsRoot(["unused", "--net-fd", "3", "--net-host"])) { error in
self.assertError(error, contains: "--net-bridged, --net-softnet, --net-host and --net-fd are mutually exclusive")
}
}
private func assertError(
_ error: Error,
contains expectedSubstring: String,
file: StaticString = #filePath,
line: UInt = #line
) {
XCTAssertTrue(
String(describing: error).contains(expectedSubstring),
"Expected error to contain \"\(expectedSubstring)\", got \"\(error)\"",
file: file,
line: line
)
}
}
+14
View File
@@ -77,6 +77,20 @@ Note: that accessing host is only possible with the default NAT network. If you
[Softnet](https://github.com/cirruslabs/softnet) (via `tart run --net-softnet <VM NAME>)`, then the network isolation
is stricter and it's not possible to access the host.
## Using externally managed networking (`--net-fd`)
For advanced integrations, `tart run` can consume a pre-opened connected datagram socket via `--net-fd`.
Unlike `--net-softnet`, Tart will not launch Softnet or configure Softnet permissions in this mode.
External launcher is responsible for:
* creating a connected datagram socketpair (for example, `socketpair(AF_UNIX, SOCK_DGRAM, ...)`)
* starting `softnet --vm-fd ...` (or another networking helper) with one end of that socketpair
* starting `tart run --net-fd <FD> <VM NAME>` with the other end inherited into Tart
If the file descriptor is invalid, not a datagram socket, or not connected, `tart run` fails fast.
## Changing the default NAT subnet
To change the default network to `192.168.77.1`:
+1 -1
View File
@@ -4,6 +4,7 @@ go 1.25
require (
github.com/stretchr/testify v1.11.1
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/proto/otlp v1.9.0
google.golang.org/protobuf v1.36.11
)
@@ -11,7 +12,6 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/net v0.49.0 // indirect
+12 -29
View File
@@ -1,4 +1,3 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@@ -11,8 +10,6 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.5 h1:jP1RStw811EvUDzsUQ9oESqw2e4RqCjSAD9qIL8eMns=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.5/go.mod h1:WXNBZ64q3+ZUemCMXD9kYnr56H7CgZxDBHCVwstfl3s=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -25,48 +22,34 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
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=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM=
google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+20 -5
View File
@@ -11,7 +11,9 @@ import (
"testing"
"github.com/stretchr/testify/require"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
tracepkg "go.opentelemetry.io/proto/otlp/collector/trace/v1"
v1 "go.opentelemetry.io/proto/otlp/common/v1"
"google.golang.org/protobuf/proto"
)
@@ -70,7 +72,13 @@ func TestOpenTelemetry(t *testing.T) {
resourceSpans := traces[0].GetResourceSpans()
require.Len(t, resourceSpans, 1)
scopeSpans := resourceSpans[0].GetScopeSpans()
// Ensure that service name and version resources are set
resourceSpan := resourceSpans[0]
stringAttributes := stringAttributesToMap(resourceSpan.GetResource().GetAttributes())
require.Equal(t, "tart", stringAttributes[string(semconv.ServiceNameKey)])
require.Equal(t, "SNAPSHOT", stringAttributes[string(semconv.ServiceVersionKey)])
scopeSpans := resourceSpan.GetScopeSpans()
require.Len(t, scopeSpans, 1)
spans := scopeSpans[0].GetSpans()
@@ -81,10 +89,7 @@ func TestOpenTelemetry(t *testing.T) {
require.Equal(t, "list", span.Name)
// Ensure that CIRRUS_SENTRY_TAGS are propagated
stringAttributes := map[string]string{}
for _, attribute := range span.GetAttributes() {
stringAttributes[attribute.GetKey()] = attribute.GetValue().GetStringValue()
}
stringAttributes = stringAttributesToMap(span.GetAttributes())
require.Equal(t, stringAttributes["A"], "B")
require.Equal(t, stringAttributes["C"], "D")
@@ -92,3 +97,13 @@ func TestOpenTelemetry(t *testing.T) {
require.Equal(t, "00000000000000000000000000000001", hex.EncodeToString(span.GetTraceId()))
require.Equal(t, "0000000000000001", hex.EncodeToString(span.GetParentSpanId()))
}
func stringAttributesToMap(attributes []*v1.KeyValue) map[string]string {
result := map[string]string{}
for _, attribute := range attributes {
result[attribute.GetKey()] = attribute.GetValue().GetStringValue()
}
return result
}