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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 68 additions & 127 deletions go/deploy/metald/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

"connectrpc.com/connect"
"github.com/unkeyed/unkey/go/deploy/pkg/tls"
vmprovisionerv1 "github.com/unkeyed/unkey/go/gen/proto/metal/vmprovisioner/v1"
"github.com/unkeyed/unkey/go/gen/proto/metal/vmprovisioner/v1/vmprovisionerv1connect"
metaldv1 "github.com/unkeyed/unkey/go/gen/proto/metald/v1"
"github.com/unkeyed/unkey/go/gen/proto/metald/v1/metaldv1connect"
)

// This client provides a high-level interface for metald VM operations with proper authentication
Expand All @@ -22,14 +22,7 @@ type Config struct {
// UserID is the user identifier for authentication
UserID string

// TenantID is the tenant identifier for data scoping
TenantID string

// ProjectID identifies the tenants project
ProjectID string

// EnvironmentID identifies the environment within the project
EnvironmentID string
DeploymentID string

// TLS configuration
TLSMode string // "disabled", "file", or "spiffe"
Expand All @@ -46,7 +39,7 @@ type Config struct {

// Client provides a high-level interface to metald services
type Client struct {
vmService vmprovisionerv1connect.VmServiceClient
vmService metaldv1connect.VmServiceClient
tlsProvider tls.Provider
tenantID string
projectID string
Expand Down Expand Up @@ -92,25 +85,19 @@ func New(ctx context.Context, config Config) (*Client, error) {

// Add authentication and tenant isolation transport
httpClient.Transport = &tenantTransport{
Base: httpClient.Transport,
ProjectID: config.ProjectID,
TenantID: config.TenantID,
EnvironmentID: config.EnvironmentID,
Base: httpClient.Transport,
}

// Create ConnectRPC client
vmService := vmprovisionerv1connect.NewVmServiceClient(
vmService := metaldv1connect.NewVmServiceClient(
httpClient,
config.ServerAddress,
)

return &Client{
vmService: vmService,
tlsProvider: tlsProvider,
tenantID: config.TenantID,
projectID: config.ProjectID,
environmentID: config.EnvironmentID,
serverAddr: config.ServerAddress,
vmService: vmService,
tlsProvider: tlsProvider,
serverAddr: config.ServerAddress,
}, nil
}

Expand All @@ -123,167 +110,85 @@ func (c *Client) Close() error {
}

// CreateVM creates a new virtual machine with the specified configuration
func (c *Client) CreateVM(ctx context.Context, req *CreateVMRequest) (*CreateVMResponse, error) {
// Convert to protobuf request
pbReq := &vmprovisionerv1.CreateVmRequest{
VmId: req.VMID,
Config: req.Config,
TenantId: c.tenantID,
}

resp, err := c.vmService.CreateVm(ctx, connect.NewRequest(pbReq))
func (c *Client) CreateVM(ctx context.Context, req *metaldv1.CreateVmRequest) (*metaldv1.CreateVmResponse, error) {
resp, err := c.vmService.CreateVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to create VM: %w", err)
}

return &CreateVMResponse{
VMID: resp.Msg.VmId,
State: resp.Msg.State,
}, nil
return resp.Msg, nil
}

// BootVM starts a created virtual machine
func (c *Client) BootVM(ctx context.Context, vmID string) (*BootVMResponse, error) {
req := &vmprovisionerv1.BootVmRequest{
VmId: vmID,
}
func (c *Client) BootVM(ctx context.Context, req *metaldv1.BootVmRequest) (*metaldv1.BootVmResponse, error) {

resp, err := c.vmService.BootVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to boot VM: %w", err)
}

return &BootVMResponse{
Success: resp.Msg.Success,
State: resp.Msg.State,
}, nil
return resp.Msg, nil
}

// ShutdownVM gracefully stops a running virtual machine
func (c *Client) ShutdownVM(ctx context.Context, req *ShutdownVMRequest) (*ShutdownVMResponse, error) {
pbReq := &vmprovisionerv1.ShutdownVmRequest{
VmId: req.VMID,
Force: req.Force,
TimeoutSeconds: int32(req.TimeoutSeconds),
}

resp, err := c.vmService.ShutdownVm(ctx, connect.NewRequest(pbReq))
func (c *Client) ShutdownVM(ctx context.Context, req *metaldv1.ShutdownVmRequest) (*metaldv1.ShutdownVmResponse, error) {
resp, err := c.vmService.ShutdownVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to shutdown VM: %w", err)
}

return &ShutdownVMResponse{
Success: resp.Msg.Success,
State: resp.Msg.State,
}, nil
return resp.Msg, nil
}

// DeleteVM removes a virtual machine
func (c *Client) DeleteVM(ctx context.Context, req *DeleteVMRequest) (*DeleteVMResponse, error) {
pbReq := &vmprovisionerv1.DeleteVmRequest{
VmId: req.VMID,
Force: req.Force,
}

resp, err := c.vmService.DeleteVm(ctx, connect.NewRequest(pbReq))
func (c *Client) DeleteVM(ctx context.Context, req *metaldv1.DeleteVmRequest) (*metaldv1.DeleteVmResponse, error) {
resp, err := c.vmService.DeleteVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to delete VM: %w", err)
}

return &DeleteVMResponse{
Success: resp.Msg.Success,
}, nil
return resp.Msg, nil
}

// GetVMInfo retrieves detailed information about a virtual machine
func (c *Client) GetVMInfo(ctx context.Context, vmID string) (*VMInfo, error) {
req := &vmprovisionerv1.GetVmInfoRequest{
VmId: vmID,
}

func (c *Client) GetVMInfo(ctx context.Context, req *metaldv1.GetVmInfoRequest) (*metaldv1.GetVmInfoResponse, error) {
resp, err := c.vmService.GetVmInfo(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to get VM info: %w", err)
}

return &VMInfo{
VMID: resp.Msg.VmId,
State: resp.Msg.State,
Config: resp.Msg.Config,
Metrics: resp.Msg.Metrics,
NetworkInfo: resp.Msg.NetworkInfo,
}, nil
return resp.Msg, nil
}

// ListVMs retrieves a list of virtual machines for the authenticated customer
func (c *Client) ListVMs(ctx context.Context, req *ListVMsRequest) (*ListVMsResponse, error) {
pbReq := &vmprovisionerv1.ListVmsRequest{
PageSize: req.PageSize,
PageToken: req.PageToken,
}

resp, err := c.vmService.ListVms(ctx, connect.NewRequest(pbReq))
func (c *Client) ListVMs(ctx context.Context, req *metaldv1.ListVmsRequest) (*metaldv1.ListVmsResponse, error) {
resp, err := c.vmService.ListVms(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to list VMs: %w", err)
}

return &ListVMsResponse{
VMs: resp.Msg.Vms,
NextPageToken: resp.Msg.NextPageToken,
TotalCount: resp.Msg.TotalCount,
}, nil
return resp.Msg, nil
}

// PauseVM pauses a running virtual machine
func (c *Client) PauseVM(ctx context.Context, vmID string) (*PauseVMResponse, error) {
req := &vmprovisionerv1.PauseVmRequest{
VmId: vmID,
}

func (c *Client) PauseVM(ctx context.Context, req *metaldv1.PauseVmRequest) (*metaldv1.PauseVmResponse, error) {
resp, err := c.vmService.PauseVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to pause VM: %w", err)
}

return &PauseVMResponse{
Success: resp.Msg.Success,
State: resp.Msg.State,
}, nil
return resp.Msg, nil
}

// ResumeVM resumes a paused virtual machine
func (c *Client) ResumeVM(ctx context.Context, vmID string) (*ResumeVMResponse, error) {
req := &vmprovisionerv1.ResumeVmRequest{
VmId: vmID,
}

func (c *Client) ResumeVM(ctx context.Context, req *metaldv1.ResumeVmRequest) (*metaldv1.ResumeVmResponse, error) {
resp, err := c.vmService.ResumeVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to resume VM: %w", err)
}

return &ResumeVMResponse{
Success: resp.Msg.Success,
State: resp.Msg.State,
}, nil
return resp.Msg, nil
}

// RebootVM restarts a virtual machine
func (c *Client) RebootVM(ctx context.Context, req *RebootVMRequest) (*RebootVMResponse, error) {
pbReq := &vmprovisionerv1.RebootVmRequest{
VmId: req.VMID,
Force: req.Force,
}

resp, err := c.vmService.RebootVm(ctx, connect.NewRequest(pbReq))
func (c *Client) RebootVM(ctx context.Context, req *metaldv1.RebootVmRequest) (*metaldv1.RebootVmResponse, error) {
resp, err := c.vmService.RebootVm(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to reboot VM: %w", err)
}

return &RebootVMResponse{
Success: resp.Msg.Success,
State: resp.Msg.State,
}, nil
return resp.Msg, nil
}

// GetTenantID returns the tenant ID associated with this client
Expand All @@ -296,6 +201,42 @@ func (c *Client) GetServerAddress() string {
return c.serverAddr
}

// CreateDeployment creates a new deployment with multiple VMs
func (c *Client) CreateDeployment(ctx context.Context, req *metaldv1.CreateDeploymentRequest) (*metaldv1.CreateDeploymentResponse, error) {
resp, err := c.vmService.CreateDeployment(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to create deployment: %w", err)
}
return resp.Msg, nil
}

// UpdateDeployment updates an existing deployment
func (c *Client) UpdateDeployment(ctx context.Context, req *metaldv1.UpdateDeploymentRequest) (*metaldv1.UpdateDeploymentResponse, error) {
resp, err := c.vmService.UpdateDeployment(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to update deployment: %w", err)
}
return resp.Msg, nil
}

// DeleteDeployment deletes an existing deployment
func (c *Client) DeleteDeployment(ctx context.Context, req *metaldv1.DeleteDeploymentRequest) (*metaldv1.DeleteDeploymentResponse, error) {
resp, err := c.vmService.DeleteDeployment(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to delete deployment: %w", err)
}
return resp.Msg, nil
}

// GetDeployment retrieves information about a deployment
func (c *Client) GetDeployment(ctx context.Context, req *metaldv1.GetDeploymentRequest) (*metaldv1.GetDeploymentResponse, error) {
resp, err := c.vmService.GetDeployment(ctx, connect.NewRequest(req))
if err != nil {
return nil, fmt.Errorf("failed to get deployment: %w", err)
}
return resp.Msg, nil
}

// tenantTransport adds authentication and tenant isolation headers to all requests
type tenantTransport struct {
Base http.RoundTripper
Expand Down
Loading