Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ tools:
$(GO) install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.0
$(GO) install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1
$(GO) install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@v2.24.0
$(GO) install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.24.0
$(GO) install ./hack/protoc-gen-kouja-client
$(GO) install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.5
$(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.15.0

Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ CGO_ENABLED=0 go build -trimpath -o bin/kouja-csi-node ./cmd/kouja-csi-node
Protocol generation and CRD generation require the tools installed by
`make tools`, followed by `make proto` and `make generate`.

## Client SDK

Generated gRPC stubs and the HTTP/JSON gateway live in `gen/kouja/v1`. The
merged OpenAPI 2.0 document is generated at
`gen/openapi/openapi.swagger.json`. The generated `gen/client` package wraps
both transports with mTLS helpers and typed methods for every RPC:

```go
grpcClient, err := client.NewGRPCFromFiles(target, certFile, keyFile, caFile)
httpClient, err := client.NewHTTPFromFiles("https://"+target, certFile, keyFile, caFile)
```

See [Client SDK](docs/clients.md) for complete examples, request-ID
propagation, error handling, and regeneration instructions.

## Install the agent

### 1. Prepare the host
Expand Down
13 changes: 13 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@ plugins:
out: gen
opt:
- paths=source_relative
- name: kouja-client
out: gen
- name: openapiv2
out: gen/openapi
opt:
- allow_merge=true
- merge_file_name=openapi
- json_names_for_fields=true
- proto3_optional_nullable=true
- preserve_rpc_order=true
- openapi_naming_strategy=simple
- simple_operation_ids=true
- openapi_configuration=proto/openapi.yaml
136 changes: 136 additions & 0 deletions docs/clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Client SDK

The repository ships a Go client for both agent transports:

| Artifact | Path | Generated |
|---|---|---|
| gRPC stubs and clients | `gen/kouja/v1/*.pb.go`, `*_grpc.pb.go` | `buf generate` |
| HTTP/JSON gateway | `gen/kouja/v1/*.pb.gw.go` | `buf generate` |
| OpenAPI 2.0 document | `gen/openapi/openapi.swagger.json` | `buf generate` |
| Typed gRPC and HTTP wrappers | `gen/client/client.gen.go` | `buf generate` |

The wrappers use the same protobuf request and response messages as the
generated stubs, so callers can switch transports without converting models.
The `protoc-gen-kouja-client` generator lives in
`hack/protoc-gen-kouja-client`; `make tools` installs it and `make proto`
regenerates `gen/client/client.gen.go` from the `google.api.http` annotations.

## gRPC

```go
package main

import (
"context"
"log"

"github.com/voidstarit/kouja/gen/client"
koujav1 "github.com/voidstarit/kouja/gen/kouja/v1"
)

func main() {
conn, err := client.NewGRPCFromFiles(
"storage.example:9443",
"/etc/kouja/tls/client.crt",
"/etc/kouja/tls/client.key",
"/etc/kouja/tls/ca.crt",
)
if err != nil {
log.Fatal(err)
}
defer conn.Close()

ctx := context.Background()
info, err := conn.GetInfo(ctx, &koujav1.GetInfoRequest{})
if err != nil {
log.Fatal(err)
}
log.Printf("host=%s version=%s ready=%t", info.GetHostUuid(), info.GetAgentVersion(), info.GetReady())
}
```

`NewGRPC` accepts a prepared `*tls.Config`; `NewGRPCFromFiles` loads the
certificate, key, and CA from disk. The target may be a plain
`host:port`, `https://host:port`, or a gRPC resolver target such as
`dns:///host:port`.

## HTTP/JSON

```go
package main

import (
"context"
"log"

"github.com/voidstarit/kouja/gen/client"
koujav1 "github.com/voidstarit/kouja/gen/kouja/v1"
"google.golang.org/protobuf/proto"
)

func main() {
httpClient, err := client.NewHTTPFromFiles(
"https://storage.example:9443",
"/etc/kouja/tls/client.crt",
"/etc/kouja/tls/client.key",
"/etc/kouja/tls/ca.crt",
)
if err != nil {
log.Fatal(err)
}
defer httpClient.CloseIdleConnections()

volume, err := httpClient.CreateVolume(context.Background(), &koujav1.CreateVolumeRequest{
VolumeId: "example-1",
SizeBytes: 1 << 30,
Vg: "kouja",
Export: proto.Bool(true),
Protocol: koujav1.Protocol_PROTOCOL_ISCSI,
Initiator: "iqn.2026-11.io.example:consumer",
})
if err != nil {
log.Fatal(err)
}
log.Printf("state=%s target=%s", volume.GetVolume().GetState(), volume.GetExport().GetTargetName())
}
```

The HTTP client implements every RPC in `volume.proto` and `admin.proto`,
including the admin operations. Requests and responses are encoded with
`protojson`, matching the agent's grpc-gateway surface.

Use `client.WithHTTPClient` to supply a custom `*http.Client` for timeouts,
proxies, tracing, or tests. Use `client.WithTLSConfig` to attach an mTLS
transport to the default client.

## Request IDs and errors

`client.WithRequestID` stores a correlation ID in the context. The gRPC client
sends it as `x-request-id` metadata and the HTTP client sends it as the
`X-Request-Id` header, matching the agent's request-ID propagation.

HTTP errors are returned as `*client.Error`. It exposes the HTTP status, the
gRPC code, the message, and any `google.rpc.Status` details. It also implements
`GRPCStatus()`, so `status.Code` and `status.Convert` work across both
transports:

```go
_, err := httpClient.GetVolume(ctx, &koujav1.GetVolumeRequest{VolumeId: "missing"})
if status.Code(err) == codes.NotFound {
// handle a missing volume
}
```

## Regenerating clients and the OpenAPI document

Install the pinned tools once, then regenerate:

```sh
make tools
make proto
```

`make proto` runs `buf lint` and `buf generate`. It writes the Go gRPC and
gateway code under `gen/kouja/v1` and merges the OpenAPI document into
`gen/openapi/openapi.swagger.json`. The OpenAPI title, description, and scheme
come from `proto/openapi.yaml`.
Loading
Loading