mirror of
https://github.com/cirruslabs/tart.git
synced 2026-10-01 19:51:10 +02:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15754bcc8d | ||
|
|
37b8219579 | ||
|
|
f1aa591935 |
+4
-4
@@ -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
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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`:
|
||||
|
||||
@@ -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
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user