mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
feat: impl of functionality for renaming and updating machines (#91)
This commit is contained in:
@@ -37,7 +37,9 @@ jobs:
|
||||
(echo "go.mod or go.sum has changed. Please run 'go mod tidy' and commit the changes." && exit 1)
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
run: |
|
||||
make ucind-image
|
||||
make test
|
||||
timeout-minutes: 10
|
||||
|
||||
check-protobuf:
|
||||
|
||||
@@ -46,6 +46,11 @@ proto:
|
||||
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
|
||||
|
||||
.PHONY: proto-mise
|
||||
proto-mise:
|
||||
mise exec -- 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
|
||||
|
||||
.PHONY: corrosion-image
|
||||
corrosion-image:
|
||||
docker build -t "$(CORROSION_IMAGE)" --target corrosion .
|
||||
@@ -67,9 +72,13 @@ test:
|
||||
ifeq ($(TEST_NAME),)
|
||||
go test -count=1 -v ./...
|
||||
else
|
||||
go test -count=1 -v -run ^$(TEST_NAME)$$ ./...
|
||||
go test -race -count=1 -v -run ^$(TEST_NAME)$$ ./...
|
||||
endif
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e:
|
||||
go test -race -count=1 -v ./test/e2e
|
||||
|
||||
.PHONY: test-clean
|
||||
test-clean:
|
||||
@CONTAINERS=$$(docker ps --filter "name=ucind-test" -q); \
|
||||
|
||||
@@ -59,7 +59,7 @@ func NewAddCommand() *cobra.Command {
|
||||
cmd.Flags().StringVar(
|
||||
&opts.publicIP, "public-ip", "auto",
|
||||
"Public IP address of the machine for ingress configuration. Use 'auto' for automatic detection, "+
|
||||
"blank '' or 'none' to disable ingress on this machine, or specify an IP address.",
|
||||
fmt.Sprintf("blank '' or '%s' to disable ingress on this machine, or specify an IP address.", PublicIPNone),
|
||||
)
|
||||
cmd.Flags().StringVarP(
|
||||
&opts.sshKey, "ssh-key", "i", "~/.ssh/id_ed25519",
|
||||
@@ -82,7 +82,7 @@ func add(ctx context.Context, uncli *cli.CLI, remoteMachine cli.RemoteMachine, o
|
||||
switch opts.publicIP {
|
||||
case "auto":
|
||||
publicIP = &netip.Addr{}
|
||||
case "", "none":
|
||||
case "", PublicIPNone:
|
||||
publicIP = nil
|
||||
default:
|
||||
ip, err := netip.ParseAddr(opts.publicIP)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package machine
|
||||
|
||||
const (
|
||||
// PublicIPNone is the value used to indicate removal of public IP
|
||||
PublicIPNone = "none"
|
||||
)
|
||||
@@ -77,7 +77,7 @@ func NewInitCommand() *cobra.Command {
|
||||
cmd.Flags().StringVar(
|
||||
&opts.publicIP, "public-ip", "auto",
|
||||
"Public IP address of the machine for ingress configuration. Use 'auto' for automatic detection, "+
|
||||
"blank '' or 'none' to disable ingress on this machine, or specify an IP address.",
|
||||
fmt.Sprintf("blank '' or '%s' to disable ingress on this machine, or specify an IP address.", PublicIPNone),
|
||||
)
|
||||
cmd.Flags().StringVarP(
|
||||
&opts.sshKey, "ssh-key", "i", "~/.ssh/id_ed25519",
|
||||
@@ -105,7 +105,7 @@ func initCluster(ctx context.Context, uncli *cli.CLI, remoteMachine *cli.RemoteM
|
||||
switch opts.publicIP {
|
||||
case "auto":
|
||||
publicIP = &netip.Addr{}
|
||||
case "", "none":
|
||||
case "", PublicIPNone:
|
||||
publicIP = nil
|
||||
default:
|
||||
ip, err := netip.ParseAddr(opts.publicIP)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewRenameCommand() *cobra.Command {
|
||||
var contextName string
|
||||
cmd := &cobra.Command{
|
||||
Use: "rename OLD_NAME NEW_NAME",
|
||||
Short: "Rename a machine in the cluster.",
|
||||
Long: `Rename a machine in the cluster.
|
||||
|
||||
This command changes the name of an existing machine while preserving all other
|
||||
configuration including network settings, public IP, and cluster membership.`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||
return rename(cmd.Context(), uncli, contextName, args[0], args[1])
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(
|
||||
&contextName, "context", "c", "",
|
||||
"Name of the cluster context. (default is the current context)",
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func rename(ctx context.Context, uncli *cli.CLI, contextName, oldName, newName string) error {
|
||||
client, err := uncli.ConnectCluster(ctx, contextName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
machine, err := client.RenameMachine(ctx, oldName, newName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rename machine: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Machine %q renamed to %q (ID: %s)\n", oldName, machine.Name, machine.Id)
|
||||
return nil
|
||||
}
|
||||
@@ -14,7 +14,9 @@ func NewRootCommand() *cobra.Command {
|
||||
NewAddCommand(),
|
||||
NewInitCommand(),
|
||||
NewListCommand(),
|
||||
NewRenameCommand(),
|
||||
NewRmCommand(),
|
||||
NewUpdateCommand(),
|
||||
NewTokenCommand(),
|
||||
)
|
||||
return cmd
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/cli"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type updateOptions struct {
|
||||
name string
|
||||
publicIP string
|
||||
context string
|
||||
}
|
||||
|
||||
func NewUpdateCommand() *cobra.Command {
|
||||
opts := updateOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update machine configuration in the cluster.",
|
||||
Long: `Update machine configuration in the cluster.
|
||||
|
||||
This command allows setting various machine properties including:
|
||||
- Machine name (--name)
|
||||
- Public IP address (--public-ip)
|
||||
|
||||
At least one flag must be specified to perform an update operation.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||
return update(cmd.Context(), uncli, cmd, opts, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(
|
||||
&opts.name, "name", "",
|
||||
"New name for the machine",
|
||||
)
|
||||
cmd.Flags().StringVar(
|
||||
&opts.publicIP, "public-ip", "",
|
||||
fmt.Sprintf("Public IP address of the machine for ingress configuration. Use '%s' or '' to remove the public IP.", PublicIPNone),
|
||||
)
|
||||
cmd.Flags().StringVarP(
|
||||
&opts.context, "context", "c", "",
|
||||
"Name of the cluster context. (default is the current context)",
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func update(ctx context.Context, uncli *cli.CLI, cmd *cobra.Command, opts updateOptions, machineNameOrID string) error {
|
||||
// Check if at least one flag was explicitly set
|
||||
if !cmd.Flags().Changed("name") && !cmd.Flags().Changed("public-ip") {
|
||||
return fmt.Errorf("at least one update flag must be specified (--name, --public-ip)")
|
||||
}
|
||||
|
||||
client, err := uncli.ConnectCluster(ctx, opts.context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// First, resolve the machine to get its ID
|
||||
machine, err := client.InspectMachine(ctx, machineNameOrID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("find machine: %w", err)
|
||||
}
|
||||
|
||||
// Build the update request
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: machine.Machine.Id,
|
||||
}
|
||||
|
||||
if opts.name != "" {
|
||||
req.Name = &opts.name
|
||||
}
|
||||
|
||||
// Check if --public-ip flag was explicitly provided
|
||||
if cmd.Flags().Changed("public-ip") {
|
||||
if opts.publicIP == "" || opts.publicIP == PublicIPNone {
|
||||
req.PublicIp = &pb.IP{} // Empty IP to signal removal
|
||||
} else {
|
||||
// Parse and validate the public IP
|
||||
ip, err := netip.ParseAddr(opts.publicIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid public IP address %q: %w", opts.publicIP, err)
|
||||
}
|
||||
req.PublicIp = pb.NewIP(ip)
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the update operation
|
||||
updatedMachine, err := client.UpdateMachine(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update machine: %w", err)
|
||||
}
|
||||
|
||||
// Report what was changed
|
||||
changes := make([]string, 0)
|
||||
if opts.name != "" {
|
||||
changes = append(changes, fmt.Sprintf("name: %q -> %q", machine.Machine.Name, updatedMachine.Name))
|
||||
}
|
||||
if cmd.Flags().Changed("public-ip") {
|
||||
oldIP := PublicIPNone
|
||||
if machine.Machine.PublicIp != nil {
|
||||
if addr, err := machine.Machine.PublicIp.ToAddr(); err == nil {
|
||||
oldIP = addr.String()
|
||||
}
|
||||
}
|
||||
newIP := PublicIPNone
|
||||
if updatedMachine.PublicIp != nil {
|
||||
if addr, err := updatedMachine.PublicIp.ToAddr(); err == nil {
|
||||
newIP = addr.String()
|
||||
}
|
||||
}
|
||||
changes = append(changes, fmt.Sprintf("public IP: %s -> %s", oldIP, newIP))
|
||||
}
|
||||
|
||||
fmt.Printf("Machine %q (ID: %s) configuration updated:\n", updatedMachine.Name, updatedMachine.Id)
|
||||
for _, change := range changes {
|
||||
fmt.Printf(" %s\n", change)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -590,6 +590,126 @@ func (x *DNSRecord) GetValues() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateMachineRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Machine to update
|
||||
MachineId string `protobuf:"bytes,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"`
|
||||
// Updated machine information
|
||||
Name *string `protobuf:"bytes,2,opt,name=name,proto3,oneof" json:"name,omitempty"`
|
||||
PublicIp *IP `protobuf:"bytes,3,opt,name=public_ip,json=publicIp,proto3,oneof" json:"public_ip,omitempty"`
|
||||
Endpoints []*IPPort `protobuf:"bytes,4,rep,name=endpoints,proto3" json:"endpoints,omitempty"`
|
||||
}
|
||||
|
||||
func (x *UpdateMachineRequest) Reset() {
|
||||
*x = UpdateMachineRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *UpdateMachineRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UpdateMachineRequest) ProtoMessage() {}
|
||||
|
||||
func (x *UpdateMachineRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[9]
|
||||
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 UpdateMachineRequest.ProtoReflect.Descriptor instead.
|
||||
func (*UpdateMachineRequest) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *UpdateMachineRequest) GetMachineId() string {
|
||||
if x != nil {
|
||||
return x.MachineId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UpdateMachineRequest) GetName() string {
|
||||
if x != nil && x.Name != nil {
|
||||
return *x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *UpdateMachineRequest) GetPublicIp() *IP {
|
||||
if x != nil {
|
||||
return x.PublicIp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UpdateMachineRequest) GetEndpoints() []*IPPort {
|
||||
if x != nil {
|
||||
return x.Endpoints
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateMachineResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Machine *MachineInfo `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"`
|
||||
}
|
||||
|
||||
func (x *UpdateMachineResponse) Reset() {
|
||||
*x = UpdateMachineResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *UpdateMachineResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UpdateMachineResponse) ProtoMessage() {}
|
||||
|
||||
func (x *UpdateMachineResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_machine_api_pb_cluster_proto_msgTypes[10]
|
||||
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 UpdateMachineResponse.ProtoReflect.Descriptor instead.
|
||||
func (*UpdateMachineResponse) Descriptor() ([]byte, []int) {
|
||||
return file_internal_machine_api_pb_cluster_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *UpdateMachineResponse) GetMachine() *MachineInfo {
|
||||
if x != nil {
|
||||
return x.Machine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_internal_machine_api_pb_cluster_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_internal_machine_api_pb_cluster_proto_rawDesc = []byte{
|
||||
@@ -654,35 +774,56 @@ var file_internal_machine_api_pb_cluster_proto_rawDesc = []byte{
|
||||
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, 0x86, 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, 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,
|
||||
0x22, 0xbb, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63,
|
||||
0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d,
|
||||
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, 0x01,
|
||||
0x01, 0x12, 0x29, 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, 0x48, 0x01, 0x52,
|
||||
0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x09,
|
||||
0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x09, 0x65, 0x6e,
|
||||
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x22, 0x43,
|
||||
0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 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, 0x32, 0xce, 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, 0x46, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
|
||||
0x6e, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d,
|
||||
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e,
|
||||
0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, 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,
|
||||
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,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -698,7 +839,7 @@ func file_internal_machine_api_pb_cluster_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
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, 9)
|
||||
var file_internal_machine_api_pb_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
|
||||
var file_internal_machine_api_pb_cluster_proto_goTypes = []any{
|
||||
(MachineMember_MembershipState)(0), // 0: api.MachineMember.MembershipState
|
||||
(DNSRecord_RecordType)(0), // 1: api.DNSRecord.RecordType
|
||||
@@ -711,38 +852,46 @@ var file_internal_machine_api_pb_cluster_proto_goTypes = []any{
|
||||
(*CreateDomainRecordsRequest)(nil), // 8: api.CreateDomainRecordsRequest
|
||||
(*CreateDomainRecordsResponse)(nil), // 9: api.CreateDomainRecordsResponse
|
||||
(*DNSRecord)(nil), // 10: api.DNSRecord
|
||||
(*NetworkConfig)(nil), // 11: api.NetworkConfig
|
||||
(*IP)(nil), // 12: api.IP
|
||||
(*MachineInfo)(nil), // 13: api.MachineInfo
|
||||
(*emptypb.Empty)(nil), // 14: google.protobuf.Empty
|
||||
(*UpdateMachineRequest)(nil), // 11: api.UpdateMachineRequest
|
||||
(*UpdateMachineResponse)(nil), // 12: api.UpdateMachineResponse
|
||||
(*NetworkConfig)(nil), // 13: api.NetworkConfig
|
||||
(*IP)(nil), // 14: api.IP
|
||||
(*MachineInfo)(nil), // 15: api.MachineInfo
|
||||
(*IPPort)(nil), // 16: api.IPPort
|
||||
(*emptypb.Empty)(nil), // 17: google.protobuf.Empty
|
||||
}
|
||||
var file_internal_machine_api_pb_cluster_proto_depIdxs = []int32{
|
||||
11, // 0: api.AddMachineRequest.network:type_name -> api.NetworkConfig
|
||||
12, // 1: api.AddMachineRequest.public_ip:type_name -> api.IP
|
||||
13, // 2: api.AddMachineResponse.machine:type_name -> api.MachineInfo
|
||||
13, // 3: api.MachineMember.machine:type_name -> api.MachineInfo
|
||||
13, // 0: api.AddMachineRequest.network:type_name -> api.NetworkConfig
|
||||
14, // 1: api.AddMachineRequest.public_ip:type_name -> api.IP
|
||||
15, // 2: api.AddMachineResponse.machine:type_name -> api.MachineInfo
|
||||
15, // 3: api.MachineMember.machine:type_name -> api.MachineInfo
|
||||
0, // 4: api.MachineMember.state:type_name -> api.MachineMember.MembershipState
|
||||
4, // 5: api.ListMachinesResponse.machines:type_name -> api.MachineMember
|
||||
10, // 6: api.CreateDomainRecordsRequest.records:type_name -> api.DNSRecord
|
||||
10, // 7: api.CreateDomainRecordsResponse.records:type_name -> api.DNSRecord
|
||||
1, // 8: api.DNSRecord.type:type_name -> api.DNSRecord.RecordType
|
||||
2, // 9: api.Cluster.AddMachine:input_type -> api.AddMachineRequest
|
||||
14, // 10: api.Cluster.ListMachines:input_type -> google.protobuf.Empty
|
||||
7, // 11: api.Cluster.ReserveDomain:input_type -> api.ReserveDomainRequest
|
||||
14, // 12: api.Cluster.GetDomain:input_type -> google.protobuf.Empty
|
||||
14, // 13: api.Cluster.ReleaseDomain:input_type -> google.protobuf.Empty
|
||||
8, // 14: api.Cluster.CreateDomainRecords:input_type -> api.CreateDomainRecordsRequest
|
||||
3, // 15: api.Cluster.AddMachine:output_type -> api.AddMachineResponse
|
||||
5, // 16: api.Cluster.ListMachines:output_type -> api.ListMachinesResponse
|
||||
6, // 17: api.Cluster.ReserveDomain:output_type -> api.Domain
|
||||
6, // 18: api.Cluster.GetDomain:output_type -> api.Domain
|
||||
6, // 19: api.Cluster.ReleaseDomain:output_type -> api.Domain
|
||||
9, // 20: api.Cluster.CreateDomainRecords:output_type -> api.CreateDomainRecordsResponse
|
||||
15, // [15:21] is the sub-list for method output_type
|
||||
9, // [9:15] is the sub-list for method input_type
|
||||
9, // [9:9] is the sub-list for extension type_name
|
||||
9, // [9:9] is the sub-list for extension extendee
|
||||
0, // [0:9] is the sub-list for field type_name
|
||||
14, // 9: api.UpdateMachineRequest.public_ip:type_name -> api.IP
|
||||
16, // 10: api.UpdateMachineRequest.endpoints:type_name -> api.IPPort
|
||||
15, // 11: api.UpdateMachineResponse.machine:type_name -> api.MachineInfo
|
||||
2, // 12: api.Cluster.AddMachine:input_type -> api.AddMachineRequest
|
||||
17, // 13: api.Cluster.ListMachines:input_type -> google.protobuf.Empty
|
||||
11, // 14: api.Cluster.UpdateMachine:input_type -> api.UpdateMachineRequest
|
||||
7, // 15: api.Cluster.ReserveDomain:input_type -> api.ReserveDomainRequest
|
||||
17, // 16: api.Cluster.GetDomain:input_type -> google.protobuf.Empty
|
||||
17, // 17: api.Cluster.ReleaseDomain:input_type -> google.protobuf.Empty
|
||||
8, // 18: api.Cluster.CreateDomainRecords:input_type -> api.CreateDomainRecordsRequest
|
||||
3, // 19: api.Cluster.AddMachine:output_type -> api.AddMachineResponse
|
||||
5, // 20: api.Cluster.ListMachines:output_type -> api.ListMachinesResponse
|
||||
12, // 21: api.Cluster.UpdateMachine:output_type -> api.UpdateMachineResponse
|
||||
6, // 22: api.Cluster.ReserveDomain:output_type -> api.Domain
|
||||
6, // 23: api.Cluster.GetDomain:output_type -> api.Domain
|
||||
6, // 24: api.Cluster.ReleaseDomain:output_type -> api.Domain
|
||||
9, // 25: api.Cluster.CreateDomainRecords:output_type -> api.CreateDomainRecordsResponse
|
||||
19, // [19:26] is the sub-list for method output_type
|
||||
12, // [12:19] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_internal_machine_api_pb_cluster_proto_init() }
|
||||
@@ -861,14 +1010,39 @@ 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 {
|
||||
switch v := v.(*UpdateMachineRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[10].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*UpdateMachineResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_internal_machine_api_pb_cluster_proto_msgTypes[9].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_machine_api_pb_cluster_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 9,
|
||||
NumMessages: 11,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ import "internal/machine/api/pb/machine.proto";
|
||||
service Cluster {
|
||||
rpc AddMachine(AddMachineRequest) returns (AddMachineResponse);
|
||||
rpc ListMachines(google.protobuf.Empty) returns (ListMachinesResponse);
|
||||
rpc UpdateMachine(UpdateMachineRequest) returns (UpdateMachineResponse);
|
||||
|
||||
rpc ReserveDomain(ReserveDomainRequest) returns (Domain);
|
||||
rpc GetDomain(google.protobuf.Empty) returns (Domain);
|
||||
@@ -76,3 +77,17 @@ message DNSRecord {
|
||||
RecordType type = 2;
|
||||
repeated string values = 3;
|
||||
}
|
||||
|
||||
message UpdateMachineRequest {
|
||||
// Machine to update
|
||||
string machine_id = 1;
|
||||
|
||||
// Updated machine information
|
||||
optional string name = 2;
|
||||
optional IP public_ip = 3;
|
||||
repeated IPPort endpoints = 4;
|
||||
}
|
||||
|
||||
message UpdateMachineResponse {
|
||||
MachineInfo machine = 1;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const _ = grpc.SupportPackageIsVersion9
|
||||
const (
|
||||
Cluster_AddMachine_FullMethodName = "/api.Cluster/AddMachine"
|
||||
Cluster_ListMachines_FullMethodName = "/api.Cluster/ListMachines"
|
||||
Cluster_UpdateMachine_FullMethodName = "/api.Cluster/UpdateMachine"
|
||||
Cluster_ReserveDomain_FullMethodName = "/api.Cluster/ReserveDomain"
|
||||
Cluster_GetDomain_FullMethodName = "/api.Cluster/GetDomain"
|
||||
Cluster_ReleaseDomain_FullMethodName = "/api.Cluster/ReleaseDomain"
|
||||
@@ -34,6 +35,7 @@ const (
|
||||
type ClusterClient interface {
|
||||
AddMachine(ctx context.Context, in *AddMachineRequest, opts ...grpc.CallOption) (*AddMachineResponse, error)
|
||||
ListMachines(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ListMachinesResponse, error)
|
||||
UpdateMachine(ctx context.Context, in *UpdateMachineRequest, opts ...grpc.CallOption) (*UpdateMachineResponse, error)
|
||||
ReserveDomain(ctx context.Context, in *ReserveDomainRequest, opts ...grpc.CallOption) (*Domain, error)
|
||||
GetDomain(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Domain, error)
|
||||
ReleaseDomain(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Domain, error)
|
||||
@@ -68,6 +70,16 @@ func (c *clusterClient) ListMachines(ctx context.Context, in *emptypb.Empty, opt
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clusterClient) UpdateMachine(ctx context.Context, in *UpdateMachineRequest, opts ...grpc.CallOption) (*UpdateMachineResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateMachineResponse)
|
||||
err := c.cc.Invoke(ctx, Cluster_UpdateMachine_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clusterClient) ReserveDomain(ctx context.Context, in *ReserveDomainRequest, opts ...grpc.CallOption) (*Domain, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Domain)
|
||||
@@ -114,6 +126,7 @@ func (c *clusterClient) CreateDomainRecords(ctx context.Context, in *CreateDomai
|
||||
type ClusterServer interface {
|
||||
AddMachine(context.Context, *AddMachineRequest) (*AddMachineResponse, error)
|
||||
ListMachines(context.Context, *emptypb.Empty) (*ListMachinesResponse, error)
|
||||
UpdateMachine(context.Context, *UpdateMachineRequest) (*UpdateMachineResponse, error)
|
||||
ReserveDomain(context.Context, *ReserveDomainRequest) (*Domain, error)
|
||||
GetDomain(context.Context, *emptypb.Empty) (*Domain, error)
|
||||
ReleaseDomain(context.Context, *emptypb.Empty) (*Domain, error)
|
||||
@@ -134,6 +147,9 @@ func (UnimplementedClusterServer) AddMachine(context.Context, *AddMachineRequest
|
||||
func (UnimplementedClusterServer) ListMachines(context.Context, *emptypb.Empty) (*ListMachinesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListMachines not implemented")
|
||||
}
|
||||
func (UnimplementedClusterServer) UpdateMachine(context.Context, *UpdateMachineRequest) (*UpdateMachineResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateMachine not implemented")
|
||||
}
|
||||
func (UnimplementedClusterServer) ReserveDomain(context.Context, *ReserveDomainRequest) (*Domain, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ReserveDomain not implemented")
|
||||
}
|
||||
@@ -203,6 +219,24 @@ func _Cluster_ListMachines_Handler(srv interface{}, ctx context.Context, dec fun
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Cluster_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 {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ClusterServer).UpdateMachine(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Cluster_UpdateMachine_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClusterServer).UpdateMachine(ctx, req.(*UpdateMachineRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Cluster_ReserveDomain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ReserveDomainRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -290,6 +324,10 @@ var Cluster_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListMachines",
|
||||
Handler: _Cluster_ListMachines_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateMachine",
|
||||
Handler: _Cluster_UpdateMachine_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ReserveDomain",
|
||||
Handler: _Cluster_ReserveDomain_Handler,
|
||||
|
||||
@@ -198,6 +198,89 @@ func (c *Cluster) AddMachine(ctx context.Context, req *pb.AddMachineRequest) (*p
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateMachine updates machine configuration in the cluster.
|
||||
func (c *Cluster) UpdateMachine(ctx context.Context, req *pb.UpdateMachineRequest) (*pb.UpdateMachineResponse, error) {
|
||||
if err := c.checkInitialised(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.MachineId == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "machine_id not set")
|
||||
}
|
||||
|
||||
// Get the current machine info
|
||||
currentMachine, err := c.store.GetMachine(ctx, req.MachineId)
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrMachineNotFound) {
|
||||
return nil, status.Errorf(codes.NotFound, "machine not found: %s", req.MachineId)
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "failed to get machine: %v", err)
|
||||
}
|
||||
|
||||
// Create a copy of the current machine for updating
|
||||
updatedMachine := &pb.MachineInfo{
|
||||
Id: currentMachine.Id,
|
||||
Name: currentMachine.Name,
|
||||
Network: currentMachine.Network,
|
||||
PublicIp: currentMachine.PublicIp,
|
||||
}
|
||||
|
||||
// Apply updates from the request
|
||||
if req.Name != nil {
|
||||
// Check for empty name
|
||||
if *req.Name == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "machine name cannot be empty")
|
||||
}
|
||||
// Check for duplicate names (excluding the current machine)
|
||||
if *req.Name != currentMachine.Name {
|
||||
machines, err := c.store.ListMachines(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list machines: %v", err)
|
||||
}
|
||||
for _, m := range machines {
|
||||
if m.Id != req.MachineId && m.Name == *req.Name {
|
||||
return nil, status.Errorf(codes.AlreadyExists, "machine with name %q already exists", *req.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedMachine.Name = *req.Name
|
||||
}
|
||||
if req.PublicIp != nil {
|
||||
// Check if this is an empty IP (used to signal removal)
|
||||
if len(req.PublicIp.Ip) == 0 {
|
||||
// User wants to remove public IP
|
||||
updatedMachine.PublicIp = nil
|
||||
} else {
|
||||
// Validate and set the new IP
|
||||
ip, err := req.PublicIp.ToAddr()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid public IP: %v", err)
|
||||
}
|
||||
if !ip.IsValid() {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid public IP")
|
||||
}
|
||||
updatedMachine.PublicIp = req.PublicIp
|
||||
}
|
||||
}
|
||||
if req.Endpoints != nil {
|
||||
updatedMachine.Network.Endpoints = req.Endpoints
|
||||
}
|
||||
|
||||
// Update the machine in the store
|
||||
if err = c.store.UpdateMachine(ctx, updatedMachine); err != nil {
|
||||
if errors.Is(err, store.ErrMachineNotFound) {
|
||||
return nil, status.Errorf(codes.NotFound, "machine not found: %s", req.MachineId)
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "update machine: %v", err)
|
||||
}
|
||||
|
||||
slog.Info("Machine configuration updated in the cluster.",
|
||||
"id", updatedMachine.Id, "name", updatedMachine.Name)
|
||||
|
||||
resp := &pb.UpdateMachineResponse{Machine: updatedMachine}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ListMachines lists all machines in the cluster including their membership states.
|
||||
func (c *Cluster) ListMachines(ctx context.Context, _ *emptypb.Empty) (*pb.ListMachinesResponse, error) {
|
||||
if err := c.checkInitialised(ctx); err != nil {
|
||||
|
||||
@@ -17,6 +17,7 @@ var (
|
||||
Schema string
|
||||
|
||||
ErrKeyNotFound = errors.New("key not found")
|
||||
ErrMachineNotFound = errors.New("machine not found")
|
||||
)
|
||||
|
||||
// Store is a cluster store backed by a distributed Corrosion database.
|
||||
@@ -67,6 +68,79 @@ func (s *Store) CreateMachine(ctx context.Context, m *pb.MachineInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) UpdateMachine(ctx context.Context, m *pb.MachineInfo) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("machine info cannot be nil")
|
||||
}
|
||||
if m.Id == "" {
|
||||
return fmt.Errorf("machine ID cannot be empty")
|
||||
}
|
||||
|
||||
mJSON, err := protojson.Marshal(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal machine info: %w", err)
|
||||
}
|
||||
|
||||
result, err := s.corro.ExecContext(ctx, "UPDATE machines SET info = ? WHERE id = ?", string(mJSON), m.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update machine: %w", err)
|
||||
}
|
||||
|
||||
// Check if machine exists
|
||||
if result.RowsAffected == 0 {
|
||||
return fmt.Errorf("%w: %s", ErrMachineNotFound, m.Id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) GetMachine(ctx context.Context, machineID string) (*pb.MachineInfo, error) {
|
||||
if machineID == "" {
|
||||
return nil, fmt.Errorf("machine ID cannot be empty")
|
||||
}
|
||||
|
||||
rows, err := s.corro.QueryContext(ctx, "SELECT info FROM machines WHERE id = ?", machineID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query machine: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("query error: %w", err)
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %s", ErrMachineNotFound, machineID)
|
||||
}
|
||||
|
||||
var mJSON string
|
||||
if err = rows.Scan(&mJSON); err != nil {
|
||||
return nil, fmt.Errorf("scan machine info: %w", err)
|
||||
}
|
||||
|
||||
if mJSON == "" {
|
||||
return nil, fmt.Errorf("machine info is empty for id %s", machineID)
|
||||
}
|
||||
|
||||
protojsonParser := protojson.UnmarshalOptions{DiscardUnknown: true}
|
||||
var m pb.MachineInfo
|
||||
if err = protojsonParser.Unmarshal([]byte(mJSON), &m); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal machine info for id %s: %w", machineID, err)
|
||||
}
|
||||
|
||||
// Validate the unmarshaled data. just in case
|
||||
if m.Id != machineID {
|
||||
return nil, fmt.Errorf("machine ID mismatch: expected %s, got %s", machineID, m.Id)
|
||||
}
|
||||
|
||||
if m.Network != nil {
|
||||
if err = m.Network.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid network configuration for machine %s: %w", m.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListMachines(ctx context.Context) ([]*pb.MachineInfo, error) {
|
||||
rows, err := s.corro.QueryContext(ctx, "SELECT info FROM machines ORDER BY name")
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
@@ -46,6 +48,35 @@ func (cli *Client) ListMachines(ctx context.Context, filter *api.MachineFilter)
|
||||
return machines, nil
|
||||
}
|
||||
|
||||
// UpdateMachine updates machine configuration in the cluster.
|
||||
func (cli *Client) UpdateMachine(ctx context.Context, req *pb.UpdateMachineRequest) (*pb.MachineInfo, error) {
|
||||
resp, err := cli.ClusterClient.UpdateMachine(ctx, req)
|
||||
if err != nil {
|
||||
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
||||
return nil, api.ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return resp.Machine, nil
|
||||
}
|
||||
|
||||
// RenameMachine renames an existing machine in the cluster.
|
||||
func (cli *Client) RenameMachine(ctx context.Context, nameOrID, newName string) (*pb.MachineInfo, error) {
|
||||
// First, resolve the machine to get its ID
|
||||
machine, err := cli.InspectMachine(ctx, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update the machine with the new name
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: machine.Machine.Id,
|
||||
Name: &newName,
|
||||
}
|
||||
|
||||
return cli.UpdateMachine(ctx, req)
|
||||
}
|
||||
|
||||
func MachineMatchesFilter(machine *pb.MachineMember, filter *api.MachineFilter) bool {
|
||||
if filter == nil {
|
||||
return true
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/ucind"
|
||||
"github.com/psviderski/uncloud/pkg/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMachineRename(t *testing.T) {
|
||||
name := "ucind-test.machine-rename"
|
||||
ctx := context.Background()
|
||||
c, _ := createTestCluster(t, name, ucind.CreateClusterOptions{Machines: 3}, true)
|
||||
|
||||
cli, err := c.Machines[0].Connect(ctx)
|
||||
require.NoError(t, err)
|
||||
defer cli.Close()
|
||||
|
||||
t.Run("rename machine by name", func(t *testing.T) {
|
||||
// Get initial machine state
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
// Select the second machine to rename
|
||||
originalMachine := machines[1]
|
||||
originalName := originalMachine.Machine.Name
|
||||
// nolint:goconst
|
||||
newName := "renamed-machine-1"
|
||||
|
||||
// Rename the machine
|
||||
updatedMachine, err := cli.RenameMachine(ctx, originalName, newName)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, updatedMachine.Name)
|
||||
assert.Equal(t, originalMachine.Machine.Id, updatedMachine.Id)
|
||||
|
||||
// Verify the machine list reflects the change
|
||||
machines, err = cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
// Find the renamed machine
|
||||
var found bool
|
||||
for _, m := range machines {
|
||||
if m.Machine.Id == originalMachine.Machine.Id {
|
||||
assert.Equal(t, newName, m.Machine.Name)
|
||||
found = true
|
||||
} else {
|
||||
// Ensure other machines are unaffected
|
||||
assert.NotEqual(t, newName, m.Machine.Name)
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "Renamed machine should be in the list")
|
||||
|
||||
// Verify we can inspect the machine by its new name
|
||||
inspectedMachine, err := cli.InspectMachine(ctx, newName)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, inspectedMachine.Machine.Name)
|
||||
assert.Equal(t, originalMachine.Machine.Id, inspectedMachine.Machine.Id)
|
||||
|
||||
// Verify the old name no longer works
|
||||
_, err = cli.InspectMachine(ctx, originalName)
|
||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||
})
|
||||
|
||||
t.Run("rename machine by ID", func(t *testing.T) {
|
||||
// Get the third machine
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
// Find a machine that hasn't been renamed yet
|
||||
var targetMachine *pb.MachineMember
|
||||
for _, m := range machines {
|
||||
if m.Machine.Name != "renamed-machine-1" {
|
||||
targetMachine = m
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, targetMachine)
|
||||
|
||||
originalName := targetMachine.Machine.Name
|
||||
machineID := targetMachine.Machine.Id
|
||||
newName := "renamed-machine-2"
|
||||
|
||||
// Rename using ID instead of name
|
||||
updatedMachine, err := cli.RenameMachine(ctx, machineID, newName)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, updatedMachine.Name)
|
||||
assert.Equal(t, machineID, updatedMachine.Id)
|
||||
|
||||
// Verify the rename was successful
|
||||
inspectedMachine, err := cli.InspectMachine(ctx, newName)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, inspectedMachine.Machine.Name)
|
||||
assert.Equal(t, machineID, inspectedMachine.Machine.Id)
|
||||
|
||||
// Verify the old name no longer works
|
||||
_, err = cli.InspectMachine(ctx, originalName)
|
||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||
})
|
||||
|
||||
t.Run("rename non-existent machine", func(t *testing.T) {
|
||||
// Try to rename a machine that doesn't exist
|
||||
_, err := cli.RenameMachine(ctx, "non-existent-machine", "new-name")
|
||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||
|
||||
// Try with a non-existent ID
|
||||
_, err = cli.RenameMachine(ctx, "non-existent-id-12345", "new-name")
|
||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||
})
|
||||
|
||||
t.Run("rename to existing name", func(t *testing.T) {
|
||||
// Get current machines
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
// Try to rename machine 0 to the name of machine 1
|
||||
machine0Name := machines[0].Machine.Name
|
||||
machine1Name := machines[1].Machine.Name
|
||||
|
||||
// This should fail because the name is already taken
|
||||
_, err = cli.RenameMachine(ctx, machine0Name, machine1Name)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("rename with empty name", func(t *testing.T) {
|
||||
// Get a machine to rename
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
machineName := machines[0].Machine.Name
|
||||
|
||||
// Try to rename with empty string
|
||||
_, err = cli.RenameMachine(ctx, machineName, "")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("service continuity after rename", func(t *testing.T) {
|
||||
// Deploy a service on a specific machine
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Find a machine that hasn't been renamed to test with
|
||||
var targetMachine *pb.MachineMember
|
||||
for _, m := range machines {
|
||||
if m.Machine.Name != "renamed-machine-1" && m.Machine.Name != "renamed-machine-2" {
|
||||
targetMachine = m
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, targetMachine)
|
||||
|
||||
originalMachineName := targetMachine.Machine.Name
|
||||
serviceName := "test-service-rename-continuity"
|
||||
|
||||
// Create a service on the specific machine
|
||||
spec := api.ServiceSpec{
|
||||
Name: serviceName,
|
||||
Mode: api.ServiceModeGlobal,
|
||||
Container: api.ContainerSpec{
|
||||
Image: "portainer/pause:latest",
|
||||
},
|
||||
Placement: api.Placement{
|
||||
Machines: []string{originalMachineName},
|
||||
},
|
||||
}
|
||||
|
||||
_, err = cli.RunService(ctx, spec)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
err := cli.RemoveService(ctx, serviceName)
|
||||
if err != nil && !errors.Is(err, api.ErrNotFound) {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
// Verify service is running on the machine
|
||||
svc, err := cli.InspectService(ctx, serviceName)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, svc.Containers, 1)
|
||||
assert.Equal(t, targetMachine.Machine.Id, svc.Containers[0].MachineID)
|
||||
|
||||
// Rename the machine
|
||||
newMachineName := "renamed-for-service-test"
|
||||
_, err = cli.RenameMachine(ctx, originalMachineName, newMachineName)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify service is still running on the renamed machine
|
||||
svc, err = cli.InspectService(ctx, serviceName)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, svc.Containers, 1)
|
||||
assert.Equal(t, targetMachine.Machine.Id, svc.Containers[0].MachineID)
|
||||
|
||||
// The service spec's placement still references the old name,
|
||||
// but the service should continue to run on the same machine id
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateMachine(t *testing.T) {
|
||||
name := "ucind-test.machine-update"
|
||||
ctx := context.Background()
|
||||
c, _ := createTestCluster(t, name, ucind.CreateClusterOptions{Machines: 3}, true)
|
||||
|
||||
cli, err := c.Machines[0].Connect(ctx)
|
||||
require.NoError(t, err)
|
||||
defer cli.Close()
|
||||
|
||||
t.Run("update machine name", func(t *testing.T) {
|
||||
// Get initial machine state
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
// Select a machine to update
|
||||
targetMachine := machines[1]
|
||||
originalName := targetMachine.Machine.Name
|
||||
//TODO: From my point of view, the most correct option is to use exclusions.rules list for `_test\.go$` files
|
||||
// nolint:goconst
|
||||
newName := "updated-machine-name"
|
||||
|
||||
// Update the machine name using UpdateMachine directly
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: targetMachine.Machine.Id,
|
||||
Name: &newName,
|
||||
}
|
||||
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, updatedMachine.Name)
|
||||
assert.Equal(t, targetMachine.Machine.Id, updatedMachine.Id)
|
||||
|
||||
// Verify the change persisted
|
||||
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, inspected.Machine.Name)
|
||||
|
||||
// Verify old name no longer works
|
||||
_, err = cli.InspectMachine(ctx, originalName)
|
||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||
})
|
||||
|
||||
t.Run("update machine public IP", func(t *testing.T) {
|
||||
// Get a machine to update
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Find a machine that hasn't been renamed
|
||||
var targetMachine *pb.MachineMember
|
||||
for _, m := range machines {
|
||||
if m.Machine.Name != "updated-machine-name" {
|
||||
targetMachine = m
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, targetMachine)
|
||||
|
||||
// Create a new public IP (must be a valid public IP address)
|
||||
newPublicIP := &pb.IP{
|
||||
Ip: []byte{8, 8, 8, 8},
|
||||
}
|
||||
|
||||
// Update the public IP
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: targetMachine.Machine.Id,
|
||||
PublicIp: newPublicIP,
|
||||
}
|
||||
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newPublicIP.Ip, updatedMachine.PublicIp.Ip)
|
||||
|
||||
// Verify the change persisted
|
||||
inspected, err := cli.InspectMachine(ctx, targetMachine.Machine.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newPublicIP.Ip, inspected.Machine.PublicIp.Ip)
|
||||
})
|
||||
|
||||
t.Run("remove machine public IP", func(t *testing.T) {
|
||||
// Get machines
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, len(machines) > 0, "Need at least one machine")
|
||||
|
||||
// First, set a public IP on a machine
|
||||
targetMachine := machines[0]
|
||||
setIPReq := &pb.UpdateMachineRequest{
|
||||
MachineId: targetMachine.Machine.Id,
|
||||
PublicIp: &pb.IP{Ip: []byte{192, 0, 2, 1}}, // TEST-NET-1 address
|
||||
}
|
||||
updatedMachine, err := cli.UpdateMachine(ctx, setIPReq)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedMachine.PublicIp)
|
||||
|
||||
// Now test removing the public IP
|
||||
|
||||
// Remove the public IP by setting it to empty
|
||||
emptyIP := &pb.IP{}
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: updatedMachine.Id,
|
||||
PublicIp: emptyIP,
|
||||
}
|
||||
removedIPMachine, err := cli.UpdateMachine(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, removedIPMachine.PublicIp)
|
||||
|
||||
// Verify the change persisted
|
||||
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, inspected.Machine.PublicIp)
|
||||
})
|
||||
|
||||
t.Run("update machine endpoints", func(t *testing.T) {
|
||||
// Get a machine to update
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
var targetMachine *pb.MachineMember
|
||||
for _, m := range machines {
|
||||
targetMachine = m
|
||||
break
|
||||
}
|
||||
require.NotNil(t, targetMachine)
|
||||
|
||||
newEndpoints := []*pb.IPPort{
|
||||
{
|
||||
Ip: &pb.IP{Ip: []byte{10, 0, 0, 10}},
|
||||
Port: 8080,
|
||||
},
|
||||
{
|
||||
Ip: &pb.IP{Ip: []byte{10, 0, 0, 10}},
|
||||
Port: 8443,
|
||||
},
|
||||
}
|
||||
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: targetMachine.Machine.Id,
|
||||
Endpoints: newEndpoints,
|
||||
}
|
||||
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(newEndpoints), len(updatedMachine.Network.Endpoints))
|
||||
|
||||
// Verify endpoints were updated
|
||||
for i, endpoint := range updatedMachine.Network.Endpoints {
|
||||
assert.Equal(t, newEndpoints[i].Ip.Ip, endpoint.Ip.Ip)
|
||||
assert.Equal(t, newEndpoints[i].Port, endpoint.Port)
|
||||
}
|
||||
|
||||
// Verify other network fields remain unchanged
|
||||
assert.Equal(t, targetMachine.Machine.Network.Subnet.Ip.Ip, updatedMachine.Network.Subnet.Ip.Ip)
|
||||
assert.Equal(t, targetMachine.Machine.Network.Subnet.Bits, updatedMachine.Network.Subnet.Bits)
|
||||
assert.Equal(t, targetMachine.Machine.Network.ManagementIp.Ip, updatedMachine.Network.ManagementIp.Ip)
|
||||
assert.Equal(t, targetMachine.Machine.Network.PublicKey, updatedMachine.Network.PublicKey)
|
||||
})
|
||||
|
||||
t.Run("update multiple fields simultaneously", func(t *testing.T) {
|
||||
// Get a machine to update
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
var targetMachine *pb.MachineMember
|
||||
for _, m := range machines {
|
||||
if m.Machine.Name != "updated-machine-name" {
|
||||
targetMachine = m
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, targetMachine)
|
||||
|
||||
// Update both name and public IP
|
||||
newName := "multi-update-machine"
|
||||
newPublicIP := &pb.IP{
|
||||
Ip: []byte{1, 1, 1, 1},
|
||||
}
|
||||
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: targetMachine.Machine.Id,
|
||||
Name: &newName,
|
||||
PublicIp: newPublicIP,
|
||||
}
|
||||
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, updatedMachine.Name)
|
||||
assert.Equal(t, newPublicIP.Ip, updatedMachine.PublicIp.Ip)
|
||||
|
||||
// Verify both changes persisted
|
||||
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, inspected.Machine.Name)
|
||||
assert.Equal(t, newPublicIP.Ip, inspected.Machine.PublicIp.Ip)
|
||||
})
|
||||
|
||||
t.Run("update non-existent machine", func(t *testing.T) {
|
||||
// Try to update properties on a machine that doesn't exist
|
||||
nonExistentName := "should-be-updated"
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: "non-existent-machine-id",
|
||||
Name: &nonExistentName,
|
||||
}
|
||||
_, err := cli.UpdateMachine(ctx, req)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("update to duplicate name", func(t *testing.T) {
|
||||
// Get two machines
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, machines, 3)
|
||||
|
||||
machine1 := machines[0]
|
||||
machine2 := machines[1]
|
||||
|
||||
// Try to update machine2 with machine1's name
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: machine2.Machine.Id,
|
||||
Name: &machine1.Machine.Name,
|
||||
}
|
||||
_, err = cli.UpdateMachine(ctx, req)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("update with empty request", func(t *testing.T) {
|
||||
// Get a machine
|
||||
machines, err := cli.ListMachines(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
targetMachine := machines[0]
|
||||
|
||||
// Update with no fields set (should be a no-op)
|
||||
req := &pb.UpdateMachineRequest{
|
||||
MachineId: targetMachine.Machine.Id,
|
||||
}
|
||||
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Machine should remain unchanged
|
||||
assert.Equal(t, targetMachine.Machine.Name, updatedMachine.Name)
|
||||
if targetMachine.Machine.PublicIp != nil && updatedMachine.PublicIp != nil {
|
||||
assert.Equal(t, targetMachine.Machine.PublicIp.Ip, updatedMachine.PublicIp.Ip)
|
||||
}
|
||||
assert.Equal(t, len(targetMachine.Machine.Network.Endpoints), len(updatedMachine.Network.Endpoints))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user