mirror of
https://github.com/psviderski/uncloud.git
synced 2026-11-10 20:54:17 +00:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f68c9859d9 | ||
|
|
c65f352250 | ||
|
|
751ea97f47 | ||
|
|
abb85f8734 | ||
|
|
37f817aa58 | ||
|
|
db55df64a2 | ||
|
|
e9e7b3a61e | ||
|
|
e81e130544 | ||
|
|
1e471d3ec4 | ||
|
|
f7be7c55c9 | ||
|
|
6ab02f95fe | ||
|
|
9a4098694c | ||
|
|
992b819f5b | ||
|
|
842f796e38 | ||
|
|
0f6c03240b | ||
|
|
c6d0371f15 | ||
|
|
5e01db5e20 | ||
|
|
8aedc2bf19 | ||
|
|
7a88865f64 | ||
|
|
a24fc11caf | ||
|
|
c7e0e36daa | ||
|
|
01426ca03d | ||
|
|
3f86b3de44 | ||
|
|
aaf67687d2 | ||
|
|
1c29d4d5d0 | ||
|
|
00d68d9465 | ||
|
|
43bf2baaf7 | ||
|
|
613cd6e418 | ||
|
|
7a7a313426 | ||
|
|
a5ab6e3788 | ||
|
|
5a956e72de | ||
|
|
ac56754281 | ||
|
|
b7e224a1ef | ||
|
|
351698c280 | ||
|
|
fa77edf53e | ||
|
|
b99abb7c39 | ||
|
|
c2ae11a293 | ||
|
|
f1555259de | ||
|
|
50f8fbcfda | ||
|
|
73f29092ff | ||
|
|
08b24af341 | ||
|
|
b9c54f1ff5 | ||
|
|
4d76dd601c | ||
|
|
3af9936d27 | ||
|
|
dc721e511e | ||
|
|
054b3b8fd4 | ||
|
|
b3896ff642 | ||
|
|
5b2823de1e | ||
|
|
0007f76d09 | ||
|
|
f8e6d1caaa | ||
|
|
e684795aee | ||
|
|
7964273552 | ||
|
|
4ad4bce29d | ||
|
|
d670d18810 | ||
|
|
a1957a156e | ||
|
|
6266e9ec3a | ||
|
|
7c4fcde88d | ||
|
|
d36b28c55a | ||
|
|
6b41340f1d | ||
|
|
4b9ea43402 | ||
|
|
e5f23a9189 | ||
|
|
15e3e32b47 |
@@ -2,6 +2,7 @@
|
||||
*
|
||||
|
||||
# Allow files and directories.
|
||||
!api/
|
||||
!cmd/
|
||||
!internal/
|
||||
!pkg/
|
||||
|
||||
@@ -57,7 +57,8 @@ jobs:
|
||||
|
||||
The commands below show how to install the nightly version of uncloud.
|
||||
|
||||
> **Warning**: These are nightly development builds and may be unstable.
|
||||
> [!WARNING]
|
||||
> These are nightly development builds and may be unstable.
|
||||
|
||||
**Install uncloud CLI:**
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ uc context use <name> # Switch context
|
||||
- **Package naming**: Use clear, descriptive names
|
||||
- **Error handling**: Wrap errors with context using `fmt.Errorf`
|
||||
- **Logging**: Use structured logging with levels
|
||||
- **gRPC**: Services defined in `internal/machine/api/pb/`
|
||||
- **gRPC**: Services defined in `api/pb/`
|
||||
|
||||
### Testing
|
||||
|
||||
@@ -196,7 +196,6 @@ uc context use <name> # Switch context
|
||||
- Unit tests alongside source files (`*_test.go`)
|
||||
- Integration tests in `test/e2e/`
|
||||
- Test fixtures in `test/fixtures/`
|
||||
- Use table driven tests whenever possible
|
||||
- Use the `testify` library for assertions (e.g., `require.Equal`, `assert.Nil`)
|
||||
|
||||
### Dependencies
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ make lint-and-fix
|
||||
|
||||
## Code generation
|
||||
|
||||
Update the generated Go code for the machine gRPC API after modifying `.proto` files:
|
||||
Update the generated Go code for the gRPC API after modifying files in [`api/pb`](./api/pb):
|
||||
|
||||
```shell
|
||||
mise run proto
|
||||
|
||||
@@ -154,7 +154,6 @@ Here is a diagram of an Uncloud multi-provider cluster of 3 machines:
|
||||
```bash
|
||||
$ uc machine init --name oracle-vm ubuntu@152.67.101.197
|
||||
|
||||
Downloading Uncloud install script: https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/install.sh
|
||||
⏳ Running Uncloud install script...
|
||||
✓ Docker is already installed.
|
||||
⏳ Installing Docker...
|
||||
@@ -167,11 +166,10 @@ Downloading Uncloud install script: https://raw.githubusercontent.com/psviderski
|
||||
✓ uncloudd binary installed: /usr/local/bin/uncloudd
|
||||
⏳ Downloading uninstall script: https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/uninstall.sh
|
||||
✓ uncloud-uninstall script installed: /usr/local/bin/uncloud-uninstall
|
||||
✓ Systemd unit file created: /etc/systemd/system/uncloud.socket
|
||||
✓ Systemd unit file created: /etc/systemd/system/uncloud.service
|
||||
Created symlink /etc/systemd/system/sockets.target.wants/uncloud.socket → /etc/systemd/system/uncloud.socket.
|
||||
Created symlink /etc/systemd/system/multi-user.target.wants/uncloud.service → /etc/systemd/system/uncloud.service.
|
||||
⏳ Downloading uncloud-corrosion binary: https://github.com/psviderski/corrosion/releases/latest/download/corrosion-aarch64-unknown-linux-gnu.tar.gz
|
||||
✓ uncloud-corrosion binary installed: /usr/local/bin/uncloud-corrosion
|
||||
✓ Systemd unit file created: /etc/systemd/system/uncloud-corrosion.service
|
||||
⏳ Starting Uncloud machine daemon (uncloud.service)...
|
||||
✓ Uncloud machine daemon started.
|
||||
✓ Uncloud installed on the machine successfully! 🎉
|
||||
@@ -190,21 +188,20 @@ DNS records updated to use only the internet-reachable machines running caddy se
|
||||
*.xuw3xd.cluster.uncloud.run A → 152.67.101.197
|
||||
```
|
||||
|
||||
1. The CLI SSHs into the machine and installs Docker, the `uncloudd` machine daemon and
|
||||
[corrosion](https://github.com/superfly/corrosion) service, managed by systemd.
|
||||
1. The CLI SSHs into the machine and installs Docker and the `uncloudd` machine daemon, managed by systemd.
|
||||
2. Generates a unique WireGuard key pair, allocates a dedicated subnet `10.210.0.0/24` for the machine and its
|
||||
containers, and configures `uncloudd` accordingly. All subsequent communication happens with `uncloudd`
|
||||
through its gRPC API over SSH.
|
||||
3. Configures and starts `corrosion`, a CRDT-based distributed SQLite database to share cluster state between machines.
|
||||
3. Configures and starts [corrosion](https://github.com/superfly/corrosion), a CRDT-based distributed SQLite database to
|
||||
share cluster state between machines, as a Docker container managed by `uncloudd`.
|
||||
4. Creates a Docker bridge network connected to the WireGuard interface.
|
||||
5. This machine becomes an entry point for the newly created cluster which is stored in the cluster config under
|
||||
`~/.config/uncloud` on your local machine.
|
||||
5. This machine becomes an entry point for the newly created cluster. Its address is stored as a connection info in the
|
||||
cluster config at `~/.config/uncloud/config.yaml` on your local machine.
|
||||
|
||||
**When you add another machine:**
|
||||
|
||||
```bash
|
||||
$ uc machine add --name hetzner-server root@5.223.45.199
|
||||
Downloading Uncloud install script: https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/install.sh
|
||||
⏳ Running Uncloud install script...
|
||||
✓ Docker is already installed.
|
||||
✓ Linux user and group 'uncloud' created.
|
||||
@@ -213,11 +210,10 @@ Downloading Uncloud install script: https://raw.githubusercontent.com/psviderski
|
||||
✓ uncloudd binary installed: /usr/local/bin/uncloudd
|
||||
⏳ Downloading uninstall script: https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/uninstall.sh
|
||||
✓ uncloud-uninstall script installed: /usr/local/bin/uncloud-uninstall
|
||||
✓ Systemd unit file created: /etc/systemd/system/uncloud.socket
|
||||
✓ Systemd unit file created: /etc/systemd/system/uncloud.service
|
||||
Created symlink /etc/systemd/system/sockets.target.wants/uncloud.socket → /etc/systemd/system/uncloud.socket.
|
||||
Created symlink /etc/systemd/system/multi-user.target.wants/uncloud.service → /etc/systemd/system/uncloud.service.
|
||||
⏳ Downloading uncloud-corrosion binary: https://github.com/psviderski/corrosion/releases/latest/download/corrosion-x86_64-unknown-linux-gnu.tar.gz
|
||||
✓ uncloud-corrosion binary installed: /usr/local/bin/uncloud-corrosion
|
||||
✓ Systemd unit file created: /etc/systemd/system/uncloud-corrosion.service
|
||||
⏳ Starting Uncloud machine daemon (uncloud.service)...
|
||||
✓ Uncloud machine daemon started.
|
||||
✓ Uncloud installed on the machine successfully! 🎉
|
||||
@@ -246,9 +242,10 @@ hetzner-server Up 10.210.1.1/24 5.223.45.199 5.223.45.199:51820, [2
|
||||
3. Registers the second machine in the cluster state and exchanges WireGuard keys with the first machine.
|
||||
4. Both machines establish a WireGuard tunnel between each other, allowing Docker containers connected to the bridge
|
||||
network to communicate directly across machines.
|
||||
5. Configures and starts `corrosion` on the second machine to sync the cluster state.
|
||||
5. Configures and starts the `uncloud-corrosion` container on the second machine to sync the cluster state.
|
||||
6. The second machine is added as an alternative entry point in the cluster config.
|
||||
7. If one of the machines goes offline, the other machine can still serve cluster operations.
|
||||
7. If one of the machines goes offline, the other machine can still be used by `uc` to connect and run cluster
|
||||
operations.
|
||||
|
||||
If one more machine is added, the process repeats with a new subnet. The new machine needs to establish a WireGuard
|
||||
connection with only one of the existing machines. Other machines will learn about it through the shared cluster state
|
||||
@@ -326,9 +323,9 @@ I'm grateful to the following projects that inspired Uncloud's design and implem
|
||||
* [Tailscale](https://tailscale.com/) — for pioneering the vision of decentralised flat mesh networking with an amazing
|
||||
user experience that feels like magic.
|
||||
* [Talos Linux](https://github.com/siderolabs/talos)
|
||||
and [KubeSpan](https://www.talos.dev/v1.10/talos-guides/network/kubespan/) — for the machine API design using
|
||||
[grpc-proxy](https://github.com/siderolabs/grpc-proxy) and for its elegant approach to secure WireGuard-based overlay
|
||||
networking with zero configuration.
|
||||
and [KubeSpan](https://www.talos.dev/v1.10/talos-guides/network/kubespan/) — for inspiring Uncloud's API routing
|
||||
design with [grpc-proxy](https://github.com/siderolabs/grpc-proxy) and for their elegant approach to secure
|
||||
WireGuard-based overlay networking with zero configuration.
|
||||
* [Docker Swarm Classic](https://github.com/docker-archive/classicswarm) and
|
||||
[Rancher 1.x](http://rancher-com-website-main-elb-elb-1798790864.us-west-2.elb.amazonaws.com/docs/rancher/v1.6/en/)
|
||||
— for showing the power of simplicity and pragmatism in container orchestration and that not every problem needs the
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.34.2
|
||||
// protoc v5.27.3
|
||||
// source: api/pb/caddy.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type GetCaddyConfigResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The generated Caddyfile content.
|
||||
Caddyfile string `protobuf:"bytes,1,opt,name=caddyfile,proto3" json:"caddyfile,omitempty"`
|
||||
// Timestamp when the config was last modified.
|
||||
ModifiedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=modified_at,json=modifiedAt,proto3" json:"modified_at,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) Reset() {
|
||||
*x = GetCaddyConfigResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetCaddyConfigResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetCaddyConfigResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetCaddyConfigResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetCaddyConfigResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) GetCaddyfile() string {
|
||||
if x != nil {
|
||||
return x.Caddyfile
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) GetModifiedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.ModifiedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_api_pb_caddy_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_api_pb_caddy_proto_rawDesc = []byte{
|
||||
0x0a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x73, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x61,
|
||||
0x64, 0x64, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x61, 0x64, 0x64, 0x79, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x64, 0x64, 0x79, 0x66, 0x69, 0x6c, 0x65, 0x12,
|
||||
0x3b, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x32, 0x49, 0x0a, 0x05,
|
||||
0x43, 0x61, 0x64, 0x64, 0x79, 0x12, 0x40, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69,
|
||||
0x2e, 0x47, 0x65, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69,
|
||||
0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_api_pb_caddy_proto_rawDescOnce sync.Once
|
||||
file_api_pb_caddy_proto_rawDescData = file_api_pb_caddy_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_api_pb_caddy_proto_rawDescGZIP() []byte {
|
||||
file_api_pb_caddy_proto_rawDescOnce.Do(func() {
|
||||
file_api_pb_caddy_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_pb_caddy_proto_rawDescData)
|
||||
})
|
||||
return file_api_pb_caddy_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_api_pb_caddy_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_api_pb_caddy_proto_goTypes = []any{
|
||||
(*GetCaddyConfigResponse)(nil), // 0: api.GetCaddyConfigResponse
|
||||
(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
|
||||
(*emptypb.Empty)(nil), // 2: google.protobuf.Empty
|
||||
}
|
||||
var file_api_pb_caddy_proto_depIdxs = []int32{
|
||||
1, // 0: api.GetCaddyConfigResponse.modified_at:type_name -> google.protobuf.Timestamp
|
||||
2, // 1: api.Caddy.GetConfig:input_type -> google.protobuf.Empty
|
||||
0, // 2: api.Caddy.GetConfig:output_type -> api.GetCaddyConfigResponse
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_api_pb_caddy_proto_init() }
|
||||
func file_api_pb_caddy_proto_init() {
|
||||
if File_api_pb_caddy_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_api_pb_caddy_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*GetCaddyConfigResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_api_pb_caddy_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_api_pb_caddy_proto_goTypes,
|
||||
DependencyIndexes: file_api_pb_caddy_proto_depIdxs,
|
||||
MessageInfos: file_api_pb_caddy_proto_msgTypes,
|
||||
}.Build()
|
||||
File_api_pb_caddy_proto = out.File
|
||||
file_api_pb_caddy_proto_rawDesc = nil
|
||||
file_api_pb_caddy_proto_goTypes = nil
|
||||
file_api_pb_caddy_proto_depIdxs = nil
|
||||
}
|
||||
@@ -2,7 +2,7 @@ syntax = "proto3";
|
||||
|
||||
package api;
|
||||
|
||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||
option go_package = "github.com/psviderski/uncloud/api/pb";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
@@ -17,4 +17,4 @@ message GetCaddyConfigResponse {
|
||||
string caddyfile = 1;
|
||||
// Timestamp when the config was last modified.
|
||||
google.protobuf.Timestamp modified_at = 2;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.27.3
|
||||
// source: internal/machine/api/pb/caddy.proto
|
||||
// source: api/pb/caddy.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -120,5 +120,5 @@ var Caddy_ServiceDesc = grpc.ServiceDesc{
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal/machine/api/pb/caddy.proto",
|
||||
Metadata: "api/pb/caddy.proto",
|
||||
}
|
||||
@@ -0,0 +1,689 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.34.2
|
||||
// protoc v5.27.3
|
||||
// source: api/pb/caddy_storage.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type StoreCaddyStorageRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *StoreCaddyStorageRequest) Reset() {
|
||||
*x = StoreCaddyStorageRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *StoreCaddyStorageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StoreCaddyStorageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *StoreCaddyStorageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StoreCaddyStorageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*StoreCaddyStorageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *StoreCaddyStorageRequest) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *StoreCaddyStorageRequest) GetValue() []byte {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LoadCaddyStorageRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageRequest) Reset() {
|
||||
*x = LoadCaddyStorageRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LoadCaddyStorageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *LoadCaddyStorageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LoadCaddyStorageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*LoadCaddyStorageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageRequest) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type LoadCaddyStorageResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageResponse) Reset() {
|
||||
*x = LoadCaddyStorageResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LoadCaddyStorageResponse) ProtoMessage() {}
|
||||
|
||||
func (x *LoadCaddyStorageResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LoadCaddyStorageResponse.ProtoReflect.Descriptor instead.
|
||||
func (*LoadCaddyStorageResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageResponse) GetValue() []byte {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *LoadCaddyStorageResponse) GetUpdatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeleteCaddyStorageRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DeleteCaddyStorageRequest) Reset() {
|
||||
*x = DeleteCaddyStorageRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DeleteCaddyStorageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DeleteCaddyStorageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *DeleteCaddyStorageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DeleteCaddyStorageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*DeleteCaddyStorageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *DeleteCaddyStorageRequest) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ListCaddyStorageRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageRequest) Reset() {
|
||||
*x = ListCaddyStorageRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListCaddyStorageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListCaddyStorageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListCaddyStorageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListCaddyStorageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageRequest) GetPrefix() string {
|
||||
if x != nil {
|
||||
return x.Prefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageRequest) GetRecursive() bool {
|
||||
if x != nil {
|
||||
return x.Recursive
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ListCaddyStorageResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageResponse) Reset() {
|
||||
*x = ListCaddyStorageResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListCaddyStorageResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListCaddyStorageResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListCaddyStorageResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListCaddyStorageResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *ListCaddyStorageResponse) GetKeys() []string {
|
||||
if x != nil {
|
||||
return x.Keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type StatCaddyStorageRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageRequest) Reset() {
|
||||
*x = StatCaddyStorageRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StatCaddyStorageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *StatCaddyStorageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StatCaddyStorageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*StatCaddyStorageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageRequest) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StatCaddyStorageResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
||||
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
|
||||
IsTerminal bool `protobuf:"varint,4,opt,name=is_terminal,json=isTerminal,proto3" json:"is_terminal,omitempty"`
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageResponse) Reset() {
|
||||
*x = StatCaddyStorageResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StatCaddyStorageResponse) ProtoMessage() {}
|
||||
|
||||
func (x *StatCaddyStorageResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_pb_caddy_storage_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StatCaddyStorageResponse.ProtoReflect.Descriptor instead.
|
||||
func (*StatCaddyStorageResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_pb_caddy_storage_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageResponse) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageResponse) GetUpdatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageResponse) GetSize() int64 {
|
||||
if x != nil {
|
||||
return x.Size
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *StatCaddyStorageResponse) GetIsTerminal() bool {
|
||||
if x != nil {
|
||||
return x.IsTerminal
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_api_pb_caddy_storage_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_api_pb_caddy_storage_proto_rawDesc = []byte{
|
||||
0x0a, 0x1a, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x61, 0x64, 0x64, 0x79, 0x5f, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70,
|
||||
0x69, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
|
||||
0x42, 0x0a, 0x18, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x22, 0x2b, 0x0a, 0x17, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x64, 0x64, 0x79,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10,
|
||||
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||
0x22, 0x6b, 0x0a, 0x18, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x2d, 0x0a,
|
||||
0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4f, 0x0a, 0x17,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
|
||||
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x22, 0x2e, 0x0a,
|
||||
0x18, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79,
|
||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x2b, 0x0a,
|
||||
0x17, 0x53, 0x74, 0x61, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x9c, 0x01, 0x0a, 0x18, 0x53,
|
||||
0x74, 0x61, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x74,
|
||||
0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69,
|
||||
0x73, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x32, 0xdf, 0x02, 0x0a, 0x0c, 0x43, 0x61,
|
||||
0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x65, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43,
|
||||
0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x04, 0x4c, 0x6f,
|
||||
0x61, 0x64, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x64,
|
||||
0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x64, 0x64, 0x79,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x40, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
||||
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
|
||||
0x79, 0x12, 0x43, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x74, 0x12, 0x1c,
|
||||
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61,
|
||||
0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65,
|
||||
0x72, 0x73, 0x6b, 0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x61, 0x70, 0x69,
|
||||
0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_api_pb_caddy_storage_proto_rawDescOnce sync.Once
|
||||
file_api_pb_caddy_storage_proto_rawDescData = file_api_pb_caddy_storage_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_api_pb_caddy_storage_proto_rawDescGZIP() []byte {
|
||||
file_api_pb_caddy_storage_proto_rawDescOnce.Do(func() {
|
||||
file_api_pb_caddy_storage_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_pb_caddy_storage_proto_rawDescData)
|
||||
})
|
||||
return file_api_pb_caddy_storage_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_api_pb_caddy_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_api_pb_caddy_storage_proto_goTypes = []any{
|
||||
(*StoreCaddyStorageRequest)(nil), // 0: api.StoreCaddyStorageRequest
|
||||
(*LoadCaddyStorageRequest)(nil), // 1: api.LoadCaddyStorageRequest
|
||||
(*LoadCaddyStorageResponse)(nil), // 2: api.LoadCaddyStorageResponse
|
||||
(*DeleteCaddyStorageRequest)(nil), // 3: api.DeleteCaddyStorageRequest
|
||||
(*ListCaddyStorageRequest)(nil), // 4: api.ListCaddyStorageRequest
|
||||
(*ListCaddyStorageResponse)(nil), // 5: api.ListCaddyStorageResponse
|
||||
(*StatCaddyStorageRequest)(nil), // 6: api.StatCaddyStorageRequest
|
||||
(*StatCaddyStorageResponse)(nil), // 7: api.StatCaddyStorageResponse
|
||||
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
|
||||
(*emptypb.Empty)(nil), // 9: google.protobuf.Empty
|
||||
}
|
||||
var file_api_pb_caddy_storage_proto_depIdxs = []int32{
|
||||
8, // 0: api.LoadCaddyStorageResponse.updated_at:type_name -> google.protobuf.Timestamp
|
||||
8, // 1: api.StatCaddyStorageResponse.updated_at:type_name -> google.protobuf.Timestamp
|
||||
0, // 2: api.CaddyStorage.Store:input_type -> api.StoreCaddyStorageRequest
|
||||
1, // 3: api.CaddyStorage.Load:input_type -> api.LoadCaddyStorageRequest
|
||||
3, // 4: api.CaddyStorage.Delete:input_type -> api.DeleteCaddyStorageRequest
|
||||
4, // 5: api.CaddyStorage.List:input_type -> api.ListCaddyStorageRequest
|
||||
6, // 6: api.CaddyStorage.Stat:input_type -> api.StatCaddyStorageRequest
|
||||
9, // 7: api.CaddyStorage.Store:output_type -> google.protobuf.Empty
|
||||
2, // 8: api.CaddyStorage.Load:output_type -> api.LoadCaddyStorageResponse
|
||||
9, // 9: api.CaddyStorage.Delete:output_type -> google.protobuf.Empty
|
||||
5, // 10: api.CaddyStorage.List:output_type -> api.ListCaddyStorageResponse
|
||||
7, // 11: api.CaddyStorage.Stat:output_type -> api.StatCaddyStorageResponse
|
||||
7, // [7:12] is the sub-list for method output_type
|
||||
2, // [2:7] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_api_pb_caddy_storage_proto_init() }
|
||||
func file_api_pb_caddy_storage_proto_init() {
|
||||
if File_api_pb_caddy_storage_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_api_pb_caddy_storage_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*StoreCaddyStorageRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*LoadCaddyStorageRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*LoadCaddyStorageResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*DeleteCaddyStorageRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*ListCaddyStorageRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*ListCaddyStorageResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*StatCaddyStorageRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_api_pb_caddy_storage_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*StatCaddyStorageResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_api_pb_caddy_storage_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_api_pb_caddy_storage_proto_goTypes,
|
||||
DependencyIndexes: file_api_pb_caddy_storage_proto_depIdxs,
|
||||
MessageInfos: file_api_pb_caddy_storage_proto_msgTypes,
|
||||
}.Build()
|
||||
File_api_pb_caddy_storage_proto = out.File
|
||||
file_api_pb_caddy_storage_proto_rawDesc = nil
|
||||
file_api_pb_caddy_storage_proto_goTypes = nil
|
||||
file_api_pb_caddy_storage_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package api;
|
||||
|
||||
option go_package = "github.com/psviderski/uncloud/api/pb";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// CaddyStorage exposes the CertMagic storage operations backed by the distributed cluster store.
|
||||
// Each request operates on the receiving machine's store replica. Callers that need replication to catch up before
|
||||
// reading should capture store versions with Machine.InspectMachine and call Machine.WaitForStoreVersion on the
|
||||
// receiving machine, subject to that RPC's data-availability limitations (see WaitForStoreVersion docs).
|
||||
// See Storage interface in https://github.com/caddyserver/certmagic/blob/master/storage.go.
|
||||
service CaddyStorage {
|
||||
rpc Store(StoreCaddyStorageRequest) returns (google.protobuf.Empty);
|
||||
rpc Load(LoadCaddyStorageRequest) returns (LoadCaddyStorageResponse);
|
||||
rpc Delete(DeleteCaddyStorageRequest) returns (google.protobuf.Empty);
|
||||
rpc List(ListCaddyStorageRequest) returns (ListCaddyStorageResponse);
|
||||
rpc Stat(StatCaddyStorageRequest) returns (StatCaddyStorageResponse);
|
||||
}
|
||||
|
||||
message StoreCaddyStorageRequest {
|
||||
string key = 1;
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
message LoadCaddyStorageRequest {
|
||||
string key = 1;
|
||||
}
|
||||
|
||||
message LoadCaddyStorageResponse {
|
||||
bytes value = 1;
|
||||
google.protobuf.Timestamp updated_at = 2;
|
||||
}
|
||||
|
||||
message DeleteCaddyStorageRequest {
|
||||
string key = 1;
|
||||
}
|
||||
|
||||
message ListCaddyStorageRequest {
|
||||
string prefix = 1;
|
||||
bool recursive = 2;
|
||||
}
|
||||
|
||||
message ListCaddyStorageResponse {
|
||||
repeated string keys = 1;
|
||||
}
|
||||
|
||||
message StatCaddyStorageRequest {
|
||||
string key = 1;
|
||||
}
|
||||
|
||||
message StatCaddyStorageResponse {
|
||||
string key = 1;
|
||||
google.protobuf.Timestamp updated_at = 2;
|
||||
int64 size = 3;
|
||||
bool is_terminal = 4;
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.27.3
|
||||
// source: api/pb/caddy_storage.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
CaddyStorage_Store_FullMethodName = "/api.CaddyStorage/Store"
|
||||
CaddyStorage_Load_FullMethodName = "/api.CaddyStorage/Load"
|
||||
CaddyStorage_Delete_FullMethodName = "/api.CaddyStorage/Delete"
|
||||
CaddyStorage_List_FullMethodName = "/api.CaddyStorage/List"
|
||||
CaddyStorage_Stat_FullMethodName = "/api.CaddyStorage/Stat"
|
||||
)
|
||||
|
||||
// CaddyStorageClient is the client API for CaddyStorage service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// CaddyStorage exposes the CertMagic storage operations backed by the distributed cluster store.
|
||||
// Each request operates on the receiving machine's store replica. Callers that need replication to catch up before
|
||||
// reading should capture store versions with Machine.InspectMachine and call Machine.WaitForStoreVersion on the
|
||||
// receiving machine, subject to that RPC's data-availability limitations (see WaitForStoreVersion docs).
|
||||
// See Storage interface in https://github.com/caddyserver/certmagic/blob/master/storage.go.
|
||||
type CaddyStorageClient interface {
|
||||
Store(ctx context.Context, in *StoreCaddyStorageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
Load(ctx context.Context, in *LoadCaddyStorageRequest, opts ...grpc.CallOption) (*LoadCaddyStorageResponse, error)
|
||||
Delete(ctx context.Context, in *DeleteCaddyStorageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
List(ctx context.Context, in *ListCaddyStorageRequest, opts ...grpc.CallOption) (*ListCaddyStorageResponse, error)
|
||||
Stat(ctx context.Context, in *StatCaddyStorageRequest, opts ...grpc.CallOption) (*StatCaddyStorageResponse, error)
|
||||
}
|
||||
|
||||
type caddyStorageClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCaddyStorageClient(cc grpc.ClientConnInterface) CaddyStorageClient {
|
||||
return &caddyStorageClient{cc}
|
||||
}
|
||||
|
||||
func (c *caddyStorageClient) Store(ctx context.Context, in *StoreCaddyStorageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, CaddyStorage_Store_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *caddyStorageClient) Load(ctx context.Context, in *LoadCaddyStorageRequest, opts ...grpc.CallOption) (*LoadCaddyStorageResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LoadCaddyStorageResponse)
|
||||
err := c.cc.Invoke(ctx, CaddyStorage_Load_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *caddyStorageClient) Delete(ctx context.Context, in *DeleteCaddyStorageRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, CaddyStorage_Delete_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *caddyStorageClient) List(ctx context.Context, in *ListCaddyStorageRequest, opts ...grpc.CallOption) (*ListCaddyStorageResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListCaddyStorageResponse)
|
||||
err := c.cc.Invoke(ctx, CaddyStorage_List_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *caddyStorageClient) Stat(ctx context.Context, in *StatCaddyStorageRequest, opts ...grpc.CallOption) (*StatCaddyStorageResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StatCaddyStorageResponse)
|
||||
err := c.cc.Invoke(ctx, CaddyStorage_Stat_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CaddyStorageServer is the server API for CaddyStorage service.
|
||||
// All implementations must embed UnimplementedCaddyStorageServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// CaddyStorage exposes the CertMagic storage operations backed by the distributed cluster store.
|
||||
// Each request operates on the receiving machine's store replica. Callers that need replication to catch up before
|
||||
// reading should capture store versions with Machine.InspectMachine and call Machine.WaitForStoreVersion on the
|
||||
// receiving machine, subject to that RPC's data-availability limitations (see WaitForStoreVersion docs).
|
||||
// See Storage interface in https://github.com/caddyserver/certmagic/blob/master/storage.go.
|
||||
type CaddyStorageServer interface {
|
||||
Store(context.Context, *StoreCaddyStorageRequest) (*emptypb.Empty, error)
|
||||
Load(context.Context, *LoadCaddyStorageRequest) (*LoadCaddyStorageResponse, error)
|
||||
Delete(context.Context, *DeleteCaddyStorageRequest) (*emptypb.Empty, error)
|
||||
List(context.Context, *ListCaddyStorageRequest) (*ListCaddyStorageResponse, error)
|
||||
Stat(context.Context, *StatCaddyStorageRequest) (*StatCaddyStorageResponse, error)
|
||||
mustEmbedUnimplementedCaddyStorageServer()
|
||||
}
|
||||
|
||||
// UnimplementedCaddyStorageServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedCaddyStorageServer struct{}
|
||||
|
||||
func (UnimplementedCaddyStorageServer) Store(context.Context, *StoreCaddyStorageRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Store not implemented")
|
||||
}
|
||||
func (UnimplementedCaddyStorageServer) Load(context.Context, *LoadCaddyStorageRequest) (*LoadCaddyStorageResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Load not implemented")
|
||||
}
|
||||
func (UnimplementedCaddyStorageServer) Delete(context.Context, *DeleteCaddyStorageRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
|
||||
}
|
||||
func (UnimplementedCaddyStorageServer) List(context.Context, *ListCaddyStorageRequest) (*ListCaddyStorageResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
|
||||
}
|
||||
func (UnimplementedCaddyStorageServer) Stat(context.Context, *StatCaddyStorageRequest) (*StatCaddyStorageResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Stat not implemented")
|
||||
}
|
||||
func (UnimplementedCaddyStorageServer) mustEmbedUnimplementedCaddyStorageServer() {}
|
||||
func (UnimplementedCaddyStorageServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeCaddyStorageServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to CaddyStorageServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeCaddyStorageServer interface {
|
||||
mustEmbedUnimplementedCaddyStorageServer()
|
||||
}
|
||||
|
||||
func RegisterCaddyStorageServer(s grpc.ServiceRegistrar, srv CaddyStorageServer) {
|
||||
// If the following call pancis, it indicates UnimplementedCaddyStorageServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&CaddyStorage_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _CaddyStorage_Store_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StoreCaddyStorageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CaddyStorageServer).Store(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CaddyStorage_Store_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CaddyStorageServer).Store(ctx, req.(*StoreCaddyStorageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CaddyStorage_Load_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LoadCaddyStorageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CaddyStorageServer).Load(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CaddyStorage_Load_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CaddyStorageServer).Load(ctx, req.(*LoadCaddyStorageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CaddyStorage_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteCaddyStorageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CaddyStorageServer).Delete(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CaddyStorage_Delete_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CaddyStorageServer).Delete(ctx, req.(*DeleteCaddyStorageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CaddyStorage_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListCaddyStorageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CaddyStorageServer).List(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CaddyStorage_List_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CaddyStorageServer).List(ctx, req.(*ListCaddyStorageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CaddyStorage_Stat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StatCaddyStorageRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CaddyStorageServer).Stat(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CaddyStorage_Stat_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CaddyStorageServer).Stat(ctx, req.(*StatCaddyStorageRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// CaddyStorage_ServiceDesc is the grpc.ServiceDesc for CaddyStorage service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var CaddyStorage_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "api.CaddyStorage",
|
||||
HandlerType: (*CaddyStorageServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Store",
|
||||
Handler: _CaddyStorage_Store_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Load",
|
||||
Handler: _CaddyStorage_Load_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _CaddyStorage_Delete_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "List",
|
||||
Handler: _CaddyStorage_List_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Stat",
|
||||
Handler: _CaddyStorage_Stat_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "api/pb/caddy_storage.proto",
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// versions:
|
||||
// protoc-gen-go v1.34.2
|
||||
// protoc v5.27.3
|
||||
// source: internal/machine/api/pb/cluster.proto
|
||||
// source: api/pb/cluster.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -62,11 +62,11 @@ func (x MachineMember_MembershipState) String() string {
|
||||
}
|
||||
|
||||
func (MachineMember_MembershipState) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_internal_machine_api_pb_cluster_proto_enumTypes[0].Descriptor()
|
||||
return file_api_pb_cluster_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (MachineMember_MembershipState) Type() protoreflect.EnumType {
|
||||
return &file_internal_machine_api_pb_cluster_proto_enumTypes[0]
|
||||
return &file_api_pb_cluster_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x MachineMember_MembershipState) Number() protoreflect.EnumNumber {
|
||||
@@ -75,7 +75,7 @@ func (x MachineMember_MembershipState) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use MachineMember_MembershipState.Descriptor instead.
|
||||
func (MachineMember_MembershipState) EnumDescriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{2, 0}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{2, 0}
|
||||
}
|
||||
|
||||
type DNSRecord_RecordType int32
|
||||
@@ -111,11 +111,11 @@ func (x DNSRecord_RecordType) String() string {
|
||||
}
|
||||
|
||||
func (DNSRecord_RecordType) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_internal_machine_api_pb_cluster_proto_enumTypes[1].Descriptor()
|
||||
return file_api_pb_cluster_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (DNSRecord_RecordType) Type() protoreflect.EnumType {
|
||||
return &file_internal_machine_api_pb_cluster_proto_enumTypes[1]
|
||||
return &file_api_pb_cluster_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x DNSRecord_RecordType) Number() protoreflect.EnumNumber {
|
||||
@@ -124,7 +124,7 @@ func (x DNSRecord_RecordType) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use DNSRecord_RecordType.Descriptor instead.
|
||||
func (DNSRecord_RecordType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{9, 0}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{9, 0}
|
||||
}
|
||||
|
||||
type AddMachineRequest struct {
|
||||
@@ -140,7 +140,7 @@ type AddMachineRequest struct {
|
||||
func (x *AddMachineRequest) Reset() {
|
||||
*x = AddMachineRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[0]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func (x *AddMachineRequest) String() string {
|
||||
func (*AddMachineRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AddMachineRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[0]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -166,7 +166,7 @@ func (x *AddMachineRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AddMachineRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AddMachineRequest) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{0}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *AddMachineRequest) GetName() string {
|
||||
@@ -201,7 +201,7 @@ type AddMachineResponse struct {
|
||||
func (x *AddMachineResponse) Reset() {
|
||||
*x = AddMachineResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[1]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func (x *AddMachineResponse) String() string {
|
||||
func (*AddMachineResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AddMachineResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[1]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -227,7 +227,7 @@ func (x *AddMachineResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AddMachineResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AddMachineResponse) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{1}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *AddMachineResponse) GetMachine() *MachineInfo {
|
||||
@@ -249,7 +249,7 @@ type MachineMember struct {
|
||||
func (x *MachineMember) Reset() {
|
||||
*x = MachineMember{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[2]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -262,7 +262,7 @@ func (x *MachineMember) String() string {
|
||||
func (*MachineMember) ProtoMessage() {}
|
||||
|
||||
func (x *MachineMember) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[2]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -275,7 +275,7 @@ func (x *MachineMember) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MachineMember.ProtoReflect.Descriptor instead.
|
||||
func (*MachineMember) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{2}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *MachineMember) GetMachine() *MachineInfo {
|
||||
@@ -303,7 +303,7 @@ type ListMachinesResponse struct {
|
||||
func (x *ListMachinesResponse) Reset() {
|
||||
*x = ListMachinesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[3]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -316,7 +316,7 @@ func (x *ListMachinesResponse) String() string {
|
||||
func (*ListMachinesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[3]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -329,7 +329,7 @@ func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListMachinesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListMachinesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{3}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ListMachinesResponse) GetMachines() []*MachineMember {
|
||||
@@ -350,7 +350,7 @@ type RemoveMachineRequest struct {
|
||||
func (x *RemoveMachineRequest) Reset() {
|
||||
*x = RemoveMachineRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[4]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func (x *RemoveMachineRequest) String() string {
|
||||
func (*RemoveMachineRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RemoveMachineRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[4]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -376,7 +376,7 @@ func (x *RemoveMachineRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RemoveMachineRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RemoveMachineRequest) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{4}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *RemoveMachineRequest) GetId() string {
|
||||
@@ -397,7 +397,7 @@ type Domain struct {
|
||||
func (x *Domain) Reset() {
|
||||
*x = Domain{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[5]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -410,7 +410,7 @@ func (x *Domain) String() string {
|
||||
func (*Domain) ProtoMessage() {}
|
||||
|
||||
func (x *Domain) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[5]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -423,7 +423,7 @@ func (x *Domain) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Domain.ProtoReflect.Descriptor instead.
|
||||
func (*Domain) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{5}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *Domain) GetName() string {
|
||||
@@ -444,7 +444,7 @@ type ReserveDomainRequest struct {
|
||||
func (x *ReserveDomainRequest) Reset() {
|
||||
*x = ReserveDomainRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[6]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -457,7 +457,7 @@ func (x *ReserveDomainRequest) String() string {
|
||||
func (*ReserveDomainRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ReserveDomainRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[6]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -470,7 +470,7 @@ func (x *ReserveDomainRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ReserveDomainRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ReserveDomainRequest) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{6}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *ReserveDomainRequest) GetEndpoint() string {
|
||||
@@ -491,7 +491,7 @@ type CreateDomainRecordsRequest struct {
|
||||
func (x *CreateDomainRecordsRequest) Reset() {
|
||||
*x = CreateDomainRecordsRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[7]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -504,7 +504,7 @@ func (x *CreateDomainRecordsRequest) String() string {
|
||||
func (*CreateDomainRecordsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CreateDomainRecordsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[7]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -517,7 +517,7 @@ func (x *CreateDomainRecordsRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CreateDomainRecordsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CreateDomainRecordsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{7}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *CreateDomainRecordsRequest) GetRecords() []*DNSRecord {
|
||||
@@ -538,7 +538,7 @@ type CreateDomainRecordsResponse struct {
|
||||
func (x *CreateDomainRecordsResponse) Reset() {
|
||||
*x = CreateDomainRecordsResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[8]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -551,7 +551,7 @@ func (x *CreateDomainRecordsResponse) String() string {
|
||||
func (*CreateDomainRecordsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CreateDomainRecordsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[8]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -564,7 +564,7 @@ func (x *CreateDomainRecordsResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CreateDomainRecordsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CreateDomainRecordsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{8}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *CreateDomainRecordsResponse) GetRecords() []*DNSRecord {
|
||||
@@ -587,7 +587,7 @@ type DNSRecord struct {
|
||||
func (x *DNSRecord) Reset() {
|
||||
*x = DNSRecord{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[9]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -600,7 +600,7 @@ func (x *DNSRecord) String() string {
|
||||
func (*DNSRecord) ProtoMessage() {}
|
||||
|
||||
func (x *DNSRecord) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[9]
|
||||
mi := &file_api_pb_cluster_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -613,7 +613,7 @@ func (x *DNSRecord) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DNSRecord.ProtoReflect.Descriptor instead.
|
||||
func (*DNSRecord) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{9}
|
||||
return file_api_pb_cluster_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *DNSRecord) GetName() string {
|
||||
@@ -637,123 +637,119 @@ func (x *DNSRecord) GetValues() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_internal_machine_api_pb_cluster_proto protoreflect.FileDescriptor
|
||||
var File_api_pb_cluster_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_internal_machine_api_pb_cluster_proto_rawDesc = []byte{
|
||||
0x0a, 0x25, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
|
||||
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1b, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x24, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
||||
0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
|
||||
0x25, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7b, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63,
|
||||
0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x2c, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x24, 0x0a,
|
||||
0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x07, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69,
|
||||
0x63, 0x49, 0x70, 0x22, 0x40, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x6d, 0x61, 0x63,
|
||||
0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69,
|
||||
0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x61,
|
||||
0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x0d, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d,
|
||||
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68,
|
||||
0x69, 0x6e, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||
0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69,
|
||||
0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3d, 0x0a,
|
||||
0x0f, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, 0x0a,
|
||||
0x02, 0x55, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x53, 0x50, 0x45, 0x43, 0x54,
|
||||
0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x22, 0x46, 0x0a, 0x14,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63,
|
||||
0x68, 0x69, 0x6e, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68,
|
||||
0x69, 0x6e, 0x65, 0x73, 0x22, 0x26, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x61,
|
||||
0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x06,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x32, 0x0a, 0x14, 0x52, 0x65,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x46,
|
||||
0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65,
|
||||
0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x07,
|
||||
0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
|
||||
0x61, 0x70, 0x69, 0x2e, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72,
|
||||
0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x47, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x4e, 0x53,
|
||||
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22,
|
||||
0x96, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e,
|
||||
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x2e, 0x0a, 0x0a, 0x52, 0x65, 0x63, 0x6f,
|
||||
0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
|
||||
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01, 0x41, 0x10, 0x01, 0x12, 0x08,
|
||||
0x0a, 0x04, 0x41, 0x41, 0x41, 0x41, 0x10, 0x02, 0x32, 0xca, 0x03, 0x0a, 0x07, 0x43, 0x6c, 0x75,
|
||||
0x73, 0x74, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68,
|
||||
0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69,
|
||||
0x2e, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
|
||||
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65,
|
||||
0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0d, 0x52, 0x65,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0b,
|
||||
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x58, 0x0a, 0x13, 0x43,
|
||||
var file_api_pb_cluster_proto_rawDesc = []byte{
|
||||
0x0a, 0x14, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1b, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62,
|
||||
0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x61,
|
||||
0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0x7b, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x07,
|
||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e,
|
||||
0x61, 0x70, 0x69, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x70, 0x75,
|
||||
0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e,
|
||||
0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70,
|
||||
0x22, 0x40, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61,
|
||||
0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x0d, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4d, 0x65,
|
||||
0x6d, 0x62, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68,
|
||||
0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||
0x12, 0x38, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4d, 0x65, 0x6d,
|
||||
0x62, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x53, 0x74,
|
||||
0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3d, 0x0a, 0x0f, 0x4d, 0x65,
|
||||
0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a,
|
||||
0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x55, 0x50,
|
||||
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x53, 0x50, 0x45, 0x43, 0x54, 0x10, 0x02, 0x12,
|
||||
0x08, 0x0a, 0x04, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x22, 0x46, 0x0a, 0x14, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||
0x73, 0x22, 0x26, 0x0a, 0x14, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x06, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x32, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x46, 0x0a, 0x1a, 0x43,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72,
|
||||
0x64, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69, 0x2f, 0x75,
|
||||
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
|
||||
0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x07, 0x72, 0x65, 0x63,
|
||||
0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69,
|
||||
0x2e, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f,
|
||||
0x72, 0x64, 0x73, 0x22, 0x47, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63,
|
||||
0x6f, 0x72, 0x64, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x96, 0x01, 0x0a,
|
||||
0x09, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d,
|
||||
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x61,
|
||||
0x70, 0x69, 0x2e, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x52, 0x65, 0x63,
|
||||
0x6f, 0x72, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x2e, 0x0a, 0x0a, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
|
||||
0x45, 0x44, 0x10, 0x00, 0x12, 0x05, 0x0a, 0x01, 0x41, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41,
|
||||
0x41, 0x41, 0x41, 0x10, 0x02, 0x32, 0xca, 0x03, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65,
|
||||
0x72, 0x12, 0x3d, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12,
|
||||
0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64, 0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64,
|
||||
0x64, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x41, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73,
|
||||
0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x61, 0x63,
|
||||
0x68, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
||||
0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x12, 0x30, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x16, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x12, 0x34, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0b, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x58, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12,
|
||||
0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d, 0x61,
|
||||
0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c,
|
||||
0x6f, 0x75, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_internal_machine_api_pb_cluster_proto_rawDescOnce sync.Once
|
||||
file_internal_machine_api_pb_cluster_proto_rawDescData = file_internal_machine_api_pb_cluster_proto_rawDesc
|
||||
file_api_pb_cluster_proto_rawDescOnce sync.Once
|
||||
file_api_pb_cluster_proto_rawDescData = file_api_pb_cluster_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_internal_machine_api_pb_cluster_proto_rawDescGZIP() []byte {
|
||||
file_internal_machine_api_pb_cluster_proto_rawDescOnce.Do(func() {
|
||||
file_internal_machine_api_pb_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_machine_api_pb_cluster_proto_rawDescData)
|
||||
func file_api_pb_cluster_proto_rawDescGZIP() []byte {
|
||||
file_api_pb_cluster_proto_rawDescOnce.Do(func() {
|
||||
file_api_pb_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_pb_cluster_proto_rawDescData)
|
||||
})
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescData
|
||||
return file_api_pb_cluster_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_internal_machine_api_pb_cluster_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_internal_machine_api_pb_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_internal_machine_api_pb_cluster_proto_goTypes = []any{
|
||||
var file_api_pb_cluster_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_api_pb_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_api_pb_cluster_proto_goTypes = []any{
|
||||
(MachineMember_MembershipState)(0), // 0: api.MachineMember.MembershipState
|
||||
(DNSRecord_RecordType)(0), // 1: api.DNSRecord.RecordType
|
||||
(*AddMachineRequest)(nil), // 2: api.AddMachineRequest
|
||||
@@ -771,7 +767,7 @@ var file_internal_machine_api_pb_cluster_proto_goTypes = []any{
|
||||
(*MachineInfo)(nil), // 14: api.MachineInfo
|
||||
(*emptypb.Empty)(nil), // 15: google.protobuf.Empty
|
||||
}
|
||||
var file_internal_machine_api_pb_cluster_proto_depIdxs = []int32{
|
||||
var file_api_pb_cluster_proto_depIdxs = []int32{
|
||||
12, // 0: api.AddMachineRequest.network:type_name -> api.NetworkConfig
|
||||
13, // 1: api.AddMachineRequest.public_ip:type_name -> api.IP
|
||||
14, // 2: api.AddMachineResponse.machine:type_name -> api.MachineInfo
|
||||
@@ -802,15 +798,15 @@ var file_internal_machine_api_pb_cluster_proto_depIdxs = []int32{
|
||||
0, // [0:9] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_internal_machine_api_pb_cluster_proto_init() }
|
||||
func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
if File_internal_machine_api_pb_cluster_proto != nil {
|
||||
func init() { file_api_pb_cluster_proto_init() }
|
||||
func file_api_pb_cluster_proto_init() {
|
||||
if File_api_pb_cluster_proto != nil {
|
||||
return
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_init()
|
||||
file_internal_machine_api_pb_machine_proto_init()
|
||||
file_api_pb_common_proto_init()
|
||||
file_api_pb_machine_proto_init()
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*AddMachineRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -822,7 +818,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*AddMachineResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -834,7 +830,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*MachineMember); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -846,7 +842,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*ListMachinesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -858,7 +854,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*RemoveMachineRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -870,7 +866,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*Domain); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -882,7 +878,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*ReserveDomainRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -894,7 +890,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*CreateDomainRecordsRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -906,7 +902,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[8].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[8].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*CreateDomainRecordsResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -918,7 +914,7 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[9].Exporter = func(v any, i int) any {
|
||||
file_api_pb_cluster_proto_msgTypes[9].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*DNSRecord); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -935,19 +931,19 @@ func file_internal_machine_api_pb_cluster_proto_init() {
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_machine_api_pb_cluster_proto_rawDesc,
|
||||
RawDescriptor: file_api_pb_cluster_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_internal_machine_api_pb_cluster_proto_goTypes,
|
||||
DependencyIndexes: file_internal_machine_api_pb_cluster_proto_depIdxs,
|
||||
EnumInfos: file_internal_machine_api_pb_cluster_proto_enumTypes,
|
||||
MessageInfos: file_internal_machine_api_pb_cluster_proto_msgTypes,
|
||||
GoTypes: file_api_pb_cluster_proto_goTypes,
|
||||
DependencyIndexes: file_api_pb_cluster_proto_depIdxs,
|
||||
EnumInfos: file_api_pb_cluster_proto_enumTypes,
|
||||
MessageInfos: file_api_pb_cluster_proto_msgTypes,
|
||||
}.Build()
|
||||
File_internal_machine_api_pb_cluster_proto = out.File
|
||||
file_internal_machine_api_pb_cluster_proto_rawDesc = nil
|
||||
file_internal_machine_api_pb_cluster_proto_goTypes = nil
|
||||
file_internal_machine_api_pb_cluster_proto_depIdxs = nil
|
||||
File_api_pb_cluster_proto = out.File
|
||||
file_api_pb_cluster_proto_rawDesc = nil
|
||||
file_api_pb_cluster_proto_goTypes = nil
|
||||
file_api_pb_cluster_proto_depIdxs = nil
|
||||
}
|
||||
@@ -2,11 +2,11 @@ syntax = "proto3";
|
||||
|
||||
package api;
|
||||
|
||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||
option go_package = "github.com/psviderski/uncloud/api/pb";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "internal/machine/api/pb/common.proto";
|
||||
import "internal/machine/api/pb/machine.proto";
|
||||
import "api/pb/common.proto";
|
||||
import "api/pb/machine.proto";
|
||||
|
||||
service Cluster {
|
||||
rpc AddMachine(AddMachineRequest) returns (AddMachineResponse);
|
||||
@@ -2,7 +2,7 @@
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.27.3
|
||||
// source: internal/machine/api/pb/cluster.proto
|
||||
// source: api/pb/cluster.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -346,5 +346,5 @@ var Cluster_ServiceDesc = grpc.ServiceDesc{
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal/machine/api/pb/cluster.proto",
|
||||
Metadata: "api/pb/cluster.proto",
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// versions:
|
||||
// protoc-gen-go v1.34.2
|
||||
// protoc v5.27.3
|
||||
// source: internal/machine/api/pb/common.proto
|
||||
// source: api/pb/common.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -58,11 +58,11 @@ func (x LogEntry_StreamType) String() string {
|
||||
}
|
||||
|
||||
func (LogEntry_StreamType) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_internal_machine_api_pb_common_proto_enumTypes[0].Descriptor()
|
||||
return file_api_pb_common_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (LogEntry_StreamType) Type() protoreflect.EnumType {
|
||||
return &file_internal_machine_api_pb_common_proto_enumTypes[0]
|
||||
return &file_api_pb_common_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x LogEntry_StreamType) Number() protoreflect.EnumNumber {
|
||||
@@ -71,7 +71,7 @@ func (x LogEntry_StreamType) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use LogEntry_StreamType.Descriptor instead.
|
||||
func (LogEntry_StreamType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{7, 0}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{7, 0}
|
||||
}
|
||||
|
||||
// Common metadata message nested in all reply message types, injected by the gRPC proxy to provide information
|
||||
@@ -96,7 +96,7 @@ type Metadata struct {
|
||||
func (x *Metadata) Reset() {
|
||||
*x = Metadata{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[0]
|
||||
mi := &file_api_pb_common_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (x *Metadata) String() string {
|
||||
func (*Metadata) ProtoMessage() {}
|
||||
|
||||
func (x *Metadata) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[0]
|
||||
mi := &file_api_pb_common_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -122,7 +122,7 @@ func (x *Metadata) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
|
||||
func (*Metadata) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{0}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Metadata) GetMachineId() string {
|
||||
@@ -172,7 +172,7 @@ type Empty struct {
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[1]
|
||||
mi := &file_api_pb_common_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -185,7 +185,7 @@ func (x *Empty) String() string {
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[1]
|
||||
mi := &file_api_pb_common_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -198,7 +198,7 @@ func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{1}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Empty) GetMetadata() *Metadata {
|
||||
@@ -221,7 +221,7 @@ type EmptyResponse struct {
|
||||
func (x *EmptyResponse) Reset() {
|
||||
*x = EmptyResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[2]
|
||||
mi := &file_api_pb_common_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (x *EmptyResponse) String() string {
|
||||
func (*EmptyResponse) ProtoMessage() {}
|
||||
|
||||
func (x *EmptyResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[2]
|
||||
mi := &file_api_pb_common_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -247,7 +247,7 @@ func (x *EmptyResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead.
|
||||
func (*EmptyResponse) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{2}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *EmptyResponse) GetMessages() []*Empty {
|
||||
@@ -268,7 +268,7 @@ type IP struct {
|
||||
func (x *IP) Reset() {
|
||||
*x = IP{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[3]
|
||||
mi := &file_api_pb_common_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func (x *IP) String() string {
|
||||
func (*IP) ProtoMessage() {}
|
||||
|
||||
func (x *IP) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[3]
|
||||
mi := &file_api_pb_common_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -294,7 +294,7 @@ func (x *IP) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use IP.ProtoReflect.Descriptor instead.
|
||||
func (*IP) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{3}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *IP) GetIp() []byte {
|
||||
@@ -316,7 +316,7 @@ type IPPort struct {
|
||||
func (x *IPPort) Reset() {
|
||||
*x = IPPort{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[4]
|
||||
mi := &file_api_pb_common_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -329,7 +329,7 @@ func (x *IPPort) String() string {
|
||||
func (*IPPort) ProtoMessage() {}
|
||||
|
||||
func (x *IPPort) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[4]
|
||||
mi := &file_api_pb_common_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -342,7 +342,7 @@ func (x *IPPort) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use IPPort.ProtoReflect.Descriptor instead.
|
||||
func (*IPPort) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{4}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *IPPort) GetIp() *IP {
|
||||
@@ -371,7 +371,7 @@ type IPPrefix struct {
|
||||
func (x *IPPrefix) Reset() {
|
||||
*x = IPPrefix{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[5]
|
||||
mi := &file_api_pb_common_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -384,7 +384,7 @@ func (x *IPPrefix) String() string {
|
||||
func (*IPPrefix) ProtoMessage() {}
|
||||
|
||||
func (x *IPPrefix) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[5]
|
||||
mi := &file_api_pb_common_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -397,7 +397,7 @@ func (x *IPPrefix) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use IPPrefix.ProtoReflect.Descriptor instead.
|
||||
func (*IPPrefix) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{5}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *IPPrefix) GetIp() *IP {
|
||||
@@ -430,7 +430,7 @@ type LogsRequest struct {
|
||||
func (x *LogsRequest) Reset() {
|
||||
*x = LogsRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[6]
|
||||
mi := &file_api_pb_common_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -443,7 +443,7 @@ func (x *LogsRequest) String() string {
|
||||
func (*LogsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *LogsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[6]
|
||||
mi := &file_api_pb_common_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -456,7 +456,7 @@ func (x *LogsRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LogsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*LogsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{6}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *LogsRequest) GetId() string {
|
||||
@@ -508,7 +508,7 @@ type LogEntry struct {
|
||||
func (x *LogEntry) Reset() {
|
||||
*x = LogEntry{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[7]
|
||||
mi := &file_api_pb_common_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -521,7 +521,7 @@ func (x *LogEntry) String() string {
|
||||
func (*LogEntry) ProtoMessage() {}
|
||||
|
||||
func (x *LogEntry) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_common_proto_msgTypes[7]
|
||||
mi := &file_api_pb_common_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -534,7 +534,7 @@ func (x *LogEntry) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead.
|
||||
func (*LogEntry) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{7}
|
||||
return file_api_pb_common_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *LogEntry) GetStream() LogEntry_StreamType {
|
||||
@@ -558,84 +558,82 @@ func (x *LogEntry) GetMessage() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_internal_machine_api_pb_common_proto protoreflect.FileDescriptor
|
||||
var File_api_pb_common_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_internal_machine_api_pb_common_proto_rawDesc = []byte{
|
||||
0x0a, 0x24, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x17, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49,
|
||||
0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f,
|
||||
0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68,
|
||||
0x69, 0x6e, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2a, 0x0a,
|
||||
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75,
|
||||
0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x32, 0x0a, 0x05, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x12, 0x29, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x37, 0x0a,
|
||||
0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26,
|
||||
0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x0a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x08, 0x6d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x14, 0x0a, 0x02, 0x49, 0x50, 0x12, 0x0e, 0x0a, 0x02,
|
||||
0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x22, 0x35, 0x0a, 0x06,
|
||||
0x49, 0x50, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x07, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x02, 0x69, 0x70, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x22, 0x37, 0x0a, 0x08, 0x49, 0x50, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12,
|
||||
0x17, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x49, 0x50, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x22, 0x75, 0x0a, 0x0b,
|
||||
0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66,
|
||||
0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x6f, 0x6c,
|
||||
0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x05, 0x52, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x75, 0x6e,
|
||||
0x74, 0x69, 0x6c, 0x22, 0xd2, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e,
|
||||
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x73, 0x74, 0x72, 0x65,
|
||||
0x61, 0x6d, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||
0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x40, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
||||
0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
|
||||
0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x44, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x0a, 0x0a,
|
||||
0x06, 0x53, 0x54, 0x44, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x41,
|
||||
0x52, 0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x03, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b,
|
||||
0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70,
|
||||
0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
var file_api_pb_common_proto_rawDesc = []byte{
|
||||
0x0a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x61,
|
||||
0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x41, 0x64, 0x64, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x06,
|
||||
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x32, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74,
|
||||
0x79, 0x12, 0x29, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x37, 0x0a, 0x0d,
|
||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a,
|
||||
0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x0a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x14, 0x0a, 0x02, 0x49, 0x50, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x22, 0x35, 0x0a, 0x06, 0x49,
|
||||
0x50, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x07, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x22, 0x37, 0x0a, 0x08, 0x49, 0x50, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x17,
|
||||
0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x61, 0x70, 0x69,
|
||||
0x2e, 0x49, 0x50, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x22, 0x75, 0x0a, 0x0b, 0x4c,
|
||||
0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f,
|
||||
0x6c, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x6c,
|
||||
0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x75, 0x6e, 0x74,
|
||||
0x69, 0x6c, 0x22, 0xd2, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
|
||||
0x30, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x53,
|
||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x40, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00,
|
||||
0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x44, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06,
|
||||
0x53, 0x54, 0x44, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x41, 0x52,
|
||||
0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x03, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69,
|
||||
0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_internal_machine_api_pb_common_proto_rawDescOnce sync.Once
|
||||
file_internal_machine_api_pb_common_proto_rawDescData = file_internal_machine_api_pb_common_proto_rawDesc
|
||||
file_api_pb_common_proto_rawDescOnce sync.Once
|
||||
file_api_pb_common_proto_rawDescData = file_api_pb_common_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_internal_machine_api_pb_common_proto_rawDescGZIP() []byte {
|
||||
file_internal_machine_api_pb_common_proto_rawDescOnce.Do(func() {
|
||||
file_internal_machine_api_pb_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_machine_api_pb_common_proto_rawDescData)
|
||||
func file_api_pb_common_proto_rawDescGZIP() []byte {
|
||||
file_api_pb_common_proto_rawDescOnce.Do(func() {
|
||||
file_api_pb_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_pb_common_proto_rawDescData)
|
||||
})
|
||||
return file_internal_machine_api_pb_common_proto_rawDescData
|
||||
return file_api_pb_common_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_internal_machine_api_pb_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_internal_machine_api_pb_common_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_internal_machine_api_pb_common_proto_goTypes = []any{
|
||||
var file_api_pb_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_api_pb_common_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_api_pb_common_proto_goTypes = []any{
|
||||
(LogEntry_StreamType)(0), // 0: api.LogEntry.StreamType
|
||||
(*Metadata)(nil), // 1: api.Metadata
|
||||
(*Empty)(nil), // 2: api.Empty
|
||||
@@ -648,7 +646,7 @@ var file_internal_machine_api_pb_common_proto_goTypes = []any{
|
||||
(*status.Status)(nil), // 9: google.rpc.Status
|
||||
(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp
|
||||
}
|
||||
var file_internal_machine_api_pb_common_proto_depIdxs = []int32{
|
||||
var file_api_pb_common_proto_depIdxs = []int32{
|
||||
9, // 0: api.Metadata.status:type_name -> google.rpc.Status
|
||||
1, // 1: api.Empty.metadata:type_name -> api.Metadata
|
||||
2, // 2: api.EmptyResponse.messages:type_name -> api.Empty
|
||||
@@ -663,13 +661,13 @@ var file_internal_machine_api_pb_common_proto_depIdxs = []int32{
|
||||
0, // [0:7] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_internal_machine_api_pb_common_proto_init() }
|
||||
func file_internal_machine_api_pb_common_proto_init() {
|
||||
if File_internal_machine_api_pb_common_proto != nil {
|
||||
func init() { file_api_pb_common_proto_init() }
|
||||
func file_api_pb_common_proto_init() {
|
||||
if File_api_pb_common_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*Metadata); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -681,7 +679,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -693,7 +691,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*EmptyResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -705,7 +703,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*IP); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -717,7 +715,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*IPPort); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -729,7 +727,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*IPPrefix); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -741,7 +739,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*LogsRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -753,7 +751,7 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_common_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||
file_api_pb_common_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*LogEntry); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -770,19 +768,19 @@ func file_internal_machine_api_pb_common_proto_init() {
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_machine_api_pb_common_proto_rawDesc,
|
||||
RawDescriptor: file_api_pb_common_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_internal_machine_api_pb_common_proto_goTypes,
|
||||
DependencyIndexes: file_internal_machine_api_pb_common_proto_depIdxs,
|
||||
EnumInfos: file_internal_machine_api_pb_common_proto_enumTypes,
|
||||
MessageInfos: file_internal_machine_api_pb_common_proto_msgTypes,
|
||||
GoTypes: file_api_pb_common_proto_goTypes,
|
||||
DependencyIndexes: file_api_pb_common_proto_depIdxs,
|
||||
EnumInfos: file_api_pb_common_proto_enumTypes,
|
||||
MessageInfos: file_api_pb_common_proto_msgTypes,
|
||||
}.Build()
|
||||
File_internal_machine_api_pb_common_proto = out.File
|
||||
file_internal_machine_api_pb_common_proto_rawDesc = nil
|
||||
file_internal_machine_api_pb_common_proto_goTypes = nil
|
||||
file_internal_machine_api_pb_common_proto_depIdxs = nil
|
||||
File_api_pb_common_proto = out.File
|
||||
file_api_pb_common_proto_rawDesc = nil
|
||||
file_api_pb_common_proto_goTypes = nil
|
||||
file_api_pb_common_proto_depIdxs = nil
|
||||
}
|
||||
@@ -2,9 +2,9 @@ syntax = "proto3";
|
||||
|
||||
package api;
|
||||
|
||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||
option go_package = "github.com/psviderski/uncloud/api/pb";
|
||||
|
||||
// Vendored at internal/machine/api/vendor/google/rpc/status.proto.
|
||||
// Vendored at api/pb/vendor/google/rpc/status.proto.
|
||||
import "google/rpc/status.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,10 @@ syntax = "proto3";
|
||||
|
||||
package api;
|
||||
|
||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||
option go_package = "github.com/psviderski/uncloud/api/pb";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "internal/machine/api/pb/common.proto";
|
||||
import "api/pb/common.proto";
|
||||
|
||||
service Docker {
|
||||
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
|
||||
@@ -2,7 +2,7 @@
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.27.3
|
||||
// source: internal/machine/api/pb/docker.proto
|
||||
// source: api/pb/docker.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -807,5 +807,5 @@ var Docker_ServiceDesc = grpc.ServiceDesc{
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "internal/machine/api/pb/docker.proto",
|
||||
Metadata: "api/pb/docker.proto",
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,12 @@ syntax = "proto3";
|
||||
|
||||
package api;
|
||||
|
||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||
option go_package = "github.com/psviderski/uncloud/api/pb";
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "internal/machine/api/pb/common.proto";
|
||||
import "api/pb/common.proto";
|
||||
|
||||
service Machine {
|
||||
// CheckPrerequisites verifies if the machine meets all necessary system requirements to participate in the cluster.
|
||||
@@ -19,6 +19,22 @@ service Machine {
|
||||
rpc Inspect(google.protobuf.Empty) returns (MachineInfo);
|
||||
// InspectMachine retrieves detailed information about the machine. Supports broadcasting to multiple machines.
|
||||
rpc InspectMachine(google.protobuf.Empty) returns (InspectMachineResponse);
|
||||
// WaitForStoreVersion waits until the cluster store on this machine has reached each requested actor version, with
|
||||
// no known missing or pending transactions through those versions.
|
||||
// Corrosion may satisfy a version by applying its surviving changes or by marking it complete because its changes
|
||||
// have been superseded.
|
||||
//
|
||||
// Waiting normally makes the captured data available on this machine. However, another write may replace some of that
|
||||
// data before it arrives. Corrosion can then complete the older version without transferring the replaced data.
|
||||
// If the replacement is outside the requested versions, this RPC can succeed while the affected data is still missing
|
||||
// or outdated. This can happen during concurrent updates even when all machines are well connected.
|
||||
//
|
||||
// Success does not guarantee an exact snapshot or delivery of every historical value. Callers that require a specific
|
||||
// record or condition should verify it after waiting.
|
||||
//
|
||||
// This RPC observes native replication without initiating synchronisation.
|
||||
// Use the RPC deadline to bound the wait.
|
||||
rpc WaitForStoreVersion(WaitForStoreVersionRequest) returns (google.protobuf.Empty);
|
||||
// UpdateMachine updates the configuration of the machine.
|
||||
rpc UpdateMachine(UpdateMachineRequest) returns (UpdateMachineResponse);
|
||||
// InspectWireGuardNetwork retrieves the current WireGuard network configuration and peer status.
|
||||
@@ -105,8 +121,8 @@ message JoinClusterRequest {
|
||||
// MTU of the WireGuard interface on this machine. The daemon auto-detects the optimal MTU if 0 or not set.
|
||||
int32 wireguard_mtu = 7;
|
||||
// Cluster store version this machine must reach before participating.
|
||||
// Per-actor vector (Corrosion actor UUID → max applied db_version).
|
||||
map<string, int64> min_store_version = 6;
|
||||
// Per-actor vector (Corrosion actor UUID → minimum required db_version).
|
||||
map<string, uint64> min_store_version = 6;
|
||||
}
|
||||
|
||||
message InspectMachineResponse {
|
||||
@@ -119,9 +135,24 @@ message MachineDetails {
|
||||
MachineInfo machine = 2;
|
||||
// Round-trip times to other machines in the cluster, keyed by peer machine ID.
|
||||
map<string, RTTStats> rtts = 4;
|
||||
// Current cluster store version observed on this machine.
|
||||
// Per-actor vector (Corrosion actor UUID → max applied db_version) read from Corrosion crsql_db_versions.
|
||||
map<string, int64> store_version = 5;
|
||||
// Store replication progress observed on this machine.
|
||||
// Maps each Corrosion actor UUID to its highest processed database version. Corrosion also counts versions whose
|
||||
// changes were superseded and skipped. Versions at or below a reported version may still be missing or pending
|
||||
// locally.
|
||||
//
|
||||
// Pass this vector to WaitForStoreVersion on another machine to wait for replication through these versions.
|
||||
// See that RPC's data-availability limitations. To combine observations from several machines, take the maximum
|
||||
// for each actor.
|
||||
// Capturing this vector does not wait for replication or prevent further writes.
|
||||
map<string, uint64> store_version = 5;
|
||||
}
|
||||
|
||||
message WaitForStoreVersionRequest {
|
||||
// Minimum database version to reach for each Corrosion actor UUID.
|
||||
// Obtain a vector from InspectMachine. To combine several responses, take the maximum version for each actor.
|
||||
// Actors omitted from this vector and versions above its targets are not awaited. An empty vector requires
|
||||
// no replication. Unknown actors with positive targets remain pending until replication reaches them or the RPC ends.
|
||||
map<string, uint64> min_version = 1;
|
||||
}
|
||||
|
||||
message TokenResponse {
|
||||
@@ -2,7 +2,7 @@
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.27.3
|
||||
// source: internal/machine/api/pb/machine.proto
|
||||
// source: api/pb/machine.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -26,6 +26,7 @@ const (
|
||||
Machine_Token_FullMethodName = "/api.Machine/Token"
|
||||
Machine_Inspect_FullMethodName = "/api.Machine/Inspect"
|
||||
Machine_InspectMachine_FullMethodName = "/api.Machine/InspectMachine"
|
||||
Machine_WaitForStoreVersion_FullMethodName = "/api.Machine/WaitForStoreVersion"
|
||||
Machine_UpdateMachine_FullMethodName = "/api.Machine/UpdateMachine"
|
||||
Machine_InspectWireGuardNetwork_FullMethodName = "/api.Machine/InspectWireGuardNetwork"
|
||||
Machine_Reset_FullMethodName = "/api.Machine/Reset"
|
||||
@@ -46,6 +47,22 @@ type MachineClient interface {
|
||||
Inspect(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MachineInfo, error)
|
||||
// InspectMachine retrieves detailed information about the machine. Supports broadcasting to multiple machines.
|
||||
InspectMachine(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InspectMachineResponse, error)
|
||||
// WaitForStoreVersion waits until the cluster store on this machine has reached each requested actor version, with
|
||||
// no known missing or pending transactions through those versions.
|
||||
// Corrosion may satisfy a version by applying its surviving changes or by marking it complete because its changes
|
||||
// have been superseded.
|
||||
//
|
||||
// Waiting normally makes the captured data available on this machine. However, another write may replace some of that
|
||||
// data before it arrives. Corrosion can then complete the older version without transferring the replaced data.
|
||||
// If the replacement is outside the requested versions, this RPC can succeed while the affected data is still missing
|
||||
// or outdated. This can happen during concurrent updates even when all machines are well connected.
|
||||
//
|
||||
// Success does not guarantee an exact snapshot or delivery of every historical value. Callers that require a specific
|
||||
// record or condition should verify it after waiting.
|
||||
//
|
||||
// This RPC observes native replication without initiating synchronisation.
|
||||
// Use the RPC deadline to bound the wait.
|
||||
WaitForStoreVersion(ctx context.Context, in *WaitForStoreVersionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// UpdateMachine updates the configuration of the machine.
|
||||
UpdateMachine(ctx context.Context, in *UpdateMachineRequest, opts ...grpc.CallOption) (*UpdateMachineResponse, error)
|
||||
// InspectWireGuardNetwork retrieves the current WireGuard network configuration and peer status.
|
||||
@@ -124,6 +141,16 @@ func (c *machineClient) InspectMachine(ctx context.Context, in *emptypb.Empty, o
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *machineClient) WaitForStoreVersion(ctx context.Context, in *WaitForStoreVersionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, Machine_WaitForStoreVersion_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *machineClient) UpdateMachine(ctx context.Context, in *UpdateMachineRequest, opts ...grpc.CallOption) (*UpdateMachineResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateMachineResponse)
|
||||
@@ -196,6 +223,22 @@ type MachineServer interface {
|
||||
Inspect(context.Context, *emptypb.Empty) (*MachineInfo, error)
|
||||
// InspectMachine retrieves detailed information about the machine. Supports broadcasting to multiple machines.
|
||||
InspectMachine(context.Context, *emptypb.Empty) (*InspectMachineResponse, error)
|
||||
// WaitForStoreVersion waits until the cluster store on this machine has reached each requested actor version, with
|
||||
// no known missing or pending transactions through those versions.
|
||||
// Corrosion may satisfy a version by applying its surviving changes or by marking it complete because its changes
|
||||
// have been superseded.
|
||||
//
|
||||
// Waiting normally makes the captured data available on this machine. However, another write may replace some of that
|
||||
// data before it arrives. Corrosion can then complete the older version without transferring the replaced data.
|
||||
// If the replacement is outside the requested versions, this RPC can succeed while the affected data is still missing
|
||||
// or outdated. This can happen during concurrent updates even when all machines are well connected.
|
||||
//
|
||||
// Success does not guarantee an exact snapshot or delivery of every historical value. Callers that require a specific
|
||||
// record or condition should verify it after waiting.
|
||||
//
|
||||
// This RPC observes native replication without initiating synchronisation.
|
||||
// Use the RPC deadline to bound the wait.
|
||||
WaitForStoreVersion(context.Context, *WaitForStoreVersionRequest) (*emptypb.Empty, error)
|
||||
// UpdateMachine updates the configuration of the machine.
|
||||
UpdateMachine(context.Context, *UpdateMachineRequest) (*UpdateMachineResponse, error)
|
||||
// InspectWireGuardNetwork retrieves the current WireGuard network configuration and peer status.
|
||||
@@ -232,6 +275,9 @@ func (UnimplementedMachineServer) Inspect(context.Context, *emptypb.Empty) (*Mac
|
||||
func (UnimplementedMachineServer) InspectMachine(context.Context, *emptypb.Empty) (*InspectMachineResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method InspectMachine not implemented")
|
||||
}
|
||||
func (UnimplementedMachineServer) WaitForStoreVersion(context.Context, *WaitForStoreVersionRequest) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method WaitForStoreVersion not implemented")
|
||||
}
|
||||
func (UnimplementedMachineServer) UpdateMachine(context.Context, *UpdateMachineRequest) (*UpdateMachineResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateMachine not implemented")
|
||||
}
|
||||
@@ -376,6 +422,24 @@ func _Machine_InspectMachine_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Machine_WaitForStoreVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(WaitForStoreVersionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(MachineServer).WaitForStoreVersion(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Machine_WaitForStoreVersion_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(MachineServer).WaitForStoreVersion(ctx, req.(*WaitForStoreVersionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Machine_UpdateMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateMachineRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -490,6 +554,10 @@ var Machine_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "InspectMachine",
|
||||
Handler: _Machine_InspectMachine_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "WaitForStoreVersion",
|
||||
Handler: _Machine_WaitForStoreVersion_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateMachine",
|
||||
Handler: _Machine_UpdateMachine_Handler,
|
||||
@@ -514,5 +582,5 @@ var Machine_ServiceDesc = grpc.ServiceDesc{
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "internal/machine/api/pb/machine.proto",
|
||||
Metadata: "api/pb/machine.proto",
|
||||
}
|
||||
Vendored
@@ -0,0 +1,165 @@
|
||||
package caddystorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/psviderski/uncloud/pkg/distlock"
|
||||
)
|
||||
|
||||
const (
|
||||
lockPrefix = "caddy_storage:"
|
||||
storeReplicationTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// Lock acquires an automatically renewed distributed lock and waits for the local store to catch up with versions
|
||||
// observed on responding machines. Writers using the same lock can then read locally. Unavailable machines may have
|
||||
// writes that this wait does not cover, and reads outside a lock remain eventually consistent.
|
||||
func (s *Storage) Lock(ctx context.Context, name string) (err error) {
|
||||
if name == "" {
|
||||
return errors.New("lock name is empty")
|
||||
}
|
||||
|
||||
// Count the call first. If Cleanup has already observed zero, Caddy has cancelled s.ctx and the check below rejects
|
||||
// this call before it uses the client.
|
||||
s.lockOps.Add(1)
|
||||
// A failed Lock owns its lifecycle through any lease rollback. A successful Lock transfers that responsibility to
|
||||
// Unlock, which keeps the client open until its release attempt finishes.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
s.lockOps.Add(-1)
|
||||
}
|
||||
}()
|
||||
if s.ctx.Err() != nil {
|
||||
return errors.New("storage is closed")
|
||||
}
|
||||
|
||||
// Stop acquisition retries when Caddy unloads the module (cancels s.ctx), even if the caller's context
|
||||
// is still active.
|
||||
ctx, cancel := context.WithCancelCause(ctx)
|
||||
defer cancel(nil)
|
||||
stopOnCleanup := context.AfterFunc(s.ctx, func() {
|
||||
cancel(context.Cause(s.ctx))
|
||||
})
|
||||
defer stopOnCleanup()
|
||||
|
||||
log := s.log.With("lock", name)
|
||||
started := time.Now()
|
||||
log.Debug("acquiring lock", "ttl", time.Duration(s.LockTTL))
|
||||
lease, err := s.locker.Acquire(ctx, lockPrefix+name)
|
||||
if err != nil {
|
||||
log.Debug("failed to acquire lock", "duration", time.Since(started), "error", err)
|
||||
return fmt.Errorf("acquire lock '%s': %w", name, err)
|
||||
}
|
||||
log.Debug("lock lease acquired", "duration", time.Since(started))
|
||||
// Release the lease if the lock acquisition fails after this point.
|
||||
// Unlock will take care of releasing the lease on success.
|
||||
defer func() {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if releaseErr := s.releaseLock(ctx, name, lease, "cancelled acquisition"); releaseErr != nil {
|
||||
err = errors.Join(err, releaseErr)
|
||||
}
|
||||
}()
|
||||
|
||||
// Catch up with the latest store versions observed on responding machines to increase the chance of reading
|
||||
// the latest writes on them locally.
|
||||
version, machines, err := s.clusterStoreVersion(ctx, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
waitStarted := time.Now()
|
||||
log.Debug("waiting for local store replication", "machine_names", machines, "store_version", version)
|
||||
waitCtx, cancelWait := context.WithTimeout(ctx, storeReplicationTimeout)
|
||||
err = s.client.WaitForStoreVersion(waitCtx, version)
|
||||
cancelWait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("wait for local store replication: %w", err)
|
||||
}
|
||||
log.Debug("local store replication complete", "duration", time.Since(waitStarted))
|
||||
|
||||
s.locksMu.Lock()
|
||||
defer s.locksMu.Unlock()
|
||||
if s.ctx.Err() != nil {
|
||||
return errors.New("storage is closed")
|
||||
}
|
||||
if lost := context.Cause(lease.Context()); lost != nil {
|
||||
return lost
|
||||
}
|
||||
if _, exists := s.locks[name]; exists {
|
||||
return errors.New("lock is already tracked by this storage instance")
|
||||
}
|
||||
s.locks[name] = lease
|
||||
|
||||
log.Debug("lock acquired", "duration", time.Since(started))
|
||||
return nil
|
||||
}
|
||||
|
||||
// clusterStoreVersion returns the per-actor maximum store versions from responding machines and their names.
|
||||
func (s *Storage) clusterStoreVersion(ctx context.Context, log *slog.Logger) (map[string]uint64, []string, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, distlock.DefaultMaxNodeCallTimeout)
|
||||
defer cancel()
|
||||
resp, err := s.client.MachineClient.InspectMachine(client.ProxyMachinesContext(ctx, nil), nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("inspect machines for store versions: %w", err)
|
||||
}
|
||||
|
||||
maxVersion := make(map[string]uint64)
|
||||
machines := make([]string, 0, len(resp.Machines))
|
||||
for _, m := range resp.Machines {
|
||||
if m.Metadata.Error != "" {
|
||||
log.Warn("skipping machine when collecting store versions",
|
||||
"id", m.Metadata.MachineId, "name", m.Metadata.MachineName, "error", m.Metadata.Error)
|
||||
continue
|
||||
}
|
||||
machines = append(machines, m.Metadata.MachineName)
|
||||
for actor, v := range m.StoreVersion {
|
||||
maxVersion[actor] = max(maxVersion[actor], v)
|
||||
}
|
||||
}
|
||||
slices.Sort(machines)
|
||||
return maxVersion, machines, nil
|
||||
}
|
||||
|
||||
// Unlock releases a previously acquired distributed lock.
|
||||
func (s *Storage) Unlock(ctx context.Context, name string) error {
|
||||
s.locksMu.Lock()
|
||||
lease, exists := s.locks[name]
|
||||
if exists {
|
||||
delete(s.locks, name)
|
||||
}
|
||||
s.locksMu.Unlock()
|
||||
if !exists {
|
||||
return fmt.Errorf("lock '%s' is not held by this storage instance", name)
|
||||
}
|
||||
defer s.lockOps.Add(-1)
|
||||
|
||||
// Release stops renewal even on error. Any nodes that cannot be reached will let the lease expire.
|
||||
if err := s.releaseLock(ctx, name, lease, "unlock"); err != nil {
|
||||
return fmt.Errorf("release lock '%s': %w", name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// releaseLock releases the lease with a context that ignores cancellation and logs the release attempt.
|
||||
func (s *Storage) releaseLock(ctx context.Context, name string, lease *distlock.Lease, reason string) error {
|
||||
// Unlock and rollback must attempt node cleanup even if the caller or module has already been cancelled.
|
||||
ctx = context.WithoutCancel(ctx)
|
||||
|
||||
log := s.log.With("lock", name, "reason", reason)
|
||||
started := time.Now()
|
||||
log.Debug("releasing lock")
|
||||
if err := lease.Release(ctx); err != nil {
|
||||
log.Debug("failed to release lock", "duration", time.Since(started), "error", err)
|
||||
return err
|
||||
}
|
||||
log.Debug("lock released", "duration", time.Since(started))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
// Package caddystorage provides Caddy storage backed by an Uncloud cluster.
|
||||
package caddystorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/psviderski/uncloud/pkg/client/connector"
|
||||
"github.com/psviderski/uncloud/pkg/distlock"
|
||||
)
|
||||
|
||||
const (
|
||||
// ModuleID is the Caddy module ID for Uncloud storage.
|
||||
ModuleID = "caddy.storage.uncloud"
|
||||
// DefaultSocketPath is the default path to the Uncloud API socket.
|
||||
DefaultSocketPath = "/run/uncloud/api/uncloud.sock"
|
||||
// DefaultLockTTL is the default duration of a distributed lock lease.
|
||||
DefaultLockTTL = 20 * time.Second
|
||||
// lockCleanupTimeout bounds how long an unloaded module waits for active lock operations when cleaning up.
|
||||
lockCleanupTimeout = 5 * time.Minute
|
||||
)
|
||||
|
||||
//nolint:gochecknoinits // Caddy modules must register during package initialisation.
|
||||
func init() {
|
||||
caddy.RegisterModule(new(Storage))
|
||||
}
|
||||
|
||||
// Storage implements a Caddy storage backend that uses an Uncloud cluster to store assets such as TLS certificates.
|
||||
type Storage struct {
|
||||
// Socket is the path to the Uncloud API socket.
|
||||
// Defaults to /run/uncloud/api/uncloud.sock when not set.
|
||||
Socket string `json:"socket,omitempty"`
|
||||
// LockTTL is the duration of a distributed lock after which it expires if not renewed. Locks renew automatically
|
||||
// until unlocked. If an instance crashes or cannot renew, expiry allows another instance to acquire the stale lock.
|
||||
// Longer durations tolerate longer interruptions but delay recovery after a crash. Normal unlocks release the lock
|
||||
// immediately.
|
||||
// Defaults to 20 seconds when not set.
|
||||
LockTTL caddy.Duration `json:"lock_ttl,omitempty"`
|
||||
|
||||
client *client.Client
|
||||
locker *distlock.Locker
|
||||
log *slog.Logger
|
||||
|
||||
// ctx is the module context from Provision. Caddy cancels it when it unloads the module and calls Cleanup.
|
||||
ctx context.Context
|
||||
|
||||
// locksMu protects locks.
|
||||
locksMu sync.Mutex
|
||||
// locks maps successfully acquired lock names to held leases.
|
||||
locks map[string]*distlock.Lease
|
||||
// lockOps counts Lock calls until they fail or their locks are released.
|
||||
lockOps atomic.Int64
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
func (*Storage) CaddyModule() caddy.ModuleInfo {
|
||||
return caddy.ModuleInfo{
|
||||
ID: ModuleID,
|
||||
New: func() caddy.Module { return new(Storage) },
|
||||
}
|
||||
}
|
||||
|
||||
// Provision connects the storage to the local Uncloud API and initialises the distributed locker.
|
||||
func (s *Storage) Provision(ctx caddy.Context) error {
|
||||
s.ctx = ctx
|
||||
s.log = ctx.Slogger()
|
||||
|
||||
if s.Socket == "" {
|
||||
s.Socket = DefaultSocketPath
|
||||
}
|
||||
if s.LockTTL == 0 {
|
||||
s.LockTTL = caddy.Duration(DefaultLockTTL)
|
||||
}
|
||||
if s.LockTTL < 0 {
|
||||
return errors.New("lock_ttl must be positive")
|
||||
}
|
||||
|
||||
cli, err := client.New(ctx, connector.NewUnixConnector(s.Socket))
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect to Uncloud API: %w", err)
|
||||
}
|
||||
|
||||
locker, err := cli.NewLocker(distlock.Config{
|
||||
LeaseDuration: time.Duration(s.LockTTL),
|
||||
})
|
||||
if err != nil {
|
||||
_ = cli.Close()
|
||||
return fmt.Errorf("create distributed locker: %w", err)
|
||||
}
|
||||
|
||||
s.client = cli
|
||||
s.locker = locker
|
||||
s.locks = make(map[string]*distlock.Lease)
|
||||
|
||||
s.log.Info("module provisioned", "socket", s.Socket, "lock_ttl", time.Duration(s.LockTTL))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup keeps the Uncloud API connection open so acquired lock leases can renew until Caddy releases them. It closes
|
||||
// the connection after all lock operations finish or the cleanup timeout expires.
|
||||
func (s *Storage) Cleanup() error {
|
||||
if s.client == nil {
|
||||
// Provision failed before opening the storage.
|
||||
return nil
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
s.log.Debug("cleaning up module", "locks", s.lockOps.Load())
|
||||
|
||||
go func() {
|
||||
timer := time.NewTimer(lockCleanupTimeout)
|
||||
defer timer.Stop()
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
timedOut := false
|
||||
for !timedOut && s.lockOps.Load() > 0 {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-timer.C:
|
||||
timedOut = true
|
||||
}
|
||||
}
|
||||
if remaining := s.lockOps.Load(); timedOut && remaining > 0 {
|
||||
s.log.Warn("timed out waiting for active locks to be unlocked",
|
||||
"locks", remaining, "timeout", lockCleanupTimeout)
|
||||
}
|
||||
|
||||
if err := s.client.Close(); err != nil {
|
||||
s.log.Warn("failed to clean up module", "duration", time.Since(started), "error", err)
|
||||
return
|
||||
}
|
||||
s.log.Debug("module cleanup complete", "duration", time.Since(started))
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CertMagicStorage returns the provisioned CertMagic storage implementation.
|
||||
func (s *Storage) CertMagicStorage() (certmagic.Storage, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// UnmarshalCaddyfile configures Uncloud storage from the Caddyfile global storage block.
|
||||
//
|
||||
// {
|
||||
// storage uncloud {
|
||||
// socket /run/uncloud/api/uncloud.sock
|
||||
// lock_ttl 20s
|
||||
// }
|
||||
// }
|
||||
func (s *Storage) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
d.Next() // Skip the module name 'uncloud'.
|
||||
// Reject inline arguments. NextArg leaves an opening brace for NextBlock.
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
|
||||
// Read the optional options block, skipping its surrounding braces.
|
||||
for d.NextBlock(0) {
|
||||
switch d.Val() {
|
||||
case "socket":
|
||||
// Require a socket path on the same line as 'socket' option.
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
s.Socket = d.Val()
|
||||
// Reject extra arguments after the socket path.
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
case "lock_ttl":
|
||||
if !d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
ttl, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
return d.Errf("invalid lock_ttl '%s': %w", d.Val(), err)
|
||||
}
|
||||
if ttl <= 0 {
|
||||
return d.Err("lock_ttl must be positive")
|
||||
}
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
s.LockTTL = caddy.Duration(ttl)
|
||||
default:
|
||||
return d.Errf("unknown uncloud storage option: '%s'", d.Val())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ caddy.Module = (*Storage)(nil)
|
||||
_ caddy.Provisioner = (*Storage)(nil)
|
||||
_ caddy.CleanerUpper = (*Storage)(nil)
|
||||
_ caddy.StorageConverter = (*Storage)(nil)
|
||||
_ caddyfile.Unmarshaler = (*Storage)(nil)
|
||||
_ certmagic.Storage = (*Storage)(nil)
|
||||
)
|
||||
@@ -0,0 +1,83 @@
|
||||
package caddystorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Store writes a value to the cluster store.
|
||||
func (s *Storage) Store(ctx context.Context, key string, value []byte) error {
|
||||
if _, err := s.client.CaddyStorage.Store(ctx, &pb.StoreCaddyStorageRequest{Key: key, Value: value}); err != nil {
|
||||
return storageError("store", key, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load reads a value from the cluster store.
|
||||
func (s *Storage) Load(ctx context.Context, key string) ([]byte, error) {
|
||||
resp, err := s.client.CaddyStorage.Load(ctx, &pb.LoadCaddyStorageRequest{Key: key})
|
||||
if err != nil {
|
||||
return nil, storageError("load", key, err)
|
||||
}
|
||||
return resp.Value, nil
|
||||
}
|
||||
|
||||
// Delete removes a key and its descendants from the cluster store.
|
||||
func (s *Storage) Delete(ctx context.Context, key string) error {
|
||||
if _, err := s.client.CaddyStorage.Delete(ctx, &pb.DeleteCaddyStorageRequest{Key: key}); err != nil {
|
||||
return storageError("delete", key, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists reports whether a key exists in the cluster store.
|
||||
func (s *Storage) Exists(ctx context.Context, key string) bool {
|
||||
_, err := s.Stat(ctx, key)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// List returns keys under prefix from the cluster store.
|
||||
func (s *Storage) List(ctx context.Context, prefix string, recursive bool) ([]string, error) {
|
||||
resp, err := s.client.CaddyStorage.List(ctx, &pb.ListCaddyStorageRequest{
|
||||
Prefix: prefix,
|
||||
Recursive: recursive,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, storageError("list", prefix, err)
|
||||
}
|
||||
return resp.Keys, nil
|
||||
}
|
||||
|
||||
// Stat returns information about a key in the cluster store.
|
||||
func (s *Storage) Stat(ctx context.Context, key string) (certmagic.KeyInfo, error) {
|
||||
resp, err := s.client.CaddyStorage.Stat(ctx, &pb.StatCaddyStorageRequest{Key: key})
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, storageError("stat", key, err)
|
||||
}
|
||||
|
||||
info := certmagic.KeyInfo{
|
||||
Key: resp.Key,
|
||||
Size: resp.Size,
|
||||
IsTerminal: resp.IsTerminal,
|
||||
}
|
||||
if resp.UpdatedAt != nil {
|
||||
if err := resp.UpdatedAt.CheckValid(); err != nil {
|
||||
return certmagic.KeyInfo{}, storageError("stat", key, fmt.Errorf("invalid updated_at timestamp: %w", err))
|
||||
}
|
||||
info.Modified = resp.UpdatedAt.AsTime()
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func storageError(operation, key string, err error) error {
|
||||
if status.Code(err) == codes.NotFound {
|
||||
err = fs.ErrNotExist
|
||||
}
|
||||
return fmt.Errorf("uncloud storage: %s key %q: %w", operation, key, err)
|
||||
}
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/cli/completion"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/cmd/uc/caddy"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
+11
-5
@@ -40,13 +40,19 @@ func NewAddCommand() *cobra.Command {
|
||||
Short: "Add a remote machine to a cluster.",
|
||||
Long: `Add a new machine to an existing Uncloud cluster.
|
||||
|
||||
By default, it installs Docker and the Uncloud daemon on the machine over SSH unless --no-install
|
||||
is specified, which assumes they are already installed and running.
|
||||
|
||||
Connection methods:
|
||||
[ssh://]user@host - Use system 'ssh' command with full SSH config support (default, no prefix required)
|
||||
ssh+go://user@host - Use Go's built-in SSH library`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM")
|
||||
|
||||
cli.BindEnvToFlag(cmd, "version", "UNCLOUD_DAEMON_VERSION")
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||
|
||||
// Determine connection mode and strip scheme.
|
||||
@@ -79,7 +85,7 @@ Connection methods:
|
||||
)
|
||||
cmd.Flags().BoolVar(
|
||||
&opts.noInstall, "no-install", false,
|
||||
"Skip installation of Docker, Uncloud daemon, and dependencies on the machine. "+
|
||||
"Skip installation of Docker and the Uncloud daemon on the machine. "+
|
||||
"Assumes they're already installed and running.",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
@@ -94,7 +100,7 @@ Connection methods:
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&opts.version, "version", "latest",
|
||||
"Version of the Uncloud daemon to install on the machine.",
|
||||
"Version of the Uncloud daemon to install on the machine. [$UNCLOUD_DAEMON_VERSION]",
|
||||
)
|
||||
cmd.Flags().StringSliceVar(
|
||||
&opts.wgEndpoints, "wg-endpoint", nil,
|
||||
@@ -186,7 +192,7 @@ func add(ctx context.Context, uncli *cli.CLI, remoteMachine *cli.RemoteMachine,
|
||||
// Deploy a Caddy service container to the added machine. If caddy service is already deployed on other machines,
|
||||
// use the deployed image version.
|
||||
// NOTE: We use the cluster client to inspect and scale the Caddy service because the newly added machine may have
|
||||
// issues accessing the Machine API of existing machines in the cluster.
|
||||
// issues accessing the API of existing machines in the cluster.
|
||||
// See the issue for more details: https://github.com/psviderski/uncloud/issues/65.
|
||||
caddyImage := ""
|
||||
caddySvc, err := clusterClient.InspectService(ctx, client.CaddyServiceName)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// runVersionFlagEnvTest builds the given command for each case, stubs out RunE so only flag
|
||||
// parsing and PreRunE run (no SSH calls), and asserts the resolved --version flag value.
|
||||
// Note: cannot use t.Parallel() because subtests use t.Setenv().
|
||||
func runVersionFlagEnvTest(t *testing.T, newCommand func() *cobra.Command) {
|
||||
t.Helper()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
env string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "env var sets version when flag not passed",
|
||||
args: []string{"root@localhost"},
|
||||
env: "v1.2.3",
|
||||
want: "v1.2.3",
|
||||
},
|
||||
{
|
||||
name: "explicit flag wins over env var",
|
||||
args: []string{"root@localhost", "--version", "v9.9.9"},
|
||||
env: "v1.2.3",
|
||||
want: "v9.9.9",
|
||||
},
|
||||
{
|
||||
name: "defaults to latest when neither flag nor env var set",
|
||||
args: []string{"root@localhost"},
|
||||
want: "latest",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.env != "" {
|
||||
t.Setenv("UNCLOUD_DAEMON_VERSION", tt.env)
|
||||
}
|
||||
|
||||
cmd := newCommand()
|
||||
cmd.RunE = func(*cobra.Command, []string) error {
|
||||
return nil
|
||||
}
|
||||
cmd.SetArgs(tt.args)
|
||||
require.NoError(t, cmd.Execute())
|
||||
|
||||
version, err := cmd.Flags().GetString("version")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, version)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestInitCommandVersionFlagEnvVar verifies that the UNCLOUD_DAEMON_VERSION environment variable
|
||||
// is bound to the --version flag of 'machine init', and that an explicit flag wins.
|
||||
func TestInitCommandVersionFlagEnvVar(t *testing.T) {
|
||||
runVersionFlagEnvTest(t, NewInitCommand)
|
||||
}
|
||||
|
||||
// TestAddCommandVersionFlagEnvVar verifies that the UNCLOUD_DAEMON_VERSION environment variable
|
||||
// is bound to the --version flag of 'machine add', and that an explicit flag wins.
|
||||
func TestAddCommandVersionFlagEnvVar(t *testing.T) {
|
||||
runVersionFlagEnvTest(t, NewAddCommand)
|
||||
}
|
||||
+11
-5
@@ -8,12 +8,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/cmd/uc/caddy"
|
||||
"github.com/psviderski/uncloud/cmd/uc/dns"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/cli/config"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/cluster"
|
||||
"github.com/psviderski/uncloud/internal/machine/network"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
@@ -45,6 +45,9 @@ func NewInitCommand() *cobra.Command {
|
||||
Long: `Initialise a new cluster by setting up a remote machine as the first member.
|
||||
This command creates a new context in your Uncloud config to manage the cluster.
|
||||
|
||||
By default, it installs Docker and the Uncloud daemon on the machine over SSH unless --no-install
|
||||
is specified, which assumes they are already installed and running.
|
||||
|
||||
Connection methods:
|
||||
[ssh://]user@host - Use system 'ssh' command with full SSH config support (default, no prefix required)
|
||||
ssh+go://user@host - Use Go's built-in SSH library`,
|
||||
@@ -62,9 +65,12 @@ Connection methods:
|
||||
uc machine init root@<your-server-ip> --no-caddy --no-dns`,
|
||||
// TODO: support initialising a cluster on the local machine.
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM")
|
||||
|
||||
cli.BindEnvToFlag(cmd, "version", "UNCLOUD_DAEMON_VERSION")
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||
|
||||
var remoteMachine *cli.RemoteMachine
|
||||
@@ -117,7 +123,7 @@ Connection methods:
|
||||
)
|
||||
cmd.Flags().BoolVar(
|
||||
&opts.noInstall, "no-install", false,
|
||||
"Skip installation of Docker, Uncloud daemon, and dependencies on the machine. "+
|
||||
"Skip installation of Docker and the Uncloud daemon on the machine. "+
|
||||
"Assumes they're already installed and running.",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
@@ -132,7 +138,7 @@ Connection methods:
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&opts.version, "version", "latest",
|
||||
"Version of the Uncloud daemon to install on the machine.",
|
||||
"Version of the Uncloud daemon to install on the machine. [$UNCLOUD_DAEMON_VERSION]",
|
||||
)
|
||||
cmd.Flags().StringSliceVar(
|
||||
&opts.wgEndpoints, "wg-endpoint", nil,
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
"charm.land/lipgloss/v2/tree"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/cli/completion"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -136,8 +136,7 @@ func remove(ctx context.Context, uncli *cli.CLI, nameOrID string, opts removeOpt
|
||||
return fmt.Errorf("confirm removal: %w", err)
|
||||
}
|
||||
if !confirmed {
|
||||
fmt.Println("Cancelled. Machine was not removed.")
|
||||
return nil
|
||||
return cli.Cancelled("Cancelled. Machine was not removed.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/cli/completion"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/network"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
+35
-2
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/psviderski/uncloud/internal/machine"
|
||||
"github.com/psviderski/uncloud/internal/version"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type globalOptions struct {
|
||||
@@ -42,6 +43,13 @@ func main() {
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Shell completion runs through the hidden __complete command which has flag parsing disabled,
|
||||
// so the global flags from the completed command line are never parsed. Apply them manually to make
|
||||
// completion work with --connect, --context, and --uncloud-config.
|
||||
if cmd.Name() == cobra.ShellCompRequestCmd {
|
||||
applyGlobalFlagsFromCompletionArgs(cmd.Root().PersistentFlags(), os.Args[1:])
|
||||
}
|
||||
|
||||
cli.BindEnvToFlag(cmd, "connect", "UNCLOUD_CONNECT")
|
||||
cli.BindEnvToFlag(cmd, "context", "UNCLOUD_CONTEXT")
|
||||
cli.BindEnvToFlag(cmd, "uncloud-config", "UNCLOUD_CONFIG")
|
||||
@@ -80,10 +88,11 @@ func main() {
|
||||
|
||||
configPath := fs.ExpandHomeDir(opts.configPath)
|
||||
|
||||
// Make uc connect via the local Unix socket when running on a cluster machine.
|
||||
if opts.connect == "" {
|
||||
if !fs.Exists(configPath) && fs.Exists(machine.DefaultUncloudSockPath) {
|
||||
if !fs.Exists(configPath) && fs.Exists(machine.DefaultClusterAPISockPath) {
|
||||
conn = &config.MachineConnection{
|
||||
Unix: machine.DefaultUncloudSockPath,
|
||||
Unix: machine.DefaultClusterAPISockPath,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,3 +168,27 @@ func main() {
|
||||
cobra.CheckErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
// applyGlobalFlagsFromCompletionArgs parses the global flags from the raw arguments of a __complete command and applies
|
||||
// the ones found to flags. The trailing word being completed, unknown flags, and positional arguments are ignored.
|
||||
func applyGlobalFlagsFromCompletionArgs(flags *pflag.FlagSet, args []string) {
|
||||
// The shell always passes the word being completed as the last argument, even if it's empty.
|
||||
// Exclude it from parsing as its value may not be complete yet.
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
args = args[:len(args)-1]
|
||||
|
||||
fset := pflag.NewFlagSet("global", pflag.ContinueOnError)
|
||||
fset.ParseErrorsAllowlist.UnknownFlags = true
|
||||
fset.String("connect", "", "")
|
||||
fset.StringP("context", "c", "", "")
|
||||
fset.String("uncloud-config", "", "")
|
||||
// Parsing an incomplete command line may fail, apply the flags parsed so far anyway.
|
||||
_ = fset.Parse(args)
|
||||
|
||||
fset.Visit(func(f *pflag.Flag) {
|
||||
// Setting the flag marks it as changed so it takes precedence over environment variables.
|
||||
_ = flags.Set(f.Name, f.Value.String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestApplyGlobalFlagsFromCompletionArgs(t *testing.T) {
|
||||
defaultConfigPath := "~/.config/uncloud/config.yaml"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantConnect string
|
||||
wantContext string
|
||||
wantConfigPath string
|
||||
// Flag names expected to be marked as changed on the target flag set.
|
||||
wantChanged []string
|
||||
}{
|
||||
{
|
||||
name: "no flags",
|
||||
args: []string{"__complete", "inspect", ""},
|
||||
},
|
||||
{
|
||||
name: "connect with space",
|
||||
args: []string{"__complete", "--connect", "ssh://user@host", "inspect", ""},
|
||||
wantConnect: "ssh://user@host",
|
||||
wantChanged: []string{"connect"},
|
||||
},
|
||||
{
|
||||
name: "connect with equals",
|
||||
args: []string{"__complete", "--connect=tcp://127.0.0.1:51000", "inspect", ""},
|
||||
wantConnect: "tcp://127.0.0.1:51000",
|
||||
wantChanged: []string{"connect"},
|
||||
},
|
||||
{
|
||||
name: "context shorthand",
|
||||
args: []string{"__complete", "-c", "prod", "inspect", ""},
|
||||
wantContext: "prod",
|
||||
wantChanged: []string{"context"},
|
||||
},
|
||||
{
|
||||
name: "all flags",
|
||||
args: []string{"__complete", "--connect", "user@host", "-c", "prod", "--uncloud-config", "/tmp/uncloud.yaml", "inspect", ""},
|
||||
wantConnect: "user@host",
|
||||
wantContext: "prod",
|
||||
wantConfigPath: "/tmp/uncloud.yaml",
|
||||
wantChanged: []string{"connect", "context", "uncloud-config"},
|
||||
},
|
||||
{
|
||||
name: "unknown flags are ignored",
|
||||
args: []string{"__complete", "--quiet", "-n", "5", "--connect", "user@host", "logs", ""},
|
||||
wantConnect: "user@host",
|
||||
wantChanged: []string{"connect"},
|
||||
},
|
||||
{
|
||||
name: "flags after double dash are ignored",
|
||||
args: []string{"__complete", "exec", "svc", "--", "sh", "--connect", "user@host"},
|
||||
},
|
||||
{
|
||||
name: "flags before double dash are applied",
|
||||
args: []string{"__complete", "--connect", "user@host", "exec", "svc", "--", "sh", "-c", "env"},
|
||||
wantConnect: "user@host",
|
||||
wantChanged: []string{"connect"},
|
||||
},
|
||||
{
|
||||
name: "partial flag name being completed is excluded",
|
||||
args: []string{"__complete", "--connect", "user@host", "inspect", "--context"},
|
||||
wantConnect: "user@host",
|
||||
wantChanged: []string{"connect"},
|
||||
},
|
||||
{
|
||||
name: "partial flag value being completed is excluded",
|
||||
args: []string{"__complete", "--uncloud-config", "/tmp/"},
|
||||
},
|
||||
{
|
||||
name: "partial connect value being completed is excluded",
|
||||
args: []string{"__complete", "--connect", "tcp://127.0.0.1:5"},
|
||||
},
|
||||
{
|
||||
name: "completed flag value with partial command word",
|
||||
args: []string{"__complete", "--uncloud-config", "/tmp/uncloud.yaml", "insp"},
|
||||
wantConfigPath: "/tmp/uncloud.yaml",
|
||||
wantChanged: []string{"uncloud-config"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Mirror the global persistent flags defined on the root command.
|
||||
var opts globalOptions
|
||||
flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
flags.StringVar(&opts.connect, "connect", "", "")
|
||||
flags.StringVarP(&opts.context, "context", "c", "", "")
|
||||
flags.StringVar(&opts.configPath, "uncloud-config", defaultConfigPath, "")
|
||||
|
||||
applyGlobalFlagsFromCompletionArgs(flags, tt.args)
|
||||
|
||||
assert.Equal(t, tt.wantConnect, opts.connect)
|
||||
assert.Equal(t, tt.wantContext, opts.context)
|
||||
wantConfigPath := tt.wantConfigPath
|
||||
if wantConfigPath == "" {
|
||||
wantConfigPath = defaultConfigPath
|
||||
}
|
||||
assert.Equal(t, wantConfigPath, opts.configPath)
|
||||
|
||||
for _, name := range []string{"connect", "context", "uncloud-config"} {
|
||||
assert.Equal(t, slices.Contains(tt.wantChanged, name), flags.Changed(name),
|
||||
"changed status of flag '%s'", name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+15
-16
@@ -132,35 +132,34 @@ func runProxy(ctx context.Context, uncli *cli.CLI, opts proxyOptions) error {
|
||||
// endpoint and shuffles the data, *it* will actually experience errors.
|
||||
remoteAddr := net.JoinHostPort(ip.String(), strconv.Itoa(opts.remotePort))
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
p := &proxy.Proxy{
|
||||
Listener: listener,
|
||||
RemoteAddr: remoteAddr,
|
||||
DialContext: dialer.DialContext,
|
||||
OnError: func(err error) {
|
||||
fmt.Printf("Failed to proxy to '%s': %v\n", remoteAddr, err)
|
||||
cancel()
|
||||
if proxy.IsConnectionClosedError(err) {
|
||||
return
|
||||
}
|
||||
// A more actionable error instead of the cryptic [ssh -W] command error.
|
||||
if strings.Contains(err.Error(), "Session open refused by peer") {
|
||||
fmt.Printf("Could not connect to '%s': connection refused. "+
|
||||
"Check that the service is running and listening on port %d inside the container.\n",
|
||||
remoteAddr, opts.remotePort)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Failed to proxy a connection to '%s': %v\n", remoteAddr, err)
|
||||
},
|
||||
}
|
||||
|
||||
// Run the proxy in the background and signal when it has fully shut down.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
p.Run(ctx)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Prefix the local address with the scheme for common HTTP ports so it becomes control-clickable in most
|
||||
// terminals. We assume plain HTTP since TLS is typically terminated by Caddy in front of the service.
|
||||
fmt.Printf("%s%s → %s (%s%s%s)\n", schemeForPort(opts.remotePort), p.Listener.Addr().String(),
|
||||
remoteAddr, opts.service, tui.Faint.Render("/"), containerID)
|
||||
|
||||
<-ctx.Done()
|
||||
// Wait for the proxy to drain in-flight connections and shut down gracefully.
|
||||
<-done
|
||||
|
||||
if err = p.Run(ctx); err != nil {
|
||||
return fmt.Errorf("run proxy to '%s': %w", remoteAddr, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@ func printContainers(containers []containerInfo) error {
|
||||
}
|
||||
|
||||
func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo, error) {
|
||||
listCtx := cli.ProxyMachinesContext(ctx, nil)
|
||||
listCtx := client.ProxyMachinesContext(ctx, nil)
|
||||
|
||||
// List all service containers across all machines in the cluster.
|
||||
machineContainers, err := cli.Docker.ListServiceContainers(
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/docker"
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -30,9 +30,11 @@ func NewScaleCommand(groupID string) *cobra.Command {
|
||||
Short: "Scale a replicated service by changing the number of replicas.",
|
||||
Long: "Scale a replicated service by changing the number of replicas.",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM")
|
||||
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||
|
||||
opts.service = args[0]
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/cli/completion"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
+1
-2
@@ -92,8 +92,7 @@ func remove(ctx context.Context, uncli *cli.CLI, names []string, opts removeOpti
|
||||
return fmt.Errorf("confirm removal: %w", err)
|
||||
}
|
||||
if !confirmed {
|
||||
fmt.Println("Cancelled. No volumes were removed.")
|
||||
return nil
|
||||
return cli.Cancelled("Cancelled. No volumes were removed.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/cli/completion"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
@@ -23,7 +23,7 @@ func newDialStdioCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&socketPath, "socket", machine.DefaultUncloudSockPath,
|
||||
cmd.Flags().StringVar(&socketPath, "socket", machine.DefaultClusterAPISockPath,
|
||||
"Path to the Uncloud API socket")
|
||||
|
||||
return cmd
|
||||
|
||||
+10
-5
@@ -19,16 +19,21 @@ require (
|
||||
github.com/siderolabs/discovery-api v0.1.4
|
||||
github.com/siderolabs/discovery-client v0.1.9
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
google.golang.org/grpc v1.74.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/bgentry/speakeasy v0.2.0 // indirect
|
||||
github.com/caarlos0/go-version v0.2.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
@@ -123,17 +128,17 @@ require (
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||
google.golang.org/grpc v1.74.2 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
|
||||
+14
-4
@@ -5,6 +5,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
@@ -38,6 +40,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
|
||||
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/caarlos0/go-version v0.2.2 h1:5r+nlrg4H2wOVwWjqRqRRIRbZ7ytRmjC9xoMIP0a5kQ=
|
||||
github.com/caarlos0/go-version v0.2.2/go.mod h1:X+rI5VAtJDpcjCjeEIXpxGa5+rTcgur1FK66wS0/944=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -660,8 +664,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-20210220032951-036812b2e83c/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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -694,8 +698,8 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
@@ -707,6 +711,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -725,6 +731,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
@@ -776,6 +784,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package connector
|
||||
package wg
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,39 +7,48 @@ import (
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/cli/config"
|
||||
"github.com/psviderski/uncloud/internal/grpcversion"
|
||||
"github.com/psviderski/uncloud/internal/machine/constants"
|
||||
"github.com/psviderski/uncloud/internal/machine/network"
|
||||
"github.com/psviderski/uncloud/internal/machine/network/tunnel"
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/psviderski/uncloud/internal/secret"
|
||||
"golang.org/x/net/proxy"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
// Machine describes a remote machine that the experimental connector can reach.
|
||||
type Machine struct {
|
||||
Host string
|
||||
PublicKey secret.Secret
|
||||
}
|
||||
|
||||
type User interface {
|
||||
ManagementIP() netip.Addr
|
||||
PrivateKey() secret.Secret
|
||||
}
|
||||
|
||||
// WireGuardConnector establishes a connection to the cluster API through a WireGuard tunnel
|
||||
// to one of the cluster machines.
|
||||
type WireGuardConnector struct {
|
||||
user *client.User
|
||||
machines []config.MachineConnection
|
||||
tun *tunnel.Tunnel
|
||||
user User
|
||||
machines []Machine
|
||||
tun *Tunnel
|
||||
}
|
||||
|
||||
func NewWireGuardConnector(user *client.User, machines []config.MachineConnection) *WireGuardConnector {
|
||||
func NewWireGuardConnector(user User, machines []Machine) *WireGuardConnector {
|
||||
return &WireGuardConnector{
|
||||
user: user,
|
||||
machines: machines,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle context cancelation.
|
||||
// TODO: handle context cancellation.
|
||||
func (c *WireGuardConnector) Connect(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
if len(c.machines) == 0 {
|
||||
return nil, fmt.Errorf("no machines to connect to")
|
||||
}
|
||||
// TODO: iterate over machines and try to connect to each one until successful.
|
||||
// For now, try to connect to only the first machine.
|
||||
// For now, try to connect to only the first machine.
|
||||
machine := c.machines[0]
|
||||
endpointIPs, err := net.LookupIP(machine.Host)
|
||||
if err != nil {
|
||||
@@ -49,25 +58,24 @@ func (c *WireGuardConnector) Connect(ctx context.Context) (*grpc.ClientConn, err
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse IP address %q: %w", endpointIPs[0].String(), err)
|
||||
}
|
||||
endpoint := netip.AddrPortFrom(endpointAddr, tunnel.DefaultEndpointPort)
|
||||
endpoint := netip.AddrPortFrom(endpointAddr, DefaultEndpointPort)
|
||||
machineManagementIP := network.ManagementIP(machine.PublicKey)
|
||||
machineAPIAddr := net.JoinHostPort(machineManagementIP.String(), strconv.Itoa(constants.MachineAPIPort))
|
||||
machineAPIAddr := net.JoinHostPort(machineManagementIP.String(), strconv.Itoa(constants.UncloudAPIPort))
|
||||
|
||||
tunCfg := &tunnel.Config{
|
||||
tunCfg := &Config{
|
||||
LocalAddress: c.user.ManagementIP(),
|
||||
LocalPrivateKey: c.user.PrivateKey(),
|
||||
RemotePublicKey: machine.PublicKey,
|
||||
RemoteNetwork: netip.PrefixFrom(machineManagementIP, 128),
|
||||
Endpoint: endpoint,
|
||||
}
|
||||
if c.tun, err = tunnel.Connect(tunCfg); err != nil {
|
||||
if c.tun, err = Connect(tunCfg); err != nil {
|
||||
return nil, fmt.Errorf("establish WireGuard tunnel to %q: %w", endpoint, err)
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(
|
||||
machineAPIAddr,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithDefaultServiceConfig(defaultServiceConfig),
|
||||
grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
return c.tun.DialContext(ctx, "tcp", addr)
|
||||
}),
|
||||
@@ -1,4 +1,4 @@
|
||||
package tunnel
|
||||
package wg
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
const (
|
||||
DefaultEndpointPort = 51820
|
||||
// DefaultKeepaliveInterval is sensible interval that works with a wide variety of firewalls.
|
||||
// DefaultKeepaliveInterval is a sensible interval that works with a wide variety of firewalls.
|
||||
DefaultKeepaliveInterval = 25 * time.Second
|
||||
)
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
module github.com/psviderski/uncloud
|
||||
|
||||
go 1.26
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
charm.land/bubbles/v2 v2.0.0
|
||||
charm.land/bubbletea/v2 v2.0.2
|
||||
charm.land/huh/v2 v2.0.1
|
||||
charm.land/lipgloss/v2 v2.0.1
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/BurntSushi/toml v1.6.0
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/Masterminds/squirrel v1.5.4
|
||||
github.com/alecthomas/chroma/v2 v2.20.0
|
||||
github.com/alecthomas/chroma/v2 v2.24.1
|
||||
github.com/caarlos0/go-version v0.2.2
|
||||
github.com/caddyserver/caddy/v2 v2.8.4
|
||||
github.com/caddyserver/caddy/v2 v2.11.4
|
||||
github.com/caddyserver/certmagic v0.25.4
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/charmbracelet/colorprofile v0.4.3
|
||||
github.com/charmbracelet/x/ansi v0.11.7
|
||||
@@ -33,69 +34,75 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/miekg/dns v1.1.65
|
||||
github.com/miekg/dns v1.1.73
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/prometheus/client_golang v1.24.1
|
||||
github.com/psviderski/unregistry v0.4.1
|
||||
github.com/siderolabs/grpc-proxy v0.5.1
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/sys v0.45.0
|
||||
golang.org/x/term v0.34.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/net v0.59.0
|
||||
golang.org/x/sync v0.23.0
|
||||
golang.org/x/sys v0.48.0
|
||||
golang.org/x/term v0.46.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a
|
||||
google.golang.org/grpc v1.74.2
|
||||
google.golang.org/protobuf v1.36.9
|
||||
modernc.org/sqlite v1.36.3
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4
|
||||
google.golang.org/grpc v1.81.0
|
||||
google.golang.org/protobuf v1.36.12
|
||||
modernc.org/sqlite v1.49.1
|
||||
tags.cncf.io/container-device-interface v1.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/kms v1.19.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
cloud.google.com/go/auth v0.20.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/bigmod v0.1.0 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e // indirect
|
||||
github.com/KimMachineGun/automemlimit v0.7.5 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.13.0 // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect
|
||||
github.com/aws/smithy-go v1.25.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.4 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.5 // indirect
|
||||
github.com/catppuccin/go v0.3.0 // indirect
|
||||
github.com/ccoveille/go-safecast/v2 v2.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654 // indirect
|
||||
@@ -107,6 +114,7 @@ require (
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.5 // indirect
|
||||
github.com/containerd/console v1.0.5 // indirect
|
||||
github.com/containerd/containerd/api v1.9.0 // indirect
|
||||
@@ -119,15 +127,16 @@ require (
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgraph-io/ristretto v0.2.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/distribution/distribution/v3 v3.0.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/dlclark/regexp2 v1.12.0 // indirect
|
||||
github.com/docker/buildx v0.29.1 // indirect
|
||||
github.com/docker/cli-docs-tool v0.10.0 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
@@ -141,37 +150,34 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsevents v0.2.0 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.5 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/glog v1.2.5 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/cel-go v0.20.1 // indirect
|
||||
github.com/google/cel-go v0.28.1 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.22.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -179,41 +185,36 @@ require (
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
|
||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.9.2 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/compress v1.19.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.5.1 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/acmez/v2 v2.0.3 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.6 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.2 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
@@ -239,45 +240,49 @@ require (
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.1 // indirect
|
||||
github.com/opencontainers/selinux v1.12.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.12.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.3 // indirect
|
||||
github.com/prometheus/common v0.71.0 // indirect
|
||||
github.com/prometheus/otlptranslator v1.0.0 // indirect
|
||||
github.com/prometheus/procfs v0.22.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.62.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
|
||||
github.com/redis/go-redis/v9 v9.7.3 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/slackhq/nebula v1.6.1 // indirect
|
||||
github.com/smallstep/certificates v0.26.1 // indirect
|
||||
github.com/smallstep/nosql v0.6.1 // indirect
|
||||
github.com/smallstep/pkcs7 v0.1.1 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
||||
github.com/slackhq/nebula v1.10.3 // indirect
|
||||
github.com/smallstep/certificates v0.30.2 // indirect
|
||||
github.com/smallstep/cli-utils v0.12.2 // indirect
|
||||
github.com/smallstep/linkedca v0.25.0 // indirect
|
||||
github.com/smallstep/nosql v0.8.0 // indirect
|
||||
github.com/smallstep/pkcs7 v0.2.1 // indirect
|
||||
github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 // indirect
|
||||
github.com/smallstep/truststore v0.13.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||
github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
|
||||
@@ -295,55 +300,53 @@ require (
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.68.0 // indirect
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.68.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.step.sm/cli-utils v0.9.0 // indirect
|
||||
go.step.sm/crypto v0.45.0 // indirect
|
||||
go.step.sm/linkedca v0.20.1 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.65.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.step.sm/crypto v0.81.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.2.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
|
||||
go.uber.org/zap v1.28.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541 // indirect
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
golang.org/x/time v0.16.0 // indirect
|
||||
golang.org/x/tools v0.50.0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
google.golang.org/api v0.277.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
k8s.io/api v0.32.3 // indirect
|
||||
k8s.io/apimachinery v0.32.3 // indirect
|
||||
@@ -351,9 +354,9 @@ require (
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
modernc.org/libc v1.61.13 // indirect
|
||||
modernc.org/libc v1.72.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.8.2 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
|
||||
+3
-3
@@ -8,9 +8,9 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli/config"
|
||||
"github.com/psviderski/uncloud/internal/machine"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/cluster"
|
||||
"github.com/psviderski/uncloud/internal/machine/network"
|
||||
"github.com/psviderski/uncloud/internal/sshexec"
|
||||
@@ -458,7 +458,7 @@ func (cli *CLI) AddMachine(ctx context.Context, opts AddMachineOptions) (_ *clie
|
||||
}
|
||||
|
||||
// Snapshot the cluster store version so the new machine can catch up before participating.
|
||||
var storeVersion map[string]int64
|
||||
var storeVersion map[string]uint64
|
||||
inspectResp, err = c.MachineClient.InspectMachine(ctx, &emptypb.Empty{})
|
||||
if err != nil {
|
||||
// TODO(lhf): remove Unimplemented check when v0.17.0 is released.
|
||||
@@ -603,7 +603,7 @@ func provisionOrConnectRemoteMachine(
|
||||
if remoteMachine.User != rootUser {
|
||||
// provisionMachine has just added the user to the uncloud group. Any SSH ControlMaster left over from
|
||||
// a previous uc invocation (e.g. a failed uc command against the uninitialised machine) still holds
|
||||
// the old user groups and would deny access to /run/uncloud/uncloud.sock. Close the current session
|
||||
// the old user groups and would deny access to /run/uncloud/api/uncloud.sock. Close the current session
|
||||
// if it exists so the next session picks up the new groups.
|
||||
conn.CloseControlMaster(ctx)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ import (
|
||||
)
|
||||
|
||||
func Contexts(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
// There are no contexts to complete when the CLI uses a direct machine connection (--connect) without a config.
|
||||
if uncli.Config == nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
contexts := slices.Sorted(maps.Keys(uncli.Config.Contexts))
|
||||
|
||||
names := []cobra.Completion{}
|
||||
@@ -21,7 +26,6 @@ func Contexts(ctx context.Context, uncli *cli.CLI, args []string, toComplete str
|
||||
if strings.HasPrefix(context, toComplete) {
|
||||
names = append(names, context)
|
||||
}
|
||||
names = append(names, context)
|
||||
}
|
||||
|
||||
return names, cobra.ShellCompDirectiveNoFileComp
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
)
|
||||
|
||||
func Machines(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
client, err := uncli.ConnectCluster(ctx)
|
||||
// Disable the connection progress output to not interfere with the shell completion output.
|
||||
client, err := uncli.ConnectClusterWithOptions(ctx, cli.ConnectOptions{})
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func Services(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
client, err := uncli.ConnectCluster(ctx)
|
||||
// Disable the connection progress output to not interfere with the shell completion output.
|
||||
client, err := uncli.ConnectClusterWithOptions(ctx, cli.ConnectOptions{})
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func Volumes(ctx context.Context, uncli *cli.CLI, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
client, err := uncli.ConnectCluster(ctx)
|
||||
// Disable the connection progress output to not interfere with the shell completion output.
|
||||
client, err := uncli.ConnectClusterWithOptions(ctx, cli.ConnectOptions{})
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/secret"
|
||||
)
|
||||
|
||||
type MachineConnection struct {
|
||||
@@ -23,10 +21,8 @@ type MachineConnection struct {
|
||||
// The pointer is used to omit the field when not set. Otherwise, yaml marshalling includes an empty object.
|
||||
TCP *netip.AddrPort `yaml:"tcp,omitempty"`
|
||||
// Unix is the path to the machine's API unix socket.
|
||||
Unix string `yaml:"unix,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
PublicKey secret.Secret `yaml:"public_key,omitempty"`
|
||||
MachineID string `yaml:"machine_id,omitempty"`
|
||||
Unix string `yaml:"unix,omitempty"`
|
||||
MachineID string `yaml:"machine_id,omitempty"`
|
||||
}
|
||||
|
||||
func (c *MachineConnection) String() string {
|
||||
|
||||
@@ -70,9 +70,9 @@ func TestMachineConnection_String(t *testing.T) {
|
||||
{
|
||||
name: "unix connection",
|
||||
conn: MachineConnection{
|
||||
Unix: "/run/uncloud/uncloud.sock",
|
||||
Unix: "/run/uncloud/api/uncloud.sock",
|
||||
},
|
||||
want: "unix:///run/uncloud/uncloud.sock",
|
||||
want: "unix:///run/uncloud/api/uncloud.sock",
|
||||
},
|
||||
{
|
||||
name: "no connection",
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/sshexec"
|
||||
"github.com/psviderski/uncloud/scripts"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
@@ -2,24 +2,50 @@ package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
systemd "github.com/coreos/go-systemd/daemon"
|
||||
"github.com/psviderski/uncloud/internal/machine"
|
||||
)
|
||||
|
||||
const systemdSocketUnit = "uncloud.socket"
|
||||
|
||||
type Daemon struct {
|
||||
machine *machine.Machine
|
||||
}
|
||||
|
||||
func New(dataDir string) (*Daemon, error) {
|
||||
listeners, err := activation.ListenersWithNames()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get systemd-activated sockets: %w", err)
|
||||
}
|
||||
|
||||
listener, err := selectActivatedListener(listeners)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if listener != nil {
|
||||
slog.Info("Using systemd-activated API socket.", "addr", listener.Addr().String())
|
||||
}
|
||||
|
||||
config := &machine.Config{
|
||||
DataDir: dataDir,
|
||||
DataDir: dataDir,
|
||||
ClusterAPISockPath: machine.DefaultClusterAPISockPath,
|
||||
ClusterAPIListener: listener,
|
||||
}
|
||||
mach, err := machine.NewMachine(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("init machine: %w", err)
|
||||
initErr := fmt.Errorf("init machine: %w", err)
|
||||
if listener != nil {
|
||||
if closeErr := listener.Close(); closeErr != nil {
|
||||
initErr = errors.Join(initErr, fmt.Errorf("close systemd-activated socket: %w", closeErr))
|
||||
}
|
||||
}
|
||||
return nil, initErr
|
||||
}
|
||||
|
||||
return &Daemon{
|
||||
@@ -27,6 +53,40 @@ func New(dataDir string) (*Daemon, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// selectActivatedListener returns the systemd activated listener. If systemd supplied multiple listeners,
|
||||
// it returns the one associated with systemdSocketUnit.
|
||||
func selectActivatedListener(listeners map[string][]net.Listener) (net.Listener, error) {
|
||||
var activated []net.Listener
|
||||
for _, named := range listeners {
|
||||
activated = append(activated, named...)
|
||||
}
|
||||
|
||||
if len(activated) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(activated) == 1 {
|
||||
return activated[0], nil
|
||||
}
|
||||
|
||||
named, ok := listeners[systemdSocketUnit]
|
||||
if !ok || len(named) != 1 {
|
||||
return nil, fmt.Errorf("expected one systemd-activated socket named '%s', received %d",
|
||||
systemdSocketUnit, len(named))
|
||||
}
|
||||
|
||||
// Close all other unused listeners.
|
||||
for name, unused := range listeners {
|
||||
if name == systemdSocketUnit {
|
||||
continue
|
||||
}
|
||||
for _, l := range unused {
|
||||
_ = l.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return named[0], nil
|
||||
}
|
||||
|
||||
func (d *Daemon) Run(ctx context.Context) error {
|
||||
slog.Info("Starting machine.")
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSelectActivatedListener(t *testing.T) {
|
||||
t.Run("no activated listeners", func(t *testing.T) {
|
||||
listener, err := selectActivatedListener(nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, listener)
|
||||
})
|
||||
|
||||
t.Run("only listener regardless of name", func(t *testing.T) {
|
||||
activated := testListener(t)
|
||||
|
||||
listener, err := selectActivatedListener(map[string][]net.Listener{"another.socket": {activated}})
|
||||
require.NoError(t, err)
|
||||
assert.Same(t, activated, listener)
|
||||
})
|
||||
|
||||
t.Run("named listener when multiple", func(t *testing.T) {
|
||||
activated := testListener(t)
|
||||
extra := testListener(t)
|
||||
|
||||
listener, err := selectActivatedListener(map[string][]net.Listener{
|
||||
systemdSocketUnit: {activated},
|
||||
"another.socket": {extra},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Same(t, activated, listener)
|
||||
assert.Error(t, extra.Close())
|
||||
})
|
||||
|
||||
t.Run("multiple listeners without expected name", func(t *testing.T) {
|
||||
first := testListener(t)
|
||||
second := testListener(t)
|
||||
|
||||
listener, err := selectActivatedListener(map[string][]net.Listener{
|
||||
"first.socket": {first},
|
||||
"second.socket": {second},
|
||||
})
|
||||
require.ErrorContains(t, err, "expected one systemd-activated socket")
|
||||
assert.Nil(t, listener)
|
||||
})
|
||||
|
||||
t.Run("multiple listeners under expected name", func(t *testing.T) {
|
||||
first := testListener(t)
|
||||
second := testListener(t)
|
||||
|
||||
listener, err := selectActivatedListener(map[string][]net.Listener{
|
||||
systemdSocketUnit: {first, second},
|
||||
})
|
||||
require.ErrorContains(t, err, "expected one systemd-activated socket")
|
||||
assert.Nil(t, listener)
|
||||
})
|
||||
}
|
||||
|
||||
func testListener(t *testing.T) net.Listener {
|
||||
t.Helper()
|
||||
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = listener.Close() })
|
||||
|
||||
return listener
|
||||
}
|
||||
@@ -31,9 +31,8 @@ const (
|
||||
//
|
||||
// The two minimums are independent: a client might require a newer daemon for new
|
||||
// features, while that same daemon could still handle requests from older clients.
|
||||
// TODO: update to 0.20.0 before releasing 0.20.0.
|
||||
MinClientVersion = "0.20.0-nightly"
|
||||
MinServerVersion = "0.20.0-nightly"
|
||||
MinClientVersion = "0.20.0"
|
||||
MinServerVersion = "0.20.0"
|
||||
|
||||
ReleaseURL = "https://github.com/psviderski/uncloud/releases/latest"
|
||||
)
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.34.2
|
||||
// protoc v5.27.3
|
||||
// source: internal/machine/api/pb/caddy.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type GetCaddyConfigResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The generated Caddyfile content.
|
||||
Caddyfile string `protobuf:"bytes,1,opt,name=caddyfile,proto3" json:"caddyfile,omitempty"`
|
||||
// Timestamp when the config was last modified.
|
||||
ModifiedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=modified_at,json=modifiedAt,proto3" json:"modified_at,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) Reset() {
|
||||
*x = GetCaddyConfigResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_caddy_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetCaddyConfigResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetCaddyConfigResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_caddy_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetCaddyConfigResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetCaddyConfigResponse) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_caddy_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) GetCaddyfile() string {
|
||||
if x != nil {
|
||||
return x.Caddyfile
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetCaddyConfigResponse) GetModifiedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.ModifiedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_internal_machine_api_pb_caddy_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_internal_machine_api_pb_caddy_proto_rawDesc = []byte{
|
||||
0x0a, 0x23, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x61, 0x64, 0x64, 0x79, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74,
|
||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x73, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43,
|
||||
0x61, 0x64, 0x64, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x61, 0x64, 0x64, 0x79, 0x66, 0x69, 0x6c, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x64, 0x64, 0x79, 0x66, 0x69, 0x6c, 0x65,
|
||||
0x12, 0x3b, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||
0x70, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x32, 0x49, 0x0a,
|
||||
0x05, 0x43, 0x61, 0x64, 0x64, 0x79, 0x12, 0x40, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x61, 0x70,
|
||||
0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x61, 0x64, 0x64, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b,
|
||||
0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70,
|
||||
0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_internal_machine_api_pb_caddy_proto_rawDescOnce sync.Once
|
||||
file_internal_machine_api_pb_caddy_proto_rawDescData = file_internal_machine_api_pb_caddy_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_internal_machine_api_pb_caddy_proto_rawDescGZIP() []byte {
|
||||
file_internal_machine_api_pb_caddy_proto_rawDescOnce.Do(func() {
|
||||
file_internal_machine_api_pb_caddy_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_machine_api_pb_caddy_proto_rawDescData)
|
||||
})
|
||||
return file_internal_machine_api_pb_caddy_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_internal_machine_api_pb_caddy_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_internal_machine_api_pb_caddy_proto_goTypes = []any{
|
||||
(*GetCaddyConfigResponse)(nil), // 0: api.GetCaddyConfigResponse
|
||||
(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
|
||||
(*emptypb.Empty)(nil), // 2: google.protobuf.Empty
|
||||
}
|
||||
var file_internal_machine_api_pb_caddy_proto_depIdxs = []int32{
|
||||
1, // 0: api.GetCaddyConfigResponse.modified_at:type_name -> google.protobuf.Timestamp
|
||||
2, // 1: api.Caddy.GetConfig:input_type -> google.protobuf.Empty
|
||||
0, // 2: api.Caddy.GetConfig:output_type -> api.GetCaddyConfigResponse
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_internal_machine_api_pb_caddy_proto_init() }
|
||||
func file_internal_machine_api_pb_caddy_proto_init() {
|
||||
if File_internal_machine_api_pb_caddy_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_internal_machine_api_pb_caddy_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*GetCaddyConfigResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_machine_api_pb_caddy_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_internal_machine_api_pb_caddy_proto_goTypes,
|
||||
DependencyIndexes: file_internal_machine_api_pb_caddy_proto_depIdxs,
|
||||
MessageInfos: file_internal_machine_api_pb_caddy_proto_msgTypes,
|
||||
}.Build()
|
||||
File_internal_machine_api_pb_caddy_proto = out.File
|
||||
file_internal_machine_api_pb_caddy_proto_rawDesc = nil
|
||||
file_internal_machine_api_pb_caddy_proto_goTypes = nil
|
||||
file_internal_machine_api_pb_caddy_proto_depIdxs = nil
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package proxy
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/siderolabs/grpc-proxy/proxy"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/encoding/protowire"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
// MachineTarget represents a resolved machine target.
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
// Server implements the gRPC Caddy service.
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
// Package caddystorage implements the machine-local Caddy storage API.
|
||||
// See api/pb/caddy_storage.proto for the gRPC service definition.
|
||||
package caddystorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/store"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Namespace identifies Caddy storage records in the cluster key-value store.
|
||||
const Namespace = "caddy_storage"
|
||||
|
||||
// Server implements the machine-local CaddyStorage gRPC service.
|
||||
type Server struct {
|
||||
pb.UnimplementedCaddyStorageServer
|
||||
store *store.Keyspace
|
||||
}
|
||||
|
||||
func NewServer(store *store.Keyspace) *Server {
|
||||
return &Server{store: store}
|
||||
}
|
||||
|
||||
func (s *Server) Store(ctx context.Context, req *pb.StoreCaddyStorageRequest) (*emptypb.Empty, error) {
|
||||
if err := validateKey(req.Key); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
if err := s.store.Put(ctx, req.Key, req.Value); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "put value: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Load(ctx context.Context, req *pb.LoadCaddyStorageRequest) (*pb.LoadCaddyStorageResponse, error) {
|
||||
if err := validateKey(req.Key); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
record, err := s.store.Get(ctx, req.Key)
|
||||
if errors.Is(err, store.ErrKeyNotFound) {
|
||||
return nil, status.Errorf(codes.NotFound, "Caddy storage key %q not found", req.Key)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "get value: %v", err)
|
||||
}
|
||||
|
||||
return &pb.LoadCaddyStorageResponse{
|
||||
Value: record.Value,
|
||||
UpdatedAt: timestamppb.New(record.UpdatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Delete(ctx context.Context, req *pb.DeleteCaddyStorageRequest) (*emptypb.Empty, error) {
|
||||
if err := validateKey(req.Key); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
// Delete the key and all keys prefixed by it in case it's a directory.
|
||||
if err := errors.Join(
|
||||
s.store.Delete(ctx, req.Key, store.KeyspaceDeleteOptions{}),
|
||||
s.store.Delete(ctx, req.Key+"/", store.KeyspaceDeleteOptions{Prefix: true}),
|
||||
); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "delete key: %v", err)
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) List(ctx context.Context, req *pb.ListCaddyStorageRequest) (*pb.ListCaddyStorageResponse, error) {
|
||||
if req.Prefix != "" {
|
||||
if err := validateKey(req.Prefix); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
storagePrefix := req.Prefix
|
||||
if storagePrefix != "" {
|
||||
storagePrefix += "/"
|
||||
}
|
||||
records, err := s.store.List(ctx, storagePrefix, store.KeyspaceListOptions{KeysOnly: true})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list keys: %v", err)
|
||||
}
|
||||
// No descendants can mean an existing terminal key or a missing path.
|
||||
// Check the exact key for non-root prefixes. An empty prefix lists the storage root.
|
||||
if len(records) == 0 && req.Prefix != "" {
|
||||
if _, err = s.store.Get(ctx, req.Prefix); errors.Is(err, store.ErrKeyNotFound) {
|
||||
return nil, status.Errorf(codes.NotFound, "Caddy storage prefix %q not found", req.Prefix)
|
||||
} else if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "get prefix value: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.ListCaddyStorageResponse{
|
||||
Keys: listKeys(records, req.Prefix, req.Recursive),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func listKeys(records []store.Record, prefix string, recursive bool) []string {
|
||||
keys := make(map[string]struct{})
|
||||
for _, record := range records {
|
||||
relative := record.Key
|
||||
if prefix != "" {
|
||||
relative = strings.TrimPrefix(record.Key, prefix+"/")
|
||||
}
|
||||
components := strings.Split(relative, "/")
|
||||
if !recursive {
|
||||
keys[path.Join(prefix, components[0])] = struct{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range components {
|
||||
keys[path.Join(prefix, path.Join(components[:i+1]...))] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return slices.Sorted(maps.Keys(keys))
|
||||
}
|
||||
|
||||
func (s *Server) Stat(ctx context.Context, req *pb.StatCaddyStorageRequest) (*pb.StatCaddyStorageResponse, error) {
|
||||
if err := validateKey(req.Key); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
record, err := s.store.Get(ctx, req.Key)
|
||||
if err == nil {
|
||||
return &pb.StatCaddyStorageResponse{
|
||||
Key: req.Key,
|
||||
UpdatedAt: timestamppb.New(record.UpdatedAt),
|
||||
Size: int64(len(record.Value)),
|
||||
IsTerminal: true,
|
||||
}, nil
|
||||
}
|
||||
if !errors.Is(err, store.ErrKeyNotFound) {
|
||||
return nil, status.Errorf(codes.Internal, "get value: %v", err)
|
||||
}
|
||||
|
||||
// A terminal key is not found. Check if there are any keys prefixed by the key to determine if it's a directory.
|
||||
records, err := s.store.List(ctx, req.Key+"/", store.KeyspaceListOptions{KeysOnly: true})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list keys: %v", err)
|
||||
}
|
||||
if len(records) == 0 {
|
||||
return nil, status.Errorf(codes.NotFound, "Caddy storage key %q not found", req.Key)
|
||||
}
|
||||
|
||||
return &pb.StatCaddyStorageResponse{
|
||||
Key: req.Key,
|
||||
IsTerminal: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateKey(key string) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("key is empty")
|
||||
}
|
||||
if strings.HasPrefix(key, "/") || strings.HasSuffix(key, "/") {
|
||||
return fmt.Errorf("key %q must not have a leading or trailing slash", key)
|
||||
}
|
||||
if strings.Contains(key, "\\") {
|
||||
return fmt.Errorf("key %q must use forward slashes", key)
|
||||
}
|
||||
for component := range strings.SplitSeq(key, "/") {
|
||||
if component == "" || component == "." || component == ".." {
|
||||
return fmt.Errorf("key %q contains an invalid path component", key)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package caddystorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/store"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestListKeys(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
records []store.Record
|
||||
prefix string
|
||||
recursive bool
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "empty root",
|
||||
recursive: true,
|
||||
},
|
||||
{
|
||||
name: "root immediate children",
|
||||
records: []store.Record{
|
||||
{Key: "ocsp/example.com"},
|
||||
{Key: "certificates/issuer/example.com/example.com.key"},
|
||||
{Key: "certificates/issuer/example.com/example.com.crt"},
|
||||
},
|
||||
want: []string{
|
||||
"certificates",
|
||||
"ocsp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "root recursive",
|
||||
records: []store.Record{
|
||||
{Key: "ocsp/example.com"},
|
||||
{Key: "certificates/issuer/example.com/example.com.key"},
|
||||
{Key: "certificates/issuer/example.com/example.com.crt"},
|
||||
},
|
||||
recursive: true,
|
||||
want: []string{
|
||||
"certificates",
|
||||
"certificates/issuer",
|
||||
"certificates/issuer/example.com",
|
||||
"certificates/issuer/example.com/example.com.crt",
|
||||
"certificates/issuer/example.com/example.com.key",
|
||||
"ocsp",
|
||||
"ocsp/example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested immediate children",
|
||||
records: []store.Record{
|
||||
{Key: "certificates/issuer/example.net/example.net.crt"},
|
||||
{Key: "certificates/issuer/example.com/example.com.key"},
|
||||
{Key: "certificates/issuer/example.com/example.com.crt"},
|
||||
},
|
||||
prefix: "certificates/issuer",
|
||||
want: []string{
|
||||
"certificates/issuer/example.com",
|
||||
"certificates/issuer/example.net",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested terminal children",
|
||||
records: []store.Record{
|
||||
{Key: "ocsp/example.net"},
|
||||
{Key: "ocsp/example.com"},
|
||||
},
|
||||
prefix: "ocsp",
|
||||
want: []string{
|
||||
"ocsp/example.com",
|
||||
"ocsp/example.net",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested recursive",
|
||||
records: []store.Record{
|
||||
{Key: "certificates/issuer/example.net/example.net.crt"},
|
||||
{Key: "certificates/issuer/example.com/example.com.key"},
|
||||
{Key: "certificates/issuer/example.com/example.com.crt"},
|
||||
},
|
||||
prefix: "certificates/issuer",
|
||||
recursive: true,
|
||||
want: []string{
|
||||
"certificates/issuer/example.com",
|
||||
"certificates/issuer/example.com/example.com.crt",
|
||||
"certificates/issuer/example.com/example.com.key",
|
||||
"certificates/issuer/example.net",
|
||||
"certificates/issuer/example.net/example.net.crt",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := listKeys(tt.records, tt.prefix, tt.recursive)
|
||||
if len(tt.want) == 0 {
|
||||
require.Empty(t, got)
|
||||
return
|
||||
}
|
||||
require.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
+51
-103
@@ -12,7 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/caddyconfig"
|
||||
"github.com/psviderski/uncloud/internal/machine/constants"
|
||||
"github.com/psviderski/uncloud/internal/machine/corromigrate"
|
||||
@@ -176,7 +176,7 @@ func (cc *clusterController) Run(ctx context.Context) error {
|
||||
// Start the network API server before waiting for the store sync so the machine is reachable on the mesh
|
||||
// during the sync and can serve requests that don't depend on the store.
|
||||
// Assume the management IP can't be changed when the network is running.
|
||||
apiAddr := net.JoinHostPort(cc.state.Network.ManagementIP.String(), strconv.Itoa(constants.MachineAPIPort))
|
||||
apiAddr := net.JoinHostPort(cc.state.Network.ManagementIP.String(), strconv.Itoa(constants.UncloudAPIPort))
|
||||
listener, err := net.Listen("tcp", apiAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listen API port: %w", err)
|
||||
@@ -189,9 +189,10 @@ func (cc *clusterController) Run(ctx context.Context) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
// Wait for the store database to sync to the minimum version before starting store-dependent components.
|
||||
// This prevents issues with using partially replicated data when the machine just joined the cluster,
|
||||
// e.g., an empty machine list causing WireGuard peer misconfiguration.
|
||||
// Wait for initial replication before starting store-dependent components, such as WireGuard peer reconciliation.
|
||||
// This prevents issues with using partially replicated data when the machine just joined the cluster, e.g.,
|
||||
// an empty machine list causing WireGuard peer misconfiguration.
|
||||
// This uses Store.WaitForVersion's replication-progress semantics, not an exact snapshot of the join-time data.
|
||||
if err = cc.waitStoreSync(ctx); err != nil {
|
||||
return fmt.Errorf("wait initial cluster store sync: %w", err)
|
||||
}
|
||||
@@ -366,120 +367,67 @@ func (cc *clusterController) handleEndpointChanges(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// waitStoreSync blocks until the local store version >= state.MinStoreVersion and any known gaps are synced.
|
||||
// No-op when MinStoreVersion is empty. Clears state.MinStoreVersion when reached.
|
||||
// waitStoreSync waits for initial replication using [store.Store.WaitForVersion].
|
||||
// No-op when MinStoreVersion is empty. Retries until cancellation and clears state.MinStoreVersion after success.
|
||||
func (cc *clusterController) waitStoreSync(ctx context.Context) error {
|
||||
target := cc.state.MinStoreVersion
|
||||
if len(target) == 0 {
|
||||
minVersion := cc.state.MinStoreVersion
|
||||
if len(minVersion) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
slog.Info("Waiting for the initial cluster store sync.", "actors", len(target))
|
||||
slog.Info("Waiting for the initial cluster store sync.", "min_version", minVersion)
|
||||
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
// Periodic warning to surface stuck NAT/connectivity issues without aborting.
|
||||
// Periodic deadlines surface stuck NAT/connectivity issues without aborting startup.
|
||||
warnInterval := 5 * time.Minute
|
||||
warnTimer := time.NewTimer(warnInterval)
|
||||
defer warnTimer.Stop()
|
||||
|
||||
var (
|
||||
lastLagging int
|
||||
lastErrLogTime time.Time
|
||||
)
|
||||
nextWarning := time.Now().Add(warnInterval)
|
||||
var lastErrLogTime time.Time
|
||||
|
||||
for {
|
||||
waitCtx, cancel := context.WithDeadline(ctx, nextWarning)
|
||||
err := cc.store.WaitForVersion(waitCtx, minVersion)
|
||||
cancel()
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if errors.Is(err, store.ErrInvalidStoreVersion) {
|
||||
return fmt.Errorf("wait for minimum cluster store version: %w", err)
|
||||
}
|
||||
|
||||
if !time.Now().Before(nextWarning) {
|
||||
slog.Warn("Cluster store sync still pending. Check connectivity to peers.")
|
||||
nextWarning = time.Now().Add(warnInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
// Retry store errors, throttling logs to once every 5 seconds.
|
||||
if time.Since(lastErrLogTime) >= 5*time.Second {
|
||||
slog.Error("Failed to check cluster store replication, retrying.", "err", err)
|
||||
lastErrLogTime = time.Now()
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-warnTimer.C:
|
||||
local, err := cc.store.Version(ctx)
|
||||
if err == nil {
|
||||
slog.Error("Cluster store sync still pending. Check connectivity to peers.",
|
||||
"lagging_actors", laggingActors(local, target))
|
||||
} else {
|
||||
slog.Error("Cluster store sync still pending. Check connectivity to peers.", "err", err)
|
||||
}
|
||||
warnTimer.Reset(warnInterval)
|
||||
case <-ticker.C:
|
||||
local, err := cc.store.Version(ctx)
|
||||
if err != nil {
|
||||
// Throttle error logs to once every 5 seconds.
|
||||
if time.Since(lastErrLogTime) >= 5*time.Second {
|
||||
slog.Error("Failed to get the cluster store version, retrying.", "err", err)
|
||||
lastErrLogTime = time.Now()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
lagging := laggingActors(local, target)
|
||||
if len(lagging) == 0 {
|
||||
// Per-actor max doesn't imply contiguous apply: corrosion can buffer X:N before
|
||||
// X:N-1 arrives and track the gap separately. Wait for any remaining gaps to be synced.
|
||||
if err := cc.waitKnownMissingChanges(ctx); err != nil {
|
||||
return fmt.Errorf("wait for known missing changes: %w", err)
|
||||
}
|
||||
// If the context was cancelled mid-gap-fill, don't persist a "synced" state.
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear MinStoreVersion so next restart doesn't wait for sync.
|
||||
cc.state.mu.Lock()
|
||||
cc.state.MinStoreVersion = nil
|
||||
err = cc.state.Save()
|
||||
cc.state.mu.Unlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("save machine state after the initial cluster store sync: %w", err)
|
||||
}
|
||||
|
||||
slog.Info("Cluster store completed the initial sync.", "actors", len(target))
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(lagging) != lastLagging {
|
||||
slog.Info("Syncing cluster store.", "lagging_actors", lagging)
|
||||
lastLagging = len(lagging)
|
||||
}
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// laggingActors returns target actors whose local version is below the required value, as [have, need].
|
||||
func laggingActors(local, target map[string]int64) map[string][2]int64 {
|
||||
lagging := make(map[string][2]int64)
|
||||
for actor, need := range target {
|
||||
if have := local[actor]; have < need {
|
||||
lagging[actor] = [2]int64{have, need}
|
||||
}
|
||||
// Clear MinStoreVersion so the next restart doesn't wait for sync.
|
||||
cc.state.mu.Lock()
|
||||
cc.state.MinStoreVersion = nil
|
||||
err := cc.state.Save()
|
||||
if err != nil {
|
||||
cc.state.MinStoreVersion = minVersion
|
||||
}
|
||||
return lagging
|
||||
}
|
||||
|
||||
// waitKnownMissingChanges polls the store until all known missing changes have been synced.
|
||||
func (cc *clusterController) waitKnownMissingChanges(ctx context.Context) error {
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
changes, err := cc.store.KnownMissingChanges(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("query known missing changes from cluster store: %w", err)
|
||||
}
|
||||
|
||||
if len(changes) == 0 {
|
||||
slog.Debug("All known missing changes have been synced to the cluster store.")
|
||||
return nil
|
||||
}
|
||||
|
||||
slog.Debug("Waiting for known missing changes to be synced to the cluster store.", "remaining",
|
||||
len(changes))
|
||||
}
|
||||
cc.state.mu.Unlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("save machine state after the initial cluster store sync: %w", err)
|
||||
}
|
||||
|
||||
slog.Info("Cluster store completed the initial sync.", "min_version", minVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// runMachineSync keeps this machine's info in the cluster store in sync with the local state and the Docker engine
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/corrosion"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/network"
|
||||
"github.com/psviderski/uncloud/internal/machine/store"
|
||||
"github.com/psviderski/uncloud/internal/secret"
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/dns"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/store"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
// MachineAPIPort is the port for the Machine API service on the management WireGuard network.
|
||||
MachineAPIPort = 51000
|
||||
// UnregistryPort is the port for the embedded container registry listening on the machine IP.
|
||||
// UncloudAPIPort is the TCP port on which each machine serves the Uncloud API over the management WireGuard
|
||||
// network.
|
||||
UncloudAPIPort = 51000
|
||||
// UnregistryPort is the TCP port for the embedded container registry listening on the machine IP.
|
||||
UnregistryPort = 51500
|
||||
)
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/fs"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/store"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
_ "modernc.org/sqlite"
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
systemd "github.com/coreos/go-systemd/daemon"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
@@ -22,6 +23,13 @@ const (
|
||||
Image = "ghcr.io/unlabs-dev/corrosion:2026.6.15"
|
||||
// ContainerName is the name of the managed Corrosion container.
|
||||
ContainerName = "uncloud-corrosion"
|
||||
|
||||
// systemdStartTimeoutExtension is the duration of each systemd service start timeout extension that prevents
|
||||
// a slow image pull from exceeding the start timeout (TimeoutStartSec). Extensions are a no-op if Corrosion
|
||||
// is not starting during a systemd service startup.
|
||||
systemdStartTimeoutExtension = 30 * time.Second
|
||||
// systemdStartTimeoutExtendMax caps the total duration the start timeout can be extended for.
|
||||
systemdStartTimeoutExtendMax = 5 * time.Minute
|
||||
)
|
||||
|
||||
type DockerService struct {
|
||||
@@ -175,8 +183,14 @@ func (s *DockerService) createAndStart(ctx context.Context) error {
|
||||
}
|
||||
defer respBody.Close()
|
||||
|
||||
// The pull on the first daemon start may take longer than the systemd service start timeout (TimeoutStartSec)
|
||||
// on a slow connection. Keep extending the timeout while the pull is in progress so systemd doesn't kill
|
||||
// the daemon before it reports readiness. This is a no-op if not running under systemd.
|
||||
stopExtending := extendSystemdStartTimeout(ctx)
|
||||
// Wait for pull to complete.
|
||||
if _, err := io.Copy(io.Discard, respBody); err != nil {
|
||||
_, err = io.Copy(io.Discard, respBody)
|
||||
stopExtending()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read pull response: %w", err)
|
||||
}
|
||||
slog.Info("Docker image pulled.", "image", s.Image, "duration", time.Since(start).String())
|
||||
@@ -192,3 +206,40 @@ func (s *DockerService) createAndStart(ctx context.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extendSystemdStartTimeout periodically asks systemd to extend the service start timeout, for up to
|
||||
// systemdStartTimeoutExtendMax. The returned function stops the extensions. It is a no-op when the service
|
||||
// is not running under systemd (NOTIFY_SOCKET is not set).
|
||||
func extendSystemdStartTimeout(ctx context.Context) (stop context.CancelFunc) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
go func() {
|
||||
// Send extensions more frequently than they expire so a single missed tick doesn't time out the start.
|
||||
ticker := time.NewTicker(systemdStartTimeoutExtension / 3)
|
||||
defer ticker.Stop()
|
||||
deadline := time.Now().Add(systemdStartTimeoutExtendMax)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if time.Now().After(deadline) {
|
||||
slog.Warn("Stopped extending the systemd service start timeout: corrosion service is taking "+
|
||||
"too long to start.", "timeout", systemdStartTimeoutExtendMax)
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf("EXTEND_TIMEOUT_USEC=%d", systemdStartTimeoutExtension.Microseconds())
|
||||
if _, err := systemd.SdNotify(false, msg); err != nil {
|
||||
slog.Warn("Failed to extend the systemd service start timeout when starting corrosion service.",
|
||||
"err", err)
|
||||
return
|
||||
}
|
||||
slog.Info("Extended systemd service start timeout while corrosion service is starting.",
|
||||
"duration", systemdStartTimeoutExtension)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return cancel
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
regtypes "github.com/google/go-containerregistry/pkg/v1/types"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/docker"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"os/signal"
|
||||
|
||||
"github.com/moby/term"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
@@ -40,7 +40,7 @@ import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/dns"
|
||||
"github.com/psviderski/uncloud/internal/secret"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
@@ -562,7 +562,9 @@ func (s *Server) CreateServiceContainer(
|
||||
api.LabelServiceMode: spec.Mode,
|
||||
api.LabelManaged: "",
|
||||
},
|
||||
User: spec.Container.User,
|
||||
User: spec.Container.User,
|
||||
Tty: spec.Container.Tty,
|
||||
OpenStdin: spec.Container.OpenStdin,
|
||||
}
|
||||
if spec.Mode == "" {
|
||||
config.Labels[api.LabelServiceMode] = api.ServiceModeReplicated
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -233,11 +235,17 @@ func (s *Service) ListImages(ctx context.Context, opts image.ListOptions) (Image
|
||||
return imagesResp, nil
|
||||
}
|
||||
|
||||
// ContainerLogs streams logs from a container and returns demultiplexed entries via a channel.
|
||||
// ContainerLogs streams logs from a container and returns entries via a channel.
|
||||
// The channel is closed when streaming completes or context is cancelled.
|
||||
func (s *Service) ContainerLogs(
|
||||
ctx context.Context, containerID string, opts api.ServiceLogsOptions,
|
||||
) (<-chan api.LogEntry, error) {
|
||||
ctr, err := s.Client.ContainerInspect(ctx, containerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inspect container '%s': %w", containerID, err)
|
||||
}
|
||||
isTTY := ctr.Config != nil && ctr.Config.Tty
|
||||
|
||||
dockerOpts := container.LogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
@@ -257,25 +265,31 @@ func (s *Service) ContainerLogs(
|
||||
stdoutWriter := &logsChannelWriter{ctx: ctx, ch: outCh, isStderr: false}
|
||||
stderrWriter := &logsChannelWriter{ctx: ctx, ch: outCh, isStderr: true}
|
||||
|
||||
// Wrap the context in a cancellable one to unblock the second goroutine below when StdCopy completes.
|
||||
// Wrap the context in a cancellable one to unblock the second goroutine when log copying completes.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
// Run StdCopy in a goroutine to be able to handle context cancellation.
|
||||
// Copy logs in a goroutine to be able to handle context cancellation.
|
||||
go func() {
|
||||
defer close(outCh)
|
||||
defer cancel()
|
||||
|
||||
// StdCopy is blocking and will return when the reader is closed in another goroutine below or on error.
|
||||
if _, err := stdcopy.StdCopy(stdoutWriter, stderrWriter, reader); err != nil {
|
||||
// Docker returns raw stdout for TTY containers and multiplexed stdout/stderr otherwise.
|
||||
var err error
|
||||
if isTTY {
|
||||
_, err = copyRawContainerLogs(stdoutWriter, reader)
|
||||
} else {
|
||||
_, err = stdcopy.StdCopy(stdoutWriter, stderrWriter, reader)
|
||||
}
|
||||
if err != nil {
|
||||
// Send error as the last entry.
|
||||
select {
|
||||
case outCh <- api.LogEntry{Err: fmt.Errorf("demultiplex container logs: %w", err)}:
|
||||
case outCh <- api.LogEntry{Err: fmt.Errorf("copy container logs: %w", err)}:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Close the reader when the context is done to cancel StdCopy if it's still running.
|
||||
// Close the reader when the context is done to cancel log copying if it's still running.
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
reader.Close()
|
||||
@@ -284,7 +298,32 @@ func (s *Service) ContainerLogs(
|
||||
return outCh, nil
|
||||
}
|
||||
|
||||
// logsChannelWriter is a writer for stdcopy.StdCopy that sends demultiplexed container logs to a channel.
|
||||
// copyRawContainerLogs copies a raw TTY log stream one line at a time so each write produces one log entry.
|
||||
func copyRawContainerLogs(dst io.Writer, src io.Reader) (written int64, _ error) {
|
||||
reader := bufio.NewReader(src)
|
||||
for {
|
||||
line, readErr := reader.ReadBytes('\n')
|
||||
if len(line) > 0 {
|
||||
n, writeErr := dst.Write(line)
|
||||
written += int64(n)
|
||||
if writeErr != nil {
|
||||
return written, writeErr
|
||||
}
|
||||
if n != len(line) {
|
||||
return written, io.ErrShortWrite
|
||||
}
|
||||
}
|
||||
|
||||
if readErr != nil {
|
||||
if errors.Is(readErr, io.EOF) {
|
||||
return written, nil
|
||||
}
|
||||
return written, readErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logsChannelWriter sends container log writes to a channel.
|
||||
type logsChannelWriter struct {
|
||||
ctx context.Context
|
||||
ch chan<- api.LogEntry
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
dockerclient "github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestServiceContainerLogs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
containerID = "container-id"
|
||||
firstLog = "2025-01-01T00:00:00.000000000Z first message\n"
|
||||
secondLog = "2025-01-01T00:00:01.000000000Z second message\n"
|
||||
)
|
||||
|
||||
var multiplexedLogs bytes.Buffer
|
||||
_, err := stdcopy.NewStdWriter(&multiplexedLogs, stdcopy.Stdout).Write([]byte(firstLog))
|
||||
require.NoError(t, err)
|
||||
_, err = stdcopy.NewStdWriter(&multiplexedLogs, stdcopy.Stderr).Write([]byte(secondLog))
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tty bool
|
||||
logs []byte
|
||||
streams []api.LogStreamType
|
||||
messages []string
|
||||
}{
|
||||
{
|
||||
name: "TTY raw stream",
|
||||
tty: true,
|
||||
logs: []byte(firstLog + secondLog),
|
||||
streams: []api.LogStreamType{api.LogStreamStdout, api.LogStreamStdout},
|
||||
messages: []string{"first message\n", "second message\n"},
|
||||
},
|
||||
{
|
||||
name: "non-TTY multiplexed stream",
|
||||
logs: multiplexedLogs.Bytes(),
|
||||
streams: []api.LogStreamType{api.LogStreamStdout, api.LogStreamStderr},
|
||||
messages: []string{"first message\n", "second message\n"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dockerClient := newLogsTestClient(t, tt.tty, tt.logs)
|
||||
service := NewService(dockerClient, nil)
|
||||
|
||||
logsCh, err := service.ContainerLogs(context.Background(), containerID, api.ServiceLogsOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
var entries []api.LogEntry
|
||||
for entry := range logsCh {
|
||||
require.NoError(t, entry.Err)
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
require.Len(t, entries, len(tt.messages))
|
||||
for i := range entries {
|
||||
assert.Equal(t, tt.streams[i], entries[i].Stream)
|
||||
assert.Equal(t, tt.messages[i], string(entries[i].Message))
|
||||
assert.False(t, entries[i].Timestamp.IsZero())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newLogsTestClient(t *testing.T, tty bool, logs []byte) *dockerclient.Client {
|
||||
t.Helper()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case strings.HasSuffix(r.URL.Path, "/containers/container-id/json"):
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{ID: "container-id"},
|
||||
Config: &container.Config{Tty: tty},
|
||||
}); err != nil {
|
||||
t.Errorf("encode inspect response: %v", err)
|
||||
}
|
||||
case strings.HasSuffix(r.URL.Path, "/containers/container-id/logs"):
|
||||
w.Header().Set("Content-Type", "application/vnd.docker.raw-stream")
|
||||
if _, err := w.Write(logs); err != nil {
|
||||
t.Errorf("write logs response: %v", err)
|
||||
}
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
serverURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
dockerClient, err := dockerclient.NewClientWithOpts(
|
||||
dockerclient.WithHost("tcp://"+serverURL.Host),
|
||||
dockerclient.WithHTTPClient(server.Client()),
|
||||
dockerclient.WithVersion("1.48"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, dockerClient.Close())
|
||||
})
|
||||
|
||||
return dockerClient
|
||||
}
|
||||
@@ -48,12 +48,12 @@ func ConfigureIptablesChains(machineIP netip.Addr, wgPort int) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Allow cluster machines to access Machine API via the management IPv6 WireGuard network.
|
||||
// Allow cluster machines to access the Uncloud API via the management IPv6 WireGuard network.
|
||||
acceptMachineAPIRule := []string{
|
||||
"-i", network.WireGuardInterfaceName,
|
||||
"-s", "fdcc::/16",
|
||||
"-p", "tcp",
|
||||
"--dport", strconv.Itoa(constants.MachineAPIPort),
|
||||
"--dport", strconv.Itoa(constants.UncloudAPIPort),
|
||||
"-j", "ACCEPT",
|
||||
}
|
||||
// Allow Corrosion gossip traffic from cluster machines via the management IPv6 WireGuard network.
|
||||
|
||||
+143
-60
@@ -21,14 +21,15 @@ import (
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/corrosion"
|
||||
"github.com/psviderski/uncloud/internal/docker"
|
||||
"github.com/psviderski/uncloud/internal/fs"
|
||||
"github.com/psviderski/uncloud/internal/grpcversion"
|
||||
"github.com/psviderski/uncloud/internal/journal"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
apiproxy "github.com/psviderski/uncloud/internal/machine/api/proxy"
|
||||
"github.com/psviderski/uncloud/internal/machine/caddyconfig"
|
||||
"github.com/psviderski/uncloud/internal/machine/caddystorage"
|
||||
"github.com/psviderski/uncloud/internal/machine/cluster"
|
||||
"github.com/psviderski/uncloud/internal/machine/constants"
|
||||
"github.com/psviderski/uncloud/internal/machine/corromigrate"
|
||||
@@ -42,6 +43,8 @@ import (
|
||||
"github.com/psviderski/uncloud/internal/secret"
|
||||
"github.com/psviderski/uncloud/internal/version"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/psviderski/uncloud/pkg/distlock"
|
||||
distlockgrpc "github.com/psviderski/uncloud/pkg/distlock/grpc"
|
||||
"github.com/psviderski/unregistry"
|
||||
"github.com/siderolabs/grpc-proxy/proxy"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -55,9 +58,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultMachineSockPath = "/run/uncloud/machine.sock"
|
||||
DefaultUncloudSockPath = "/run/uncloud/uncloud.sock"
|
||||
DefaultSockGroup = "uncloud"
|
||||
// DefaultMachineAPISockPath is the default path to the Unix socket for API requests handled directly by this
|
||||
// machine.
|
||||
DefaultMachineAPISockPath = "/run/uncloud/machine.sock"
|
||||
// DefaultClusterAPISockPath is the default path to the Unix socket for the client-facing API that routes requests
|
||||
// across the cluster.
|
||||
DefaultClusterAPISockPath = "/run/uncloud/api/uncloud.sock"
|
||||
DefaultSockGroup = "uncloud"
|
||||
// DefaultCaddyAdminSockPath is the default path to the Caddy admin socket for validating the generated Caddy
|
||||
// reverse proxy configuration.
|
||||
DefaultCaddyAdminSockPath = "/run/uncloud/caddy/admin.sock"
|
||||
@@ -67,9 +74,15 @@ const (
|
||||
|
||||
type Config struct {
|
||||
// DataDir is the directory where the machine stores its persistent state. Default is /var/lib/uncloud.
|
||||
DataDir string
|
||||
MachineSockPath string
|
||||
UncloudSockPath string
|
||||
DataDir string
|
||||
// MachineAPISockPath is the path to the Unix socket for API requests handled directly by this machine.
|
||||
MachineAPISockPath string
|
||||
// ClusterAPISockPath is the path to the Unix socket for the client-facing API that routes requests across the
|
||||
// cluster.
|
||||
ClusterAPISockPath string
|
||||
// ClusterAPIListener is an optional pre-bound listener for the client-facing API that routes requests across the
|
||||
// cluster. If set, Run takes ownership of it and closes it on shutdown or startup failure.
|
||||
ClusterAPIListener net.Listener
|
||||
|
||||
CorrosionDataDir string
|
||||
// CorrosionRunDir is the runtime directory for the corrosion service.
|
||||
@@ -101,11 +114,11 @@ func (c *Config) SetDefaults() (*Config, error) {
|
||||
if cfg.DataDir == "" {
|
||||
cfg.DataDir = "/var/lib/uncloud"
|
||||
}
|
||||
if cfg.MachineSockPath == "" {
|
||||
cfg.MachineSockPath = DefaultMachineSockPath
|
||||
if cfg.MachineAPISockPath == "" {
|
||||
cfg.MachineAPISockPath = DefaultMachineAPISockPath
|
||||
}
|
||||
if cfg.UncloudSockPath == "" {
|
||||
cfg.UncloudSockPath = DefaultUncloudSockPath
|
||||
if cfg.ClusterAPISockPath == "" && cfg.ClusterAPIListener == nil {
|
||||
cfg.ClusterAPISockPath = DefaultClusterAPISockPath
|
||||
}
|
||||
|
||||
if cfg.DockerClient == nil {
|
||||
@@ -162,7 +175,7 @@ type Machine struct {
|
||||
|
||||
config Config
|
||||
state *State
|
||||
// started is closed when the machine is ready to serve requests on the local API server.
|
||||
// started is closed when the machine and cluster API servers are ready to serve requests.
|
||||
started chan struct{}
|
||||
// initialised is closed when the machine is configured as a member of a cluster.
|
||||
initialised chan struct{}
|
||||
@@ -183,15 +196,14 @@ type Machine struct {
|
||||
// dockerService provides high-level operations for managing Docker containers.
|
||||
dockerService *machinedocker.Service
|
||||
dockerServer *machinedocker.Server
|
||||
// localMachineServer is the gRPC server for the machine API listening on the local Unix socket.
|
||||
localMachineServer *grpc.Server
|
||||
// machineAPIServer handles API requests directly on this machine.
|
||||
machineAPIServer *grpc.Server
|
||||
|
||||
// proxyDirector manages routing of gRPC requests between local and remote machine API servers.
|
||||
// proxyDirector routes API requests to local or remote machines.
|
||||
proxyDirector *apiproxy.Director
|
||||
// localProxyServer is the gRPC proxy server for the machine API listening on the local Unix socket.
|
||||
// It proxies requests to the local or remote machine API servers depending on the request targets
|
||||
// and aggregates responses.
|
||||
localProxyServer *grpc.Server
|
||||
// clusterAPIServer is the client-facing gRPC server that routes API requests across the cluster. It sends requests
|
||||
// to one or more local or remote machines and aggregates their responses.
|
||||
clusterAPIServer *grpc.Server
|
||||
|
||||
// mu protects the Machine from concurrent reads and writes.
|
||||
mu sync.RWMutex
|
||||
@@ -266,10 +278,10 @@ func NewMachine(config *Config) (*Machine, error) {
|
||||
}
|
||||
dockerService := machinedocker.NewService(config.DockerClient, db)
|
||||
|
||||
// Init a local gRPC proxy server that proxies requests to the local or remote machine API servers.
|
||||
// Init the client-facing API server that routes requests to local or remote machines.
|
||||
mapper := apiproxy.NewCorrosionMapper(corroStore)
|
||||
proxyDirector := apiproxy.NewDirector(config.MachineSockPath, constants.MachineAPIPort, mapper)
|
||||
localProxyServer := grpc.NewServer(
|
||||
proxyDirector := apiproxy.NewDirector(config.MachineAPISockPath, constants.UncloudAPIPort, mapper)
|
||||
clusterAPIServer := grpc.NewServer(
|
||||
grpc.ForceServerCodecV2(proxy.Codec()),
|
||||
grpc.UnaryInterceptor(grpcversion.ServerUnaryInterceptor),
|
||||
grpc.StreamInterceptor(grpcversion.ServerStreamInterceptor),
|
||||
@@ -288,7 +300,7 @@ func NewMachine(config *Config) (*Machine, error) {
|
||||
store: corroStore,
|
||||
cluster: c,
|
||||
dockerService: dockerService,
|
||||
localProxyServer: localProxyServer,
|
||||
clusterAPIServer: clusterAPIServer,
|
||||
proxyDirector: proxyDirector,
|
||||
}
|
||||
|
||||
@@ -305,7 +317,15 @@ func NewMachine(config *Config) (*Machine, error) {
|
||||
WaitForNetworkReady: m.WaitForNetworkReady,
|
||||
})
|
||||
caddyServer := caddyconfig.NewServer(caddyconfig.NewService(config.CaddyConfigDir))
|
||||
m.localMachineServer = newGRPCServer(m, c, m.dockerServer, caddyServer)
|
||||
|
||||
caddyStore, err := corroStore.Keyspace(caddystorage.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create namespaced cluster store for Caddy storage: %w", err)
|
||||
}
|
||||
caddyStorageServer := caddystorage.NewServer(caddyStore)
|
||||
|
||||
leaseServer := distlockgrpc.NewServer(distlock.NewMemoryStore())
|
||||
m.machineAPIServer = newGRPCServer(m, c, m.dockerServer, caddyServer, caddyStorageServer, leaseServer)
|
||||
|
||||
if m.Initialised() {
|
||||
close(m.initialised)
|
||||
@@ -314,16 +334,25 @@ func NewMachine(config *Config) (*Machine, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func newGRPCServer(m pb.MachineServer, c pb.ClusterServer, d pb.DockerServer, caddy pb.CaddyServer) *grpc.Server {
|
||||
func newGRPCServer(
|
||||
m pb.MachineServer,
|
||||
c pb.ClusterServer,
|
||||
d pb.DockerServer,
|
||||
caddy pb.CaddyServer,
|
||||
caddyStorage pb.CaddyStorageServer,
|
||||
lease distlockgrpc.LeaseServer,
|
||||
) *grpc.Server {
|
||||
s := grpc.NewServer()
|
||||
pb.RegisterMachineServer(s, m)
|
||||
pb.RegisterClusterServer(s, c)
|
||||
pb.RegisterDockerServer(s, d)
|
||||
pb.RegisterCaddyServer(s, caddy)
|
||||
pb.RegisterCaddyStorageServer(s, caddyStorage)
|
||||
distlockgrpc.RegisterLeaseServer(s, lease)
|
||||
return s
|
||||
}
|
||||
|
||||
// Started returns a channel that is closed when the machine is ready to serve requests on the local API server.
|
||||
// Started returns a channel that is closed when the machine and cluster API servers are ready to serve requests.
|
||||
func (m *Machine) Started() <-chan struct{} {
|
||||
return m.started
|
||||
}
|
||||
@@ -375,21 +404,54 @@ func (m *Machine) Run(ctx context.Context) error {
|
||||
// Create a cancellable context for the Run method to allow stopping the machine gracefully.
|
||||
ctx, m.stop = context.WithCancel(ctx)
|
||||
|
||||
// Take over the systemd-activated socket for the cluster API if provided, otherwise bind to the configured
|
||||
// Unix socket path.
|
||||
clusterAPIListener := m.config.ClusterAPIListener
|
||||
clusterAPISockPath := m.config.ClusterAPISockPath
|
||||
if clusterAPIListener != nil {
|
||||
if clusterAPIListener.Addr().Network() == "unix" {
|
||||
clusterAPISockPath = clusterAPIListener.Addr().String()
|
||||
} else {
|
||||
clusterAPISockPath = ""
|
||||
}
|
||||
defer clusterAPIListener.Close()
|
||||
}
|
||||
|
||||
sockGID, err := socketGID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = prepareUnixSocketDirectory(m.config.MachineAPISockPath, sockGID); err != nil {
|
||||
return fmt.Errorf("prepare machine API Unix socket directory: %w", err)
|
||||
}
|
||||
// The cluster API socket activated by systemd may have been created with root group ownership.
|
||||
// Apply the intended access permissions.
|
||||
if clusterAPISockPath != "" {
|
||||
if err = prepareUnixSocketDirectory(clusterAPISockPath, sockGID); err != nil {
|
||||
return fmt.Errorf("prepare cluster API Unix socket directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Docker dependency is essential for the machine to function. Block until it's ready.
|
||||
if err := docker.WaitDaemonReady(ctx, m.config.DockerClient); err != nil {
|
||||
return fmt.Errorf("wait for Docker daemon: %w", err)
|
||||
}
|
||||
defer m.config.DockerClient.Close()
|
||||
|
||||
// Bind the local API listeners before starting the dependencies (e.g. corrosion) to not deal with the teardown
|
||||
// Bind the API listeners before starting the dependencies (e.g. corrosion) to not deal with the teardown
|
||||
// on failure.
|
||||
machineListener, err := listenUnixSocket(m.config.MachineSockPath)
|
||||
machineAPIListener, err := sockets.NewUnixSocket(m.config.MachineAPISockPath, sockGID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listen machine API unix socket %q: %w", m.config.MachineSockPath, err)
|
||||
return fmt.Errorf("listen machine API Unix socket %q: %w", m.config.MachineAPISockPath, err)
|
||||
}
|
||||
proxyListener, err := listenUnixSocket(m.config.UncloudSockPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listen API proxy unix socket %q: %w", m.config.UncloudSockPath, err)
|
||||
defer machineAPIListener.Close()
|
||||
|
||||
if clusterAPIListener == nil {
|
||||
clusterAPIListener, err = sockets.NewUnixSocket(clusterAPISockPath, sockGID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listen cluster API Unix socket %q: %w", clusterAPISockPath, err)
|
||||
}
|
||||
defer clusterAPIListener.Close()
|
||||
}
|
||||
|
||||
// Configure and start the corrosion service on the loopback if the machine is not initialised as a cluster
|
||||
@@ -417,20 +479,20 @@ func (m *Machine) Run(ctx context.Context) error {
|
||||
// Use an errgroup to coordinate error handling and graceful shutdown of multiple machine components.
|
||||
errGroup, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
// Start the local machine API server.
|
||||
// Start the API server that handles requests directly on this machine.
|
||||
errGroup.Go(func() error {
|
||||
slog.Info("Starting local machine API server.", "path", m.config.MachineSockPath)
|
||||
if err := m.localMachineServer.Serve(machineListener); err != nil {
|
||||
return fmt.Errorf("local machine API server failed: %w", err)
|
||||
slog.Info("Starting machine API server.", "path", m.config.MachineAPISockPath)
|
||||
if err := m.machineAPIServer.Serve(machineAPIListener); err != nil {
|
||||
return fmt.Errorf("machine API server failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Start the local API proxy server.
|
||||
// Start the client-facing API server that routes requests across the cluster.
|
||||
errGroup.Go(func() error {
|
||||
slog.Info("Starting local API proxy server.", "path", m.config.UncloudSockPath)
|
||||
if err := m.localProxyServer.Serve(proxyListener); err != nil {
|
||||
return fmt.Errorf("local API proxy server failed: %w", err)
|
||||
slog.Info("Starting cluster API server.", "path", clusterAPIListener.Addr().String())
|
||||
if err := m.clusterAPIServer.Serve(clusterAPIListener); err != nil {
|
||||
return fmt.Errorf("cluster API server failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -458,7 +520,7 @@ func (m *Machine) Run(ctx context.Context) error {
|
||||
|
||||
slog.Info("Starting cluster controller.")
|
||||
// Update the proxy director's local address to the machine's management IP address, allowing
|
||||
// the proxy to identify which requests should be proxied to the local machine API server.
|
||||
// the proxy to identify which requests should be handled by the local API server.
|
||||
m.proxyDirector.UpdateLocalAddress(m.state.Network.ManagementIP.String())
|
||||
proxyServer := grpc.NewServer(
|
||||
grpc.ForceServerCodecV2(proxy.Codec()),
|
||||
@@ -557,17 +619,17 @@ func (m *Machine) Run(ctx context.Context) error {
|
||||
// Shutdown goroutine.
|
||||
errGroup.Go(func() error {
|
||||
<-ctx.Done()
|
||||
slog.Info("Stopping local machine API server.")
|
||||
slog.Info("Stopping machine API server.")
|
||||
// TODO: implement timeout for graceful shutdown.
|
||||
m.localMachineServer.GracefulStop()
|
||||
slog.Info("Local machine API server stopped.")
|
||||
m.machineAPIServer.GracefulStop()
|
||||
slog.Info("Machine API server stopped.")
|
||||
|
||||
slog.Info("Stopping local API proxy server.")
|
||||
slog.Info("Stopping cluster API server.")
|
||||
// TODO: implement timeout for graceful shutdown.
|
||||
m.localProxyServer.GracefulStop()
|
||||
m.clusterAPIServer.GracefulStop()
|
||||
// Close the proxy director to close all backend connections.
|
||||
m.proxyDirector.Close()
|
||||
slog.Info("Local API proxy server stopped.")
|
||||
slog.Info("Cluster API server stopped.")
|
||||
|
||||
return nil
|
||||
})
|
||||
@@ -596,38 +658,46 @@ func (m *Machine) Run(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// listenUnixSocket creates a new Unix socket listener with the specified path. The socket file is created with 0660
|
||||
// access mode and uncloud group if the group is found, otherwise it falls back to the root group.
|
||||
func listenUnixSocket(path string) (net.Listener, error) {
|
||||
// socketGID returns the uncloud group ID, or the root group ID if the uncloud group does not exist.
|
||||
func socketGID() (int, error) {
|
||||
gid := 0 // Fall back to the root group if the uncloud group is not found.
|
||||
group, err := user.LookupGroup(DefaultSockGroup)
|
||||
if err != nil {
|
||||
//goland:noinspection GoTypeAssertionOnErrors
|
||||
if _, ok := err.(user.UnknownGroupError); ok {
|
||||
slog.Info(
|
||||
"Specified group not found, using root group for the API socket.",
|
||||
"group", DefaultSockGroup, "path", path,
|
||||
"Specified group not found, using root group for API sockets.",
|
||||
"group", DefaultSockGroup,
|
||||
)
|
||||
} else {
|
||||
return nil, fmt.Errorf("lookup %q group ID (GID): %w", DefaultSockGroup, err)
|
||||
return 0, fmt.Errorf("lookup %q group ID (GID): %w", DefaultSockGroup, err)
|
||||
}
|
||||
} else {
|
||||
gid, err = strconv.Atoi(group.Gid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse %q group ID (GID) %q: %w", DefaultSockGroup, group.Gid, err)
|
||||
return 0, fmt.Errorf("parse %q group ID (GID) %q: %w", DefaultSockGroup, group.Gid, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the parent directory exists and has the correct group permissions.
|
||||
return gid, nil
|
||||
}
|
||||
|
||||
// prepareUnixSocketDirectory ensures the socket's parent directory has the intended group and permissions.
|
||||
func prepareUnixSocketDirectory(path string, gid int) error {
|
||||
parent, _ := filepath.Split(path)
|
||||
if err = os.MkdirAll(parent, 0o750); err != nil {
|
||||
return nil, fmt.Errorf("create directory %q: %w", parent, err)
|
||||
if err := os.MkdirAll(parent, 0o750); err != nil {
|
||||
return fmt.Errorf("create directory %q: %w", parent, err)
|
||||
}
|
||||
if err = os.Chown(parent, -1, gid); err != nil {
|
||||
return nil, fmt.Errorf("chown directory %q: %w", parent, err)
|
||||
if err := os.Chown(parent, -1, gid); err != nil {
|
||||
return fmt.Errorf("chown directory %q: %w", parent, err)
|
||||
}
|
||||
// MkdirAll preserves the mode of an existing directory. A Docker container may have created the bind-mount source
|
||||
// first, so apply the intended mode explicitly.
|
||||
if err := os.Chmod(parent, 0o750); err != nil {
|
||||
return fmt.Errorf("chmod directory %q: %w", parent, err)
|
||||
}
|
||||
|
||||
return sockets.NewUnixSocket(path, gid)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Machine) configureCorrosion() error {
|
||||
@@ -1104,6 +1174,19 @@ func (m *Machine) InspectMachine(ctx context.Context, _ *emptypb.Empty) (*pb.Ins
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Machine) WaitForStoreVersion(ctx context.Context, req *pb.WaitForStoreVersionRequest) (*emptypb.Empty, error) {
|
||||
if err := m.store.WaitForVersion(ctx, req.MinVersion); err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return nil, status.FromContextError(ctx.Err()).Err()
|
||||
}
|
||||
if errors.Is(err, store.ErrInvalidStoreVersion) {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
// UpdateMachine updates the configuration of this machine in its local state (the source of truth) and syncs
|
||||
// the result to the cluster store.
|
||||
func (m *Machine) UpdateMachine(ctx context.Context, req *pb.UpdateMachineRequest) (*pb.UpdateMachineResponse, error) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/network"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -29,9 +29,9 @@ type State struct {
|
||||
// PublicIP is this machine's advertised public IP for ingress. Zero value if unset.
|
||||
PublicIP netip.Addr `json:",omitempty"`
|
||||
// MinStoreVersion is the cluster store version this machine must reach before participating.
|
||||
// Per-actor vector (Corrosion actor UUID → max applied db_version) captured from an existing
|
||||
// Per-actor vector (Corrosion actor UUID → max processed db_version) captured from an existing
|
||||
// member at join time. Cleared once reached.
|
||||
MinStoreVersion map[string]int64 `json:",omitempty"`
|
||||
MinStoreVersion map[string]uint64 `json:",omitempty"`
|
||||
// CorrosionAPIToken authenticates requests to the local Corrosion API.
|
||||
CorrosionAPIToken secret.Secret `json:",omitempty"`
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/corrosion"
|
||||
)
|
||||
|
||||
const namespaceSeparator = ":"
|
||||
|
||||
var namespacePattern = regexp.MustCompile(`^[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*$`)
|
||||
|
||||
// Record is a key-value entry and its metadata.
|
||||
type Record struct {
|
||||
Key string
|
||||
Value []byte
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// KeyspaceListOptions controls the records returned by [Keyspace.List].
|
||||
type KeyspaceListOptions struct {
|
||||
// KeysOnly omits record values from the query when true.
|
||||
KeysOnly bool
|
||||
}
|
||||
|
||||
// KeyspaceDeleteOptions controls which records [Keyspace.Delete] deletes.
|
||||
type KeyspaceDeleteOptions struct {
|
||||
// Prefix deletes the key and every key that starts with it.
|
||||
Prefix bool
|
||||
}
|
||||
|
||||
// Keyspace provides namespace-scoped key-value storage.
|
||||
// Keys are relative to the namespace. Prefix operations use simple string-prefix matching.
|
||||
type Keyspace struct {
|
||||
corro *corrosion.APIClient
|
||||
namespace string
|
||||
}
|
||||
|
||||
// Keyspace returns key-value storage scoped to namespace.
|
||||
func (s *Store) Keyspace(namespace string) (*Keyspace, error) {
|
||||
if !namespacePattern.MatchString(namespace) {
|
||||
return nil, fmt.Errorf("invalid namespace %q", namespace)
|
||||
}
|
||||
return &Keyspace{
|
||||
corro: s.corro,
|
||||
namespace: namespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get returns the entry for key. It returns [ErrKeyNotFound] when the key does not exist.
|
||||
func (k *Keyspace) Get(ctx context.Context, key string) (Record, error) {
|
||||
if key == "" {
|
||||
return Record{}, fmt.Errorf("key is empty")
|
||||
}
|
||||
|
||||
rows, err := k.corro.QueryContext(ctx, "SELECT value, updated_at FROM cluster WHERE key = ?", k.namespacedKey(key))
|
||||
if err != nil {
|
||||
return Record{}, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
if err = rows.Err(); err != nil {
|
||||
return Record{}, err
|
||||
}
|
||||
return Record{}, ErrKeyNotFound
|
||||
}
|
||||
|
||||
record := Record{Key: key}
|
||||
var updatedAt string
|
||||
if err = rows.Scan(&record.Value, &updatedAt); err != nil {
|
||||
return Record{}, err
|
||||
}
|
||||
if record.UpdatedAt, err = parseTimestamp(updatedAt); err != nil {
|
||||
return Record{}, fmt.Errorf("parse updated_at for key %q: %w", key, err)
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// Put stores value at key.
|
||||
func (k *Keyspace) Put(ctx context.Context, key string, value []byte) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("key is empty")
|
||||
}
|
||||
|
||||
_, err := k.corro.ExecContext(ctx,
|
||||
"INSERT OR REPLACE INTO cluster (key, value, updated_at) VALUES (?, ?, datetime('now', 'subsec'))",
|
||||
k.namespacedKey(key), value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete deletes key. When Prefix is true, it also deletes every key that starts with key.
|
||||
func (k *Keyspace) Delete(ctx context.Context, key string, opts KeyspaceDeleteOptions) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("key is empty")
|
||||
}
|
||||
|
||||
namespacedKey := k.namespacedKey(key)
|
||||
if !opts.Prefix {
|
||||
_, err := k.corro.ExecContext(ctx, "DELETE FROM cluster WHERE key = ?", namespacedKey)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := k.corro.ExecContext(ctx, `DELETE FROM cluster WHERE key >= ? AND key < ?`,
|
||||
namespacedKey, prefixRangeEnd(namespacedKey))
|
||||
return err
|
||||
}
|
||||
|
||||
// List returns entries whose keys start with prefix, ordered by key. An empty prefix lists every entry in the keyspace.
|
||||
func (k *Keyspace) List(ctx context.Context, prefix string, opts KeyspaceListOptions) ([]Record, error) {
|
||||
keyspacePrefix := k.namespacedKey("")
|
||||
namespacedPrefix := k.namespacedKey(prefix)
|
||||
query := "SELECT key, value, updated_at FROM cluster WHERE key >= ? AND key < ? ORDER BY key"
|
||||
if opts.KeysOnly {
|
||||
query = "SELECT key, updated_at FROM cluster WHERE key >= ? AND key < ? ORDER BY key"
|
||||
}
|
||||
|
||||
rows, err := k.corro.QueryContext(ctx, query, namespacedPrefix, prefixRangeEnd(namespacedPrefix))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var records []Record
|
||||
for rows.Next() {
|
||||
var (
|
||||
record Record
|
||||
updatedAt string
|
||||
)
|
||||
if opts.KeysOnly {
|
||||
err = rows.Scan(&record.Key, &updatedAt)
|
||||
} else {
|
||||
err = rows.Scan(&record.Key, &record.Value, &updatedAt)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if record.UpdatedAt, err = parseTimestamp(updatedAt); err != nil {
|
||||
return nil, fmt.Errorf("parse updated_at for key %q: %w", record.Key, err)
|
||||
}
|
||||
record.Key = strings.TrimPrefix(record.Key, keyspacePrefix)
|
||||
records = append(records, record)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (k *Keyspace) namespacedKey(key string) string {
|
||||
return k.namespace + namespaceSeparator + key
|
||||
}
|
||||
|
||||
func parseTimestamp(value string) (time.Time, error) {
|
||||
return time.Parse(time.DateTime, value)
|
||||
}
|
||||
|
||||
// prefixRangeEnd returns an exclusive upper bound for a key prefix.
|
||||
func prefixRangeEnd(prefix string) string {
|
||||
return prefix + string(utf8.MaxRune)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ErrInvalidStoreVersion indicates an invalid actor UUID in a store version vector.
|
||||
var ErrInvalidStoreVersion = errors.New("invalid store version")
|
||||
|
||||
// WaitForVersion waits until the local store has reached each actor's minimum version in minVersion, with no known
|
||||
// missing or pending transactions through those versions. Corrosion may satisfy a version by applying its surviving
|
||||
// changes or by marking it complete because its changes have been superseded.
|
||||
//
|
||||
// Waiting normally makes the captured data available locally. However, if another write replaces some of that data
|
||||
// before it arrives, Corrosion can complete the older version without transferring the replaced data. If the
|
||||
// replacement is outside minVersion, this method can return while the affected data is still missing or outdated.
|
||||
// This can happen during concurrent updates even when all machines are well connected.
|
||||
//
|
||||
// This method does not guarantee an exact snapshot or delivery of every historical value.
|
||||
// Callers that require a specific record or condition should verify it after waiting.
|
||||
//
|
||||
// The method observes native replication without initiating synchronisation.
|
||||
// An empty minVersion requires no replication. The context controls cancellation and the deadline.
|
||||
func (s *Store) WaitForVersion(ctx context.Context, minVersion map[string]uint64) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
placeholders := make([]string, 0, len(minVersion))
|
||||
args := make([]any, 0, 2*len(minVersion))
|
||||
for actor, version := range minVersion {
|
||||
actorID, err := uuid.Parse(actor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: actor '%s' is not a UUID: %v", ErrInvalidStoreVersion, actor, err)
|
||||
}
|
||||
if version == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Corrosion accepts a JSON array of bytes as a BLOB parameter. A UUID marshals as a string,
|
||||
// and a byte slice marshals as base64, so use a plain fixed-size array instead.
|
||||
placeholders = append(placeholders, "(?, ?)")
|
||||
args = append(args, [16]byte(actorID), version)
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check all bookkeeping in one SQLite snapshot. Raw buffered rows can remain after application,
|
||||
// but sequence bookkeeping is removed in the same transaction that applies or clears a version.
|
||||
query := `WITH min_version(actor_id, version) AS (VALUES ` + strings.Join(placeholders, ", ") + `)
|
||||
SELECT NOT EXISTS (
|
||||
SELECT 1 FROM min_version
|
||||
LEFT JOIN crsql_db_versions AS current ON current.site_id = min_version.actor_id
|
||||
WHERE COALESCE(current.db_version, 0) < min_version.version
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM __corro_bookkeeping_gaps
|
||||
WHERE actor_id = min_version.actor_id AND start <= min_version.version
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM __corro_seq_bookkeeping
|
||||
WHERE site_id = min_version.actor_id AND db_version <= min_version.version
|
||||
)
|
||||
)`
|
||||
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
rows, err := s.corro.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check store replication: %w", err)
|
||||
}
|
||||
if !rows.Next() {
|
||||
if err = rows.Err(); err != nil {
|
||||
return fmt.Errorf("check store replication: %w", err)
|
||||
}
|
||||
return errors.New("check store replication: store version query returned no rows")
|
||||
}
|
||||
var reached int
|
||||
if err = rows.Scan(&reached); err != nil {
|
||||
rows.Close()
|
||||
return fmt.Errorf("check store replication: %w", err)
|
||||
}
|
||||
rows.Next() // Consume the end-of-query event.
|
||||
rows.Close()
|
||||
if err = rows.Err(); err != nil {
|
||||
return fmt.Errorf("check store replication: %w", err)
|
||||
}
|
||||
if reached == 1 {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,13 @@ package store
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/corrosion"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
@@ -31,14 +30,19 @@ func New(corro *corrosion.APIClient) *Store {
|
||||
return &Store{corro: corro}
|
||||
}
|
||||
|
||||
// Get retrieves an unnamespaced legacy value.
|
||||
//
|
||||
// Deprecated: Existing callers may continue to use Get for legacy records. New
|
||||
// code should use [Store.Keyspace].
|
||||
func (s *Store) Get(ctx context.Context, key string, value any) error {
|
||||
rows, err := s.corro.QueryContext(ctx, "SELECT value FROM cluster WHERE key = ?", key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
if !rows.Next() {
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
if err = rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrKeyNotFound
|
||||
}
|
||||
@@ -48,32 +52,47 @@ func (s *Store) Get(ctx context.Context, key string, value any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Put stores an unnamespaced legacy value.
|
||||
//
|
||||
// Deprecated: Existing callers may continue to use Put for legacy records. New
|
||||
// code should use [Store.Keyspace].
|
||||
func (s *Store) Put(ctx context.Context, key string, value any) error {
|
||||
_, err := s.corro.ExecContext(ctx,
|
||||
"INSERT OR REPLACE INTO cluster (key, value, updated_at) VALUES (?, ?, datetime('now'))",
|
||||
"INSERT OR REPLACE INTO cluster (key, value, updated_at) VALUES (?, ?, datetime('now', 'subsec'))",
|
||||
key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete deletes an unnamespaced legacy value.
|
||||
//
|
||||
// Deprecated: Existing callers may continue to use Delete for legacy records.
|
||||
// New code should use [Store.Keyspace].
|
||||
func (s *Store) Delete(ctx context.Context, key string) error {
|
||||
_, err := s.corro.ExecContext(ctx, "DELETE FROM cluster WHERE key = ?", key)
|
||||
return err
|
||||
}
|
||||
|
||||
// Version returns the cluster store's per-actor version vector:
|
||||
// Corrosion actor ID (UUID string) → max received db_version for that actor.
|
||||
func (s *Store) Version(ctx context.Context) (map[string]int64, error) {
|
||||
// Version returns the replication progress observed by this store.
|
||||
// Each entry maps a Corrosion actor UUID to its highest processed database version. Corrosion also counts versions
|
||||
// whose changes were superseded and skipped. Versions at or below a reported version may still be missing or pending
|
||||
// locally.
|
||||
//
|
||||
// You can use [Store.WaitForVersion] to wait for replication through these versions, subject to the data-availability
|
||||
// limitations documented there.
|
||||
//
|
||||
// Capturing a vector does not wait for replication or prevent further writes.
|
||||
func (s *Store) Version(ctx context.Context) (map[string]uint64, error) {
|
||||
rows, err := s.corro.QueryContext(ctx, "SELECT site_id, db_version FROM crsql_db_versions")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query crsql_db_versions: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
versions := make(map[string]int64)
|
||||
versions := make(map[string]uint64)
|
||||
for rows.Next() {
|
||||
var (
|
||||
siteID []byte
|
||||
version int64
|
||||
version uint64
|
||||
)
|
||||
if err = rows.Scan(&siteID, &version); err != nil {
|
||||
return nil, fmt.Errorf("scan actor version: %w", err)
|
||||
@@ -87,35 +106,6 @@ func (s *Store) Version(ctx context.Context) (map[string]int64, error) {
|
||||
return versions, nil
|
||||
}
|
||||
|
||||
type MissingChange struct {
|
||||
ActorID string
|
||||
StartVersion int64
|
||||
EndVersion int64
|
||||
}
|
||||
|
||||
// KnownMissingChanges returns a list of currently known missing changes in the Corrosion database.
|
||||
func (s *Store) KnownMissingChanges(ctx context.Context) ([]MissingChange, error) {
|
||||
rows, err := s.corro.QueryContext(ctx, "SELECT actor_id, start, end FROM __corro_bookkeeping_gaps")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query missing changes: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var changes []MissingChange
|
||||
for rows.Next() {
|
||||
var c MissingChange
|
||||
var actorBytes []byte
|
||||
if err = rows.Scan(&actorBytes, &c.StartVersion, &c.EndVersion); err != nil {
|
||||
return nil, fmt.Errorf("scan missing change: %w", err)
|
||||
}
|
||||
|
||||
c.ActorID = hex.EncodeToString(actorBytes)
|
||||
changes = append(changes, c)
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (s *Store) CreateMachine(ctx context.Context, m *pb.MachineInfo) error {
|
||||
mJSON, err := protojson.Marshal(m)
|
||||
if err != nil {
|
||||
|
||||
+61
-48
@@ -2,11 +2,12 @@ package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -15,58 +16,53 @@ type Proxy struct {
|
||||
Listener net.Listener
|
||||
RemoteAddr string
|
||||
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||
// OnError is called for errors that occur during proxying individual connections. It may be called concurrently
|
||||
// for different connections.
|
||||
OnError func(error)
|
||||
activeConns sync.WaitGroup
|
||||
}
|
||||
|
||||
// deadliner is an interface for listeners that support setting deadlines.
|
||||
type deadliner interface {
|
||||
SetDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
// halfCloser is an interface for connections that support half-close.
|
||||
type halfCloser interface {
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
// Run starts the proxy and runs until the context is canceled.
|
||||
func (p *Proxy) Run(ctx context.Context) {
|
||||
// IsConnectionClosedError reports whether err indicates that a connection was closed or aborted by either peer.
|
||||
// Callers can use it to ignore routine connection shutdown or broken pipe errors reported to Proxy.OnError.
|
||||
func IsConnectionClosedError(err error) bool {
|
||||
return errors.Is(err, net.ErrClosed) || errors.Is(err, io.ErrClosedPipe) ||
|
||||
errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET)
|
||||
}
|
||||
|
||||
// Run starts the proxy and runs until the context is canceled or the listener fails. It returns nil when the context
|
||||
// is canceled. Errors handling individual connections are reported to OnError and do not stop the proxy.
|
||||
func (p *Proxy) Run(ctx context.Context) error {
|
||||
if p.DialContext == nil {
|
||||
p.DialContext = (&net.Dialer{}).DialContext
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
defer p.Listener.Close()
|
||||
|
||||
// Handle incoming connections until context is canceled.
|
||||
Loop:
|
||||
// Closing the listener unblocks Accept when the context is canceled. This works for both TCP and Unix listeners
|
||||
// and avoids polling with listener deadlines.
|
||||
stopClose := context.AfterFunc(ctx, func() {
|
||||
p.Listener.Close()
|
||||
})
|
||||
defer stopClose()
|
||||
|
||||
var runErr error
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break Loop
|
||||
default:
|
||||
}
|
||||
|
||||
// Set a deadline on the listener if supported to check context periodically.
|
||||
if dl, ok := p.Listener.(deadliner); ok {
|
||||
dl.SetDeadline(time.Now().Add(1 * time.Second))
|
||||
}
|
||||
|
||||
conn, err := p.Listener.Accept()
|
||||
if err != nil {
|
||||
if os.IsTimeout(err) {
|
||||
// Just a timeout, continue to check context and accept again.
|
||||
continue
|
||||
if ctx.Err() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break Loop
|
||||
default:
|
||||
if p.OnError != nil {
|
||||
p.OnError(fmt.Errorf("accept local connection: %w", err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
runErr = fmt.Errorf("accept local connection: %w", err)
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
|
||||
p.activeConns.Add(1)
|
||||
@@ -75,6 +71,7 @@ Loop:
|
||||
|
||||
// Wait for all connections to finish.
|
||||
p.activeConns.Wait()
|
||||
return runErr
|
||||
}
|
||||
|
||||
func (p *Proxy) handleConnection(ctx context.Context, localConn net.Conn) {
|
||||
@@ -87,46 +84,62 @@ func (p *Proxy) handleConnection(ctx context.Context, localConn net.Conn) {
|
||||
|
||||
remoteConn, err := p.DialContext(dialCtx, "tcp", p.RemoteAddr)
|
||||
if err != nil {
|
||||
if p.OnError != nil {
|
||||
if ctx.Err() == nil && p.OnError != nil {
|
||||
p.OnError(fmt.Errorf("connect remote address '%s': %w", p.RemoteAddr, err))
|
||||
}
|
||||
return
|
||||
}
|
||||
defer remoteConn.Close()
|
||||
|
||||
// Bidirectional copy with proper half-close handling.
|
||||
// Closing both connections aborts both copies after cancellation or a copy error. A clean EOF still uses
|
||||
// half-close so the other direction can finish sending any remaining data.
|
||||
closeConnections := func() {
|
||||
localConn.Close()
|
||||
remoteConn.Close()
|
||||
}
|
||||
stopClose := context.AfterFunc(ctx, closeConnections)
|
||||
defer stopClose()
|
||||
|
||||
done := make(chan error, 2)
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(remoteConn, localConn)
|
||||
if err != nil {
|
||||
done <- err
|
||||
closeConnections()
|
||||
return
|
||||
}
|
||||
// Close write half of remote connection if supported.
|
||||
if hc, ok := remoteConn.(halfCloser); ok {
|
||||
hc.CloseWrite()
|
||||
}
|
||||
done <- err
|
||||
done <- nil
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(localConn, remoteConn)
|
||||
if err != nil {
|
||||
done <- err
|
||||
closeConnections()
|
||||
return
|
||||
}
|
||||
// Close write half of local connection if supported.
|
||||
if hc, ok := localConn.(halfCloser); ok {
|
||||
hc.CloseWrite()
|
||||
}
|
||||
done <- err
|
||||
done <- nil
|
||||
}()
|
||||
|
||||
// Wait for both copies to complete or context cancel.
|
||||
// Wait for both copies to complete. The first error is the original failure because a copy reports it before
|
||||
// closing the connections to unblock the other copy.
|
||||
var copyErr error
|
||||
for range 2 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Close connections to abort ongoing copies.
|
||||
localConn.Close()
|
||||
remoteConn.Close()
|
||||
return
|
||||
case err = <-done:
|
||||
if err != nil && p.OnError != nil {
|
||||
p.OnError(fmt.Errorf("data copy: %w", err))
|
||||
}
|
||||
if err = <-done; err != nil && copyErr == nil {
|
||||
copyErr = err
|
||||
}
|
||||
}
|
||||
|
||||
if copyErr != nil && ctx.Err() == nil && p.OnError != nil {
|
||||
p.OnError(fmt.Errorf("data copy: %w", copyErr))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIsConnectionClosedError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
want bool
|
||||
}{
|
||||
{name: "closed network connection", err: net.ErrClosed, want: true},
|
||||
{name: "closed pipe", err: io.ErrClosedPipe, want: true},
|
||||
{name: "broken pipe", err: syscall.EPIPE, want: true},
|
||||
{name: "connection reset", err: syscall.ECONNRESET, want: true},
|
||||
{name: "wrapped connection error", err: fmt.Errorf("copy data: %w", syscall.EPIPE), want: true},
|
||||
{name: "other error", err: errors.New("copy failed"), want: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.want, IsConnectionClosedError(tt.err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunContinuesAfterClosedConnectionError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
listener := newTestListener()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
closedErrCh := make(chan error, 1)
|
||||
unexpectedErrCh := make(chan error, 1)
|
||||
var dialCount atomic.Int32
|
||||
|
||||
p := &Proxy{
|
||||
Listener: listener,
|
||||
RemoteAddr: "remote:80",
|
||||
DialContext: func(context.Context, string, string) (net.Conn, error) {
|
||||
if dialCount.Add(1) == 1 {
|
||||
return readErrorConn{err: syscall.EPIPE}, nil
|
||||
}
|
||||
|
||||
proxyConn, upstreamConn := net.Pipe()
|
||||
go func() {
|
||||
defer upstreamConn.Close()
|
||||
_, _ = upstreamConn.Write([]byte("ok"))
|
||||
}()
|
||||
return proxyConn, nil
|
||||
},
|
||||
OnError: func(err error) {
|
||||
if IsConnectionClosedError(err) {
|
||||
closedErrCh <- err
|
||||
return
|
||||
}
|
||||
unexpectedErrCh <- err
|
||||
},
|
||||
}
|
||||
|
||||
runErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
runErrCh <- p.Run(ctx)
|
||||
}()
|
||||
|
||||
firstConn := listener.connect()
|
||||
defer firstConn.Close()
|
||||
|
||||
select {
|
||||
case connErr := <-closedErrCh:
|
||||
require.ErrorIs(t, connErr, syscall.EPIPE)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timed out waiting for closed connection error")
|
||||
}
|
||||
|
||||
secondConn := listener.connect()
|
||||
defer secondConn.Close()
|
||||
require.NoError(t, secondConn.SetReadDeadline(time.Now().Add(time.Second)))
|
||||
|
||||
got := make([]byte, 2)
|
||||
_, err := io.ReadFull(secondConn, got)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "ok", string(got))
|
||||
|
||||
select {
|
||||
case unexpectedErr := <-unexpectedErrCh:
|
||||
t.Fatalf("unexpected connection error: %v", unexpectedErr)
|
||||
default:
|
||||
}
|
||||
|
||||
cancel()
|
||||
select {
|
||||
case runErr := <-runErrCh:
|
||||
require.NoError(t, runErr)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timed out waiting for proxy to stop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunReturnsListenerError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
listenerErr := errors.New("listener failed")
|
||||
listener := errorListener{err: listenerErr}
|
||||
|
||||
p := &Proxy{Listener: listener}
|
||||
err := p.Run(context.Background())
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "accept local connection")
|
||||
require.ErrorIs(t, err, listenerErr)
|
||||
}
|
||||
|
||||
type testListener struct {
|
||||
conns chan net.Conn
|
||||
closed chan struct{}
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func newTestListener() *testListener {
|
||||
return &testListener{
|
||||
conns: make(chan net.Conn),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *testListener) connect() net.Conn {
|
||||
clientConn, proxyConn := net.Pipe()
|
||||
l.conns <- proxyConn
|
||||
return clientConn
|
||||
}
|
||||
|
||||
func (l *testListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case conn := <-l.conns:
|
||||
return conn, nil
|
||||
case <-l.closed:
|
||||
return nil, net.ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (l *testListener) Close() error {
|
||||
l.closeOnce.Do(func() {
|
||||
close(l.closed)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *testListener) Addr() net.Addr {
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
type errorListener struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (l errorListener) Accept() (net.Conn, error) { return nil, l.err }
|
||||
func (errorListener) Close() error { return nil }
|
||||
func (errorListener) Addr() net.Addr { return &net.TCPAddr{} }
|
||||
|
||||
// readErrorConn fails reads immediately so tests can deterministically exercise a proxy copy failure.
|
||||
type readErrorConn struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (c readErrorConn) Read([]byte) (int, error) { return 0, c.err }
|
||||
func (readErrorConn) Write(p []byte) (int, error) { return len(p), nil }
|
||||
func (readErrorConn) Close() error { return nil }
|
||||
func (readErrorConn) LocalAddr() net.Addr { return &net.TCPAddr{} }
|
||||
func (readErrorConn) RemoteAddr() net.Addr { return &net.TCPAddr{} }
|
||||
func (readErrorConn) SetDeadline(time.Time) error { return nil }
|
||||
func (readErrorConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (readErrorConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/cluster"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ Uncloud stores its configuration in `~/.config/uncloud/config.yaml`. If you wish
|
||||
|
||||
Begin by initialising the first node in your cluster with `uc machine init [USER@HOST:PORT]`. If you do not have a need for Caddy reverse proxy, you may disable this feature with `--no-caddy`. If you want to avoid using uncloud's managed DNS service, add the `--no-dns` flag.
|
||||
|
||||
This command will idempotently install Docker, uncloudd, uncloud-corrosion. If Caddy is enabled, it will set up a reverse proxy. If Uncloud DNS is enabled, it will create a DNS A record for the machine's public IP address under `*.[CLUSTER ID].uncld.dev`.
|
||||
This command will idempotently install Docker and uncloudd. If Caddy is enabled, it will set up a reverse proxy. If Uncloud DNS is enabled, it will create a DNS A record for the machine's public IP address under `*.[CLUSTER ID].uncld.dev`.
|
||||
|
||||
If you wish to uninstall Uncloud and its components, run `uncloud-uninstall`.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ run = "mise lock --platform linux-x64,macos-arm64"
|
||||
description = "Regenerate gRPC API code from .proto files"
|
||||
run = """
|
||||
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative \
|
||||
--proto_path=. --proto_path=internal/machine/api/vendor internal/machine/api/pb/*.proto
|
||||
--proto_path=. --proto_path=api/pb/vendor api/pb/*.proto pkg/distlock/grpc/*.proto
|
||||
"""
|
||||
|
||||
[tasks.uc]
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
type MachineImage struct {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
// MachineFilter defines criteria to filter machines in ListMachines.
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
+6
-1
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/distribution/reference"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -265,6 +265,11 @@ type ContainerSpec struct {
|
||||
LogDriver *LogDriver
|
||||
// PidMode sets the PID namespace mode for the container. Currently only "" or "host" is supported.
|
||||
PidMode string
|
||||
// Tty allocates a pseudo-TTY and connects the container's standard streams to it.
|
||||
// Standard output and standard error share one stream.
|
||||
Tty bool
|
||||
// OpenStdin allocates standard input and keeps it open.
|
||||
OpenStdin bool
|
||||
// Privileged gives extended privileges to the container. This is a security risk and should be used with caution.
|
||||
Privileged bool
|
||||
// PullPolicy determines when to pull the image from the registry or use the image already available in the cluster.
|
||||
|
||||
+30
-6
@@ -7,9 +7,10 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/machine/docker"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
distlockgrpc "github.com/psviderski/uncloud/pkg/distlock/grpc"
|
||||
"golang.org/x/net/proxy"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -24,10 +25,12 @@ type Client struct {
|
||||
// Methods such as Reset or Inspect are ambiguous in the context of a machine+cluster client.
|
||||
pb.MachineClient
|
||||
pb.ClusterClient
|
||||
Caddy pb.CaddyClient
|
||||
Caddy pb.CaddyClient
|
||||
CaddyStorage pb.CaddyStorageClient
|
||||
// Docker is a namespaced client for the Docker service to distinguish Uncloud-specific service container operations
|
||||
// from generic Docker operations.
|
||||
Docker *docker.Client
|
||||
leases distlockgrpc.LeaseClient
|
||||
}
|
||||
|
||||
var _ api.Client = (*Client)(nil)
|
||||
@@ -56,7 +59,9 @@ func New(ctx context.Context, connector Connector) (*Client, error) {
|
||||
c.MachineClient = pb.NewMachineClient(c.conn)
|
||||
c.ClusterClient = pb.NewClusterClient(c.conn)
|
||||
c.Caddy = pb.NewCaddyClient(c.conn)
|
||||
c.CaddyStorage = pb.NewCaddyStorageClient(c.conn)
|
||||
c.Docker = docker.NewClient(c.conn)
|
||||
c.leases = distlockgrpc.NewLeaseClient(c.conn)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
@@ -78,8 +83,8 @@ func (cli *Client) progressOut() *streams.Out {
|
||||
// ProxyMachinesContext returns a new context that proxies gRPC requests to the specified machines.
|
||||
// If namesOrIDs is nil or empty, all machines are included.
|
||||
// This triggers One2Many proxying, which always injects metadata into the response.
|
||||
func (cli *Client) ProxyMachinesContext(ctx context.Context, namesOrIDs []string) context.Context {
|
||||
md := metadata.New(nil)
|
||||
func ProxyMachinesContext(ctx context.Context, namesOrIDs []string) context.Context {
|
||||
md := outgoingMetadataWithoutProxyTargets(ctx)
|
||||
if len(namesOrIDs) == 0 {
|
||||
md.Append("machines", "*")
|
||||
} else {
|
||||
@@ -92,7 +97,26 @@ func (cli *Client) ProxyMachinesContext(ctx context.Context, namesOrIDs []string
|
||||
// ProxySingleMachineContext returns a new context that proxies gRPC requests to a single specified machine.
|
||||
// This triggers One2One proxying, which does NOT inject metadata into the response.
|
||||
// Use this for requests that expect a single response message without metadata wrapper.
|
||||
func (cli *Client) ProxySingleMachineContext(ctx context.Context, nameOrID string) context.Context {
|
||||
md := metadata.Pairs("machine", nameOrID)
|
||||
func ProxySingleMachineContext(ctx context.Context, nameOrID string) context.Context {
|
||||
md := outgoingMetadataWithoutProxyTargets(ctx)
|
||||
md.Set("machine", nameOrID)
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
|
||||
func outgoingMetadataWithoutProxyTargets(ctx context.Context) metadata.MD {
|
||||
md, _ := metadata.FromOutgoingContext(ctx)
|
||||
md = md.Copy()
|
||||
md.Delete("machine")
|
||||
md.Delete("machines")
|
||||
return md
|
||||
}
|
||||
|
||||
// ProxyMachinesContext returns a new context that proxies gRPC requests to the specified machines.
|
||||
func (cli *Client) ProxyMachinesContext(ctx context.Context, namesOrIDs []string) context.Context {
|
||||
return ProxyMachinesContext(ctx, namesOrIDs)
|
||||
}
|
||||
|
||||
// ProxySingleMachineContext returns a new context that proxies gRPC requests to a single specified machine.
|
||||
func (cli *Client) ProxySingleMachineContext(ctx context.Context, nameOrID string) context.Context {
|
||||
return ProxySingleMachineContext(ctx, nameOrID)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user