diff --git a/.gitignore b/.gitignore index 2f98d91..7301794 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,7 @@ # Go workspace file go.work -# OSP Folder -cosi-osp +# Demo +cosi-osp/ +demo/ +*.cast diff --git a/README.md b/README.md index dcf10b3..2020403 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ A Container Object Storage Interface (COSI) library and other helpful utilities - [Contributing](#contributing) - [Prior Art](#prior-art) -[![asciicast](https://asciinema.org/a/605022.svg)](https://asciinema.org/a/605022) +![gocosi usage demo](./resources/demo.gif) ## Quick Start @@ -65,9 +65,6 @@ cosi-osp |-------------------|-----------------------------------------|----------------------------------------------------------------| | `-module` | `example.com/cosi-osp` | Override name for your new module. | | `-dir` | `cosi-osp` | Location/Path, where the module will be created. | -| `-image` | `gcr.io/distroless/static:latest` | Override the default base Docker image. | -| `-rootless-image` | `docker.io/rockylinux/rockylinux:9-ubi` | Override the default base Docker image for rootless container. | -| `-rootless` | `false` | Generate the Dockerfile for rootless container. | ## Features @@ -101,6 +98,26 @@ To cater to diverse logging preferences, the gocosi package seamlessly integrate - `X_COSI_ENDPOINT_PERMS` (default: `0755`) - it should be set when the COSI endpoint is a UNIX socket file. It determines the file permissions (in octal notation) of the UNIX socket file. If the COSI endpoint is a TCP socket, this setting has no effect. - `X_COSI_ENDPOINT_USER` (default: *The user that starts the process*) - it should be set when the COSI endpoint is a UNIX socket file. It determines the owner (user) of the UNIX socket file. If the COSI endpoint is a TCP socket, this setting has no effect. - `X_COSI_ENDPOINT_GROUP` (default: *The group that starts the process*) - it should be set when the COSI endpoint is a UNIX socket file. It determines the group ownership of the UNIX socket file. If the COSI endpoint is a TCP socket, this setting has no effect. +- **Endpoint OTLP/HTTP** (default: `https://localhost:4317` or `https://localhost:4318`) - target URL to which the exporter is going to send spans or metrics. The endpoint MUST be a valid URL with scheme (http or https) and host, MAY contain a port, SHOULD contain a path and MUST NOT contain other parts (such as query string or fragment). [[spec]][otlp-exporter-spec] + - `OTEL_EXPORTER_OTLP_ENDPOINT` + - `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` + - `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` +- **Certificate File** - the trusted certificate to use when verifying a server's TLS credentials. Should only be used for a secure connection. [[spec]][otlp-exporter-spec] + - `OTEL_EXPORTER_OTLP_CERTIFICATE` + - `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` + - `OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE` +- **Headers** - key-value pairs to be used as headers associated with gRPC or HTTP requests. [[spec]][otlp-exporter-spec] + - `OTEL_EXPORTER_OTLP_HEADERS` + - `OTEL_EXPORTER_OTLP_TRACES_HEADERS` + - `OTEL_EXPORTER_OTLP_METRICS_HEADERS` +- **Compression** - compression key for supported compression types. Supported compression: `gzip`. [[spec]][otlp-exporter-spec] + - `OTEL_EXPORTER_OTLP_COMPRESSION` + - `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` + - `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` +- **Timeout** (default: `10s`) - maximum time the OTLP exporter will wait for each batch export. [[spec]][otlp-exporter-spec] + - `OTEL_EXPORTER_OTLP_TIMEOUT` + - `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` + - `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` ## Contributing @@ -109,3 +126,7 @@ You want to contibute? Hop into the [CONTRIBUTING.md](CONTRIBUTING.md) and find ## Prior Art This project was inspired by [rexray/gocsi](https://github.com/rexray/gocsi) and [dell/gocsi](https://github.com/dell/gocsi). + + + +[otlp-exporter-spec]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md diff --git a/cmd/bootstrap/template/Dockerfile.tpl b/cmd/bootstrap/template/Dockerfile.tpl index 868e15d..0f584e7 100644 --- a/cmd/bootstrap/template/Dockerfile.tpl +++ b/cmd/bootstrap/template/Dockerfile.tpl @@ -48,7 +48,7 @@ VOLUME [ "/var/lib/cosi" ] HEALTHCHECK NONE # Set the default environment. -ENV COSI_ENDPOINT="/var/lib/cosi/cosi.sock" +ENV COSI_ENDPOINT="unix:///var/lib/cosi/cosi.sock" COPY --from=builder /cosi-osp/build/cosi-osp /usr/bin/cosi-osp # Set the correct entrypoint and command arguments. diff --git a/cmd/bootstrap/template/main.go.tpl b/cmd/bootstrap/template/main.go.tpl index f5301b2..cbf6ebf 100644 --- a/cmd/bootstrap/template/main.go.tpl +++ b/cmd/bootstrap/template/main.go.tpl @@ -2,11 +2,14 @@ package main import ( "context" + stdlog "log" "os" "github.com/doomshrine/gocosi" "github.com/go-logr/logr" - + "github.com/go-logr/stdr" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" // FIXME: this might need manual update "{{ .ModPath }}/servers/identity" "{{ .ModPath }}/servers/provisioner" @@ -14,6 +17,9 @@ import ( var ( driverName = "cosi.example.com" // FIXME: replace with your own driver name + driverVersion = "v0.1.0" // FIXME: replace with your own driver version + + exporterKind = gocosi.HTTPExporter log logr.Logger ) @@ -25,18 +31,29 @@ func init() { // - https://github.com/go-logr/logr/tree/master/slogr // - https://github.com/go-logr/stdr // - https://github.com/bombsimon/logrusr + stdr.SetVerbosity(10) + log = stdr.New(stdlog.New(os.Stdout, "", stdlog.LstdFlags)) } func main() { gocosi.SetLogger(log) + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName(driverName), + semconv.ServiceVersion(driverVersion), + ) + // If there is any additional confifuration needed for your COSI Driver, // put it below this line. driver, err := gocosi.New( identity.New(driverName, log), provisioner.New(log), + res, gocosi.WithDefaultGRPCOptions(), + gocosi.WithDefaultMetricExporter(exporterKind), + gocosi.WithDefaultTraceExporter(exporterKind), ) if err != nil { log.Error(err, "failed to create COSI Driver") diff --git a/go.mod b/go.mod index dc54aa1..4b80722 100644 --- a/go.mod +++ b/go.mod @@ -10,23 +10,33 @@ require ( github.com/stretchr/testify v1.8.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0 go.opentelemetry.io/otel v1.18.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.41.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.41.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 + go.opentelemetry.io/otel/sdk v1.18.0 + go.opentelemetry.io/otel/sdk/metric v0.41.0 google.golang.org/grpc v1.58.1 sigs.k8s.io/container-object-storage-interface-spec v0.1.0 ) require ( - cloud.google.com/go/compute v1.21.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect go.opentelemetry.io/otel/metric v1.18.0 // indirect go.opentelemetry.io/otel/trace v1.18.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect golang.org/x/net v0.15.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 790e59d..2c8e325 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,11 @@ -cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= -cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -11,24 +13,33 @@ github.com/doomshrine/must v1.0.0 h1:FOIwGrEOoFyyje7A6ngoSUOAzD2l1MeOnnWllrIr3hc github.com/doomshrine/must v1.0.0/go.mod h1:hYjy87phi9Byu0+tNc/ix/HM5WlIGJNqqbCNb2gfNUA= github.com/doomshrine/testcontext v1.0.0 h1:hLaUqtQV1AubfEZYA8e6ETg1AIorHTHCxUaHBZbqRZY= github.com/doomshrine/testcontext v1.0.0/go.mod h1:MKUk0UOeq6GOKwqGJYWR6BuLFc0ZiZ4wZ/Pyq9iGNeU= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.5 h1:3IZOAnD058zZllQTZNBioTlrzrBG/IjpiZ133IEtusM= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.5/go.mod h1:xbKERva94Pw2cPen0s79J3uXmGzbbpDYFBFDlZ4mV/w= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0 h1:2cz5kSrxzMYHiWOBbKj8itQm+nRykkB8aMv4ThcHYHA= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= @@ -37,67 +48,60 @@ 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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 h1:7XZai4VhA473clBrOqqHdjHBImGfyEtv0qW4nnn/kAo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0 h1:b8xjZxHbLrXAum4SxJd1Rlm7Y/fKaB+6ACI7/e5EfSA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0/go.mod h1:1ei0a32xOGkFoySu7y1DAHfcuIhC0pNZpvY2huXuMy4= -go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= -go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.41.0 h1:k0k7hFNDd8K4iOMJXj7s8sHaC4mhTlAeppRmZXLgZ6k= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.41.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.41.0 h1:HgbDTD8pioFdY3NRc/YCvsWjqQPtweGyXxa32LgnTOw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.41.0/go.mod h1:tmvt/yK5Es5d6lHYWerLSOna8lCEfrBVX/a9M0ggqss= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.41.0 h1:iV3BOgW4fry1Riw9dwypigqlIYWXvSRVT2RJmblzo40= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.41.0/go.mod h1:7PGzqlKrxIRmbj5tlNW0nTkYZ5fHXDgk6Fy8/KjR0CI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 h1:6pu8ttx76BxHf+xz/H77AUZkPF3cwWzXqAUsXhVKI18= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0/go.mod h1:IOmXxPrxoxFMXdNy7lfDmE8MzE61YPcurbUm0SMjerI= go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= -go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY= +go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M= +go.opentelemetry.io/otel/sdk/metric v0.41.0 h1:c3sAt9/pQ5fSIUfl0gPtClV3HhE18DCVzByD33R/zsk= +go.opentelemetry.io/otel/sdk/metric v0.41.0/go.mod h1:PmOmSt+iOklKtIg5O4Vz9H/ttcRFSNTgii+E1KGyn1w= go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= -golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= -golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= +google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf h1:guOdSPaeFgN+jEJwTo1dQ71hdBm+yKSCCKuTRkJzcVo= google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58= google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gocosi.go b/gocosi.go index 76538f3..99ee04f 100644 --- a/gocosi.go +++ b/gocosi.go @@ -24,6 +24,7 @@ import ( "github.com/doomshrine/must" "github.com/go-logr/logr" + "go.opentelemetry.io/otel/sdk/resource" "google.golang.org/grpc" cosi "sigs.k8s.io/container-object-storage-interface-spec" ) @@ -36,6 +37,10 @@ type Driver struct { identity cosi.IdentityServer provisioner cosi.ProvisionerServer + resource *resource.Resource + traceShutdownFunc func(ctx context.Context) error + metricShutdownFunc func(ctx context.Context) error + endpoint *Endpoint grpcOptions []grpc.ServerOption @@ -47,11 +52,13 @@ type Driver struct { type Option func(*Driver) error // New creates a new instance of the COSI driver. -func New(identity cosi.IdentityServer, provisioner cosi.ProvisionerServer, opts ...Option) (*Driver, error) { +func New(identity cosi.IdentityServer, provisioner cosi.ProvisionerServer, res *resource.Resource, opts ...Option) (*Driver, error) { p := &Driver{ identity: identity, provisioner: provisioner, + resource: res, + endpoint: &Endpoint{ permissions: 0o755, address: must.Do(url.Parse(cosiSocket)), diff --git a/gocosi_test.go b/gocosi_test.go index 62f1754..4ef9fb2 100644 --- a/gocosi_test.go +++ b/gocosi_test.go @@ -57,6 +57,7 @@ func TestNew(t *testing.T) { _, err := New( tc.identity, tc.provisioner, + nil, tc.options..., ) assert.NoError(t, err) @@ -80,6 +81,7 @@ func TestRun(t *testing.T) { return New( identity, provisioner, + nil, WithCOSIEndpoint(testutils.MustMkUnixTemp("cosi.sock")), ) }()), diff --git a/options.go b/options.go index 30d4472..be57455 100644 --- a/options.go +++ b/options.go @@ -27,6 +27,15 @@ import ( "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/propagation" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" "google.golang.org/grpc" ) @@ -128,3 +137,158 @@ func WithGRPCOptions(opts ...grpc.ServerOption) Option { return joinedErrors } } + +// ExporterKind is an enumeration representing different exporter types. +type ExporterKind int + +const ( + // HTTPExporter represents an HTTP telemetry exporter. + HTTPExporter ExporterKind = iota + + // GRPCExporter represents a gRPC telemetry exporter. + GRPCExporter ExporterKind = iota +) + +// WithDefaultMetricExporter returns an Option function to set the default metric exporter based on the provided kind. +func WithDefaultMetricExporter(kind ExporterKind) Option { + switch kind { + case HTTPExporter: + return WithHTTPMetricExporter() + + case GRPCExporter: + return WithGRPCMetricExporter() + + default: + panic(fmt.Sprintf("unexpected kind: %#+v", kind)) + } +} + +// WithHTTPMetricExporter returns an Option function to configure an HTTP metric exporter. +func WithHTTPMetricExporter(opt ...otlpmetrichttp.Option) Option { + return func(d *Driver) error { + exp, err := otlpmetrichttp.New(context.TODO(), opt...) + if err != nil { + return fmt.Errorf("unable to create new OTLP Metric HTTP Exporter: %w", err) + } + + shutdown, err := registerMetricExporter(d.resource, exp) + if err != nil { + return fmt.Errorf("unable to register OTLP Metric HTTP Exporter: %w", err) + } + + d.metricShutdownFunc = shutdown + + return nil + } +} + +// WithGRPCMetricExporter returns an Option function to configure a gRPC metric exporter. +func WithGRPCMetricExporter(opt ...otlpmetricgrpc.Option) Option { + return func(d *Driver) error { + exp, err := otlpmetricgrpc.New(context.TODO(), opt...) + if err != nil { + return fmt.Errorf("unable to create new OTLP Metric GRPC Exporter: %w", err) + } + + shutdown, err := registerMetricExporter(d.resource, exp) + if err != nil { + return fmt.Errorf("unable to register OTLP Metric GRPC Exporter: %w", err) + } + + d.metricShutdownFunc = shutdown + + return nil + } +} + +// WithDefaultTraceExporter returns an Option function to set the default trace exporter based on the provided kind. +func WithDefaultTraceExporter(kind ExporterKind) Option { + switch kind { + case HTTPExporter: + return WithHTTPTraceExporter() + + case GRPCExporter: + return WithGRPCTraceExporter() + + default: + panic(fmt.Sprintf("unexpected kind: %#+v", kind)) + } +} + +// WithHTTPTraceExporter returns an Option function to configure an HTTP trace exporter. +func WithHTTPTraceExporter(opt ...otlptracehttp.Option) Option { + return func(d *Driver) error { + exp, err := otlptracehttp.New(context.TODO(), opt...) + if err != nil { + return fmt.Errorf("unable to create new OTLP Trace HTTP Exporter: %w", err) + } + + shutdown, err := registerTraceExporter(d.resource, exp) + if err != nil { + return fmt.Errorf("unable to register OTLP Trace HTTP Exporter: %w", err) + } + + d.traceShutdownFunc = shutdown + + return nil + } +} + +// WithGRPCTraceExporter returns an Option function to configure a gRPC trace exporter. +func WithGRPCTraceExporter(opt ...otlptracegrpc.Option) Option { + return func(d *Driver) error { + exp, err := otlptracegrpc.New(context.TODO(), opt...) + if err != nil { + return fmt.Errorf("unable to create new OTLP Trace GRPC Exporter: %w", err) + } + + shutdown, err := registerTraceExporter(d.resource, exp) + if err != nil { + return fmt.Errorf("unable to register OTLP Trace GRPC Exporter: %w", err) + } + + d.traceShutdownFunc = shutdown + + return nil + } +} + +func registerTraceExporter(res *resource.Resource, exporter sdktrace.SpanExporter) (func(context.Context) error, error) { + bsp := sdktrace.NewBatchSpanProcessor(exporter) + + options := []sdktrace.TracerProviderOption{ + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithSpanProcessor(bsp), + } + if res != nil { + options = append(options, sdktrace.WithResource(res)) + } + + tp := sdktrace.NewTracerProvider(options...) + otel.SetTracerProvider(tp) + + // set global propagator to tracecontext (the default is no-op). + otel.SetTextMapPropagator(propagation.TraceContext{}) + + // Shutdown will flush any remaining spans and shut down the exporter. + return tp.Shutdown, nil +} + +func registerMetricExporter(res *resource.Resource, exporter sdkmetric.Exporter) (func(context.Context) error, error) { + // This reader is used as a stand-in for a reader that will actually export + // data. See exporters in the go.opentelemetry.io/otel/exporters package + // for more information. + reader := sdkmetric.NewPeriodicReader(exporter) + + options := []sdkmetric.Option{ + sdkmetric.WithReader(reader), + } + if res != nil { + options = append(options, sdkmetric.WithResource(res)) + } + + mp := sdkmetric.NewMeterProvider(options...) + otel.SetMeterProvider(mp) + + return mp.Shutdown, nil +} diff --git a/options_test.go b/options_test.go index aee253b..95b34f0 100644 --- a/options_test.go +++ b/options_test.go @@ -13,3 +13,127 @@ // limitations under the License. package gocosi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestWithDefaultMetricExporter(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + kind ExporterKind + }{ + { + name: "default HTTP metric exporter", + kind: HTTPExporter, + }, + { + name: "default GRPC metric exporter", + kind: GRPCExporter, + }, + } { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + opt := WithDefaultMetricExporter(tc.kind) + + d := &Driver{} + + err := opt(d) + assert.NoError(t, err) + }) + } +} + +func TestWithHTTPMetricExporter(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + }{} { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + }) + } +} + +func TestWithGRPCMetricExporter(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + }{} { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + }) + } +} + +func TestWithDefaultTraceExporter(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + kind ExporterKind + }{ + { + name: "default HTTP trace exporter", + kind: HTTPExporter, + }, + { + name: "default GRPC trace exporter", + kind: GRPCExporter, + }, + } { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + opt := WithDefaultTraceExporter(tc.kind) + + d := &Driver{} + + err := opt(d) + assert.NoError(t, err) + }) + } +} + +func TestWithHTTPTraceExporter(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + }{} { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + }) + } +} + +func TestWithGRPCTraceExporter(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + }{} { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + }) + } +} diff --git a/resources/demo.gif b/resources/demo.gif new file mode 100644 index 0000000..657529b Binary files /dev/null and b/resources/demo.gif differ diff --git a/scripts/demo.sh b/scripts/demo.sh new file mode 100755 index 0000000..7cc8484 --- /dev/null +++ b/scripts/demo.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +VERSION=v0.2.0 + +if [[ ! -f ~/.demo-magic.sh ]]; then + curl -fsSL https://raw.githubusercontent.com/paxtonhare/demo-magic/master/demo-magic.sh > ~/.demo-magic.sh +fi + +rm -rf ./demo +mkdir -p ./demo + +DIR="$(pwd)" + +# subshell execution +( +cd demo + +source ~/.demo-magic.sh + +TYPE_SPEED=30 +PROMPT_TIMEOUT=1 +DEMO_PROMPT="${GREEN}\$${COLOR_RESET} " + +pe "ls -l" + +p "go run github.com/doomshrine/gocosi/cmd/bootstrap@${VERSION} -module example.com/your/cosi-osp -dir cosi-osp" +go run "${DIR}/cmd/bootstrap" -module example.com/your/cosi-osp -dir cosi-osp + +pe "tree -a cosi-osp" +) diff --git a/scripts/recording.sh b/scripts/recording.sh new file mode 100755 index 0000000..e917222 --- /dev/null +++ b/scripts/recording.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e + +rm -rf ./resources/{*.cast,*.gif} + +go clean -modcache +go clean -cache + +asciinema rec \ + --title '[DEMO]: gocosi' \ + --command ./scripts/demo.sh \ + --cols 120 \ + --rows 20 \ + ./resources/cosi-demo.cast + +agg ./resources/cosi-demo.cast ./resources/demo.gif diff --git a/metrics.go b/telemetry.go similarity index 100% rename from metrics.go rename to telemetry.go