From fb29ddab2029245edac4ceaa1e811ee1f9428e4c Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Fri, 24 Oct 2025 19:02:39 -0700 Subject: [PATCH 01/14] add scaffolding, and client authentication. --- go.mod | 1 + go.sum | 1 + v1/providers/sfcompute/capabilities.go | 24 +++++++++ v1/providers/sfcompute/client.go | 73 ++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 v1/providers/sfcompute/capabilities.go create mode 100644 v1/providers/sfcompute/client.go diff --git a/go.mod b/go.mod index 652a2535..bae1143a 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/stretchr/testify v1.11.0 golang.org/x/crypto v0.41.0 gopkg.in/validator.v2 v2.0.1 + github.com/sfcompute/nodes-go v0.1.0-alpha.3 ) require ( diff --git a/go.sum b/go.sum index 64dd1293..02ac61bf 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sfcompute/nodes-go v0.1.0-alpha.3/go.mod h1:dF3O8MCxLz3FTVYhjCa876Z9O3EAM8E8fONivDpfmkM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/v1/providers/sfcompute/capabilities.go b/v1/providers/sfcompute/capabilities.go new file mode 100644 index 00000000..ac0604af --- /dev/null +++ b/v1/providers/sfcompute/capabilities.go @@ -0,0 +1,24 @@ +package v1 + +import ( + "context" + + v1 "github.com/brevdev/cloud/v1" +) + +func getSFCCapabilities() v1.Capabilities { + return v1.Capabilities{ + v1.CapabilityCreateInstance, + v1.CapabilityTerminateInstance, + v1.CapabilityCreateTerminateInstance, + // add others supported by your provider: reboot, stop/start, machine-image, tags, resize-volume, modify-firewall, etc. + } +} + +func (c *SFCClient) GetCapabilities(_ context.Context) (v1.Capabilities, error) { + return getSFCCapabilities(), nil +} + +func (c *SFCCredential) GetCapabilities(_ context.Context) (v1.Capabilities, error) { + return getSFCCapabilities(), nil +} diff --git a/v1/providers/sfcompute/client.go b/v1/providers/sfcompute/client.go new file mode 100644 index 00000000..d68e5aaa --- /dev/null +++ b/v1/providers/sfcompute/client.go @@ -0,0 +1,73 @@ +package v1 + +import ( + "context" + + v1 "github.com/brevdev/cloud/v1" + "github.com/sfcompute/nodes-go/option" + + sfcnodes "github.com/sfcompute/nodes-go" +) + +type SFCCredential struct { + RefID string + apiKey string +} + +var _ v1.CloudCredential = &SFCCredential{} + +func NewSFCCredential(refID string, apiKey string /* auth fields */) *SFCCredential { + return &SFCCredential{ + RefID: refID, + apiKey: apiKey, + // ... + } +} + +func (c *SFCCredential) GetReferenceID() string { return c.RefID } +func (c *SFCCredential) GetAPIType() v1.APIType { return v1.APITypeLocational /* or v1.APITypeGlobal */ } +func (c *SFCCredential) GetCloudProviderID() v1.CloudProviderID { + return "sfcompute" // e.g., "lambdalabs" +} +func (c *SFCCredential) GetTenantID() (string, error) { + // sfc does not have a tenant system, return empty string + return "", nil +} + +func (c *SFCCredential) MakeClient(ctx context.Context, location string) (v1.CloudClient, error) { + // Create a client configured for a given location if locational API + return NewSFCClient(c.RefID, c.apiKey /* auth fields */).MakeClient(ctx, location) +} + +// ---------------- Client ---------------- + +type SFCClient struct { + v1.NotImplCloudClient + refID string + location string + apiKey string + client sfcnodes.Client // Add this field + // add http/sdk client fields, base URLs, etc. +} + +var _ v1.CloudClient = &SFCClient{} + +func NewSFCClient(refID string, apiKey string /* auth fields */) *SFCClient { + return &SFCClient{ + refID: refID, + apiKey: apiKey, + client: sfcnodes.NewClient( + option.WithBearerToken(apiKey)), + // init http/sdk clients here + } +} + +func (c *SFCClient) GetAPIType() v1.APIType { return v1.APITypeLocational /* or Global */ } +func (c *SFCClient) GetCloudProviderID() v1.CloudProviderID { return "sfcompute" } +func (c *SFCClient) GetReferenceID() string { return c.refID } +func (c *SFCClient) GetTenantID() (string, error) { return "", nil } + +func (c *SFCClient) MakeClient(_ context.Context, location string) (v1.CloudClient, error) { + c.location = location + return c, nil +} From 15549b723aa4ab5112364d77331a37598d3e9ec8 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Mon, 27 Oct 2025 15:19:38 -0700 Subject: [PATCH 02/14] return APITypeGlobal from GetAPIType function, as SFC accounts are not tied to specific regions. --- v1/providers/sfcompute/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/client.go b/v1/providers/sfcompute/client.go index d68e5aaa..3b69a41b 100644 --- a/v1/providers/sfcompute/client.go +++ b/v1/providers/sfcompute/client.go @@ -62,7 +62,7 @@ func NewSFCClient(refID string, apiKey string /* auth fields */) *SFCClient { } } -func (c *SFCClient) GetAPIType() v1.APIType { return v1.APITypeLocational /* or Global */ } +func (c *SFCClient) GetAPIType() v1.APIType { return v1.APITypeGlobal /* or Global */ } func (c *SFCClient) GetCloudProviderID() v1.CloudProviderID { return "sfcompute" } func (c *SFCClient) GetReferenceID() string { return c.refID } func (c *SFCClient) GetTenantID() (string, error) { return "", nil } From ce73d06d056fc01f139f8bc2452d7417c32bf322 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Mon, 27 Oct 2025 15:27:00 -0700 Subject: [PATCH 03/14] fix apiKey in SFCCredential struct --- v1/providers/sfcompute/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/client.go b/v1/providers/sfcompute/client.go index 3b69a41b..fce08ca0 100644 --- a/v1/providers/sfcompute/client.go +++ b/v1/providers/sfcompute/client.go @@ -11,7 +11,7 @@ import ( type SFCCredential struct { RefID string - apiKey string + apiKey string `json:"api_key"` } var _ v1.CloudCredential = &SFCCredential{} From 008158634b2966c8e0489f8952b4a499a5afb6fe Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Tue, 28 Oct 2025 16:20:14 -0700 Subject: [PATCH 04/14] scaffolding for instance.go --- v1/providers/sfcompute/instance.go | 46 ++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 v1/providers/sfcompute/instance.go diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go new file mode 100644 index 00000000..639b9619 --- /dev/null +++ b/v1/providers/sfcompute/instance.go @@ -0,0 +1,46 @@ +package v1 + +import ( + "context" + "fmt" + + v1 "github.com/brevdev/cloud/v1" +) + +func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceAttrs) (*v1.Instance, error) { + // 1) ensure SSH key present (or inject via API) per ../docs/SECURITY.md + // 2) map attrs to provider request (location, instance type, image, tags, firewall rules if supported) + // 3) launch and return instance converted to v1.Instance + return nil, fmt.Errorf("not implemented") +} + +func (c *SFCClient) GetInstance(ctx context.Context, id v1.CloudProviderInstanceID) (*v1.Instance, error) { + return nil, fmt.Errorf("not implemented") +} + +func (c *SFCClient) ListInstances(ctx context.Context, args v1.ListInstancesArgs) ([]v1.Instance, error) { + return nil, fmt.Errorf("not implemented") +} + +func (c *SFCClient) TerminateInstance(ctx context.Context, id v1.CloudProviderInstanceID) error { + return fmt.Errorf("not implemented") +} + +// Optional if supported: +func (c *SFCClient) RebootInstance(ctx context.Context, id v1.CloudProviderInstanceID) error { + return fmt.Errorf("not implemented") +} +func (c *SFCClient) StopInstance(ctx context.Context, id v1.CloudProviderInstanceID) error { + return fmt.Errorf("not implemented") +} +func (c *SFCClient) StartInstance(ctx context.Context, id v1.CloudProviderInstanceID) error { + return fmt.Errorf("not implemented") +} + +// Merge strategies (pass-through is acceptable baseline). +func (c *SFCClient) MergeInstanceForUpdate(_ v1.Instance, newInst v1.Instance) v1.Instance { + return newInst +} +func (c *SFCClient) MergeInstanceTypeForUpdate(_ v1.InstanceType, newIt v1.InstanceType) v1.InstanceType { + return newIt +} From 138d63ddb7c7ddffcc5ac6474f594456cb9c296a Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Fri, 31 Oct 2025 12:40:06 -0700 Subject: [PATCH 05/14] add instance creation implementation with SSH key support --- v1/providers/sfcompute/instance.go | 50 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index 639b9619..c83d2300 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -2,16 +2,58 @@ package v1 import ( "context" + "encoding/base64" "fmt" + "time" v1 "github.com/brevdev/cloud/v1" + sfcnodes "github.com/sfcompute/nodes-go" + "github.com/sfcompute/nodes-go/packages/param" ) +// define function to convert string to b64 +func toBase64(s string) string { + return base64.StdEncoding.EncodeToString([]byte(s)) +} + +// define function to add ssh key to cloud init +func sshKeyCloudInit(sshKey string) string { + return toBase64(fmt.Sprintf("#cloud-config\nssh_authorized_keys:\n - %s", sshKey)) +} + func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceAttrs) (*v1.Instance, error) { - // 1) ensure SSH key present (or inject via API) per ../docs/SECURITY.md - // 2) map attrs to provider request (location, instance type, image, tags, firewall rules if supported) - // 3) launch and return instance converted to v1.Instance - return nil, fmt.Errorf("not implemented") + resp, err := c.client.Nodes.New(ctx, sfcnodes.NodeNewParams{ + CreateNodesRequest: sfcnodes.CreateNodesRequestParam{ + DesiredCount: 1, + MaxPricePerNodeHour: 1000, + Zone: attrs.Location, + ImageID: param.Opt[string]{Value: attrs.ImageID}, //this needs to point to a valid image + CloudInitUserData: param.Opt[string]{Value: sshKeyCloudInit(attrs.PublicKey)}, // encode ssh key to b64-wrapped cloud-init script + }, + }) + if err != nil { + return nil, err + } + + if len(resp.Data) == 0 { + return nil, fmt.Errorf("no nodes returned") + } + node := resp.Data[0] + + inst := &v1.Instance{ + Name: attrs.Name, + RefID: attrs.RefID, + CloudCredRefID: c.refID, + CloudID: v1.CloudProviderInstanceID(node.ID), // SFC ID + ImageID: attrs.ImageID, + InstanceType: attrs.InstanceType, + Location: attrs.Location, + CreatedAt: time.Now(), + Status: v1.Status{LifecycleStatus: v1.LifecycleStatusPending}, // or map from SDK status + InstanceTypeID: v1.InstanceTypeID(node.GPUType), + } + + return inst, nil } func (c *SFCClient) GetInstance(ctx context.Context, id v1.CloudProviderInstanceID) (*v1.Instance, error) { From 30dbe43509a0157af527fe039af10f9d06ea5715 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Mon, 3 Nov 2025 15:44:48 -0800 Subject: [PATCH 06/14] add function to map the status of a node reported from SFC API to v1.LifecycleStatus in Brev --- v1/providers/sfcompute/instance.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index c83d2300..8c1b09c7 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "fmt" + "strings" "time" v1 "github.com/brevdev/cloud/v1" @@ -21,6 +22,25 @@ func sshKeyCloudInit(sshKey string) string { return toBase64(fmt.Sprintf("#cloud-config\nssh_authorized_keys:\n - %s", sshKey)) } +func mapSFCStatus(s string) v1.LifecycleStatus { + switch strings.ToLower(s) { + case "pending", "nodefailure", "unspecified", "awaitingcapacity", "unknown", "failed": + return v1.LifecycleStatusPending + case "running": + return v1.LifecycleStatusRunning + // case "stopping": + //return v1.LifecycleStatusStopping + case "stopped": + return v1.LifecycleStatusStopped + case "terminating", "released": + return v1.LifecycleStatusTerminating + case "destroyed", "deleted": + return v1.LifecycleStatusTerminated + default: + return v1.LifecycleStatusPending + } +} + func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceAttrs) (*v1.Instance, error) { resp, err := c.client.Nodes.New(ctx, sfcnodes.NodeNewParams{ CreateNodesRequest: sfcnodes.CreateNodesRequestParam{ @@ -49,7 +69,7 @@ func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceA InstanceType: attrs.InstanceType, Location: attrs.Location, CreatedAt: time.Now(), - Status: v1.Status{LifecycleStatus: v1.LifecycleStatusPending}, // or map from SDK status + Status: v1.Status{LifecycleStatus: mapSFCStatus(fmt.Sprint(node.Status))}, // map SDK status to our lifecycle InstanceTypeID: v1.InstanceTypeID(node.GPUType), } From 778871070ea59ba366140d81712e591f1f053a4c Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Tue, 4 Nov 2025 16:03:43 -0800 Subject: [PATCH 07/14] implement GetInstance in sfcompute with node data retrieval inluding SSH Hostname / Public IP --- v1/providers/sfcompute/instance.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index 8c1b09c7..d6b1be96 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -77,7 +77,27 @@ func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceA } func (c *SFCClient) GetInstance(ctx context.Context, id v1.CloudProviderInstanceID) (*v1.Instance, error) { - return nil, fmt.Errorf("not implemented") + node, err := c.client.Nodes.Get(ctx, string(id)) + if err != nil { + panic(err.Error()) + } + + ssh, err := c.client.VMs.SSH(ctx, sfcnodes.VMSSHParams{VMID: string(id)}) + if err != nil { + panic(err.Error()) + } + + inst := &v1.Instance{ + Name: node.Name, + RefID: c.refID, + CloudCredRefID: c.refID, + CloudID: v1.CloudProviderInstanceID(node.ID), // SFC ID + PublicIP: ssh.SSHHostname, + CreatedAt: time.Unix(node.CreatedAt, 0), + Status: v1.Status{LifecycleStatus: mapSFCStatus(fmt.Sprint(node.Status))}, // map SDK status to our lifecycle + InstanceTypeID: v1.InstanceTypeID(node.GPUType), + } + return inst, nil } func (c *SFCClient) ListInstances(ctx context.Context, args v1.ListInstancesArgs) ([]v1.Instance, error) { From 60ef71977ae162e026b8d0971ce846484292eb80 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Thu, 6 Nov 2025 14:07:39 -0800 Subject: [PATCH 08/14] add TerminateInstance implementation with node release and delete logic --- v1/providers/sfcompute/instance.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index d6b1be96..2d1652b9 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -105,7 +105,17 @@ func (c *SFCClient) ListInstances(ctx context.Context, args v1.ListInstancesArgs } func (c *SFCClient) TerminateInstance(ctx context.Context, id v1.CloudProviderInstanceID) error { - return fmt.Errorf("not implemented") + // release the node first + _, errRelease := c.client.Nodes.Release(ctx, string(id)) + if errRelease != nil { + panic(errRelease.Error()) + } + // then delete the node + errDelete := c.client.Nodes.Delete(ctx, string(id)) + if errDelete != nil { + panic(errDelete.Error()) + } + return nil } // Optional if supported: From 692ccf8578174e5f77c0260f79af403a9388b8ec Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Thu, 6 Nov 2025 16:38:19 -0800 Subject: [PATCH 09/14] set default SSH port to 2222, as is standard for our platform --- v1/providers/sfcompute/instance.go | 1 + 1 file changed, 1 insertion(+) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index 2d1652b9..097a4dca 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -71,6 +71,7 @@ func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceA CreatedAt: time.Now(), Status: v1.Status{LifecycleStatus: mapSFCStatus(fmt.Sprint(node.Status))}, // map SDK status to our lifecycle InstanceTypeID: v1.InstanceTypeID(node.GPUType), + SSHPort: 2222, // we use 2222/tcp for all of our SSH ports } return inst, nil From 67f3ca425321cd0bdb3823e74f5cb68197996e5b Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Thu, 6 Nov 2025 16:53:58 -0800 Subject: [PATCH 10/14] implement GetSSHHostname for retrieving the SSH hostname of an instance in sfcompute --- v1/providers/sfcompute/instance.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index 097a4dca..5c9cd9a8 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -41,6 +41,18 @@ func mapSFCStatus(s string) v1.LifecycleStatus { } } +func (c *SFCClient) GetSSHHostname(ctx context.Context, id v1.CloudProviderInstanceID) (string, error) { + _, err := c.client.Nodes.Get(ctx, string(id)) + if err != nil { + panic(err.Error()) + } + ssh, err := c.client.VMs.SSH(ctx, sfcnodes.VMSSHParams{VMID: string(id)}) + if err != nil { + panic(err.Error()) + } + return ssh.SSHHostname, nil +} + func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceAttrs) (*v1.Instance, error) { resp, err := c.client.Nodes.New(ctx, sfcnodes.NodeNewParams{ CreateNodesRequest: sfcnodes.CreateNodesRequestParam{ From be27295f8cdbeadcbb5a5257768807714c5a4fed Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Thu, 6 Nov 2025 21:39:25 -0800 Subject: [PATCH 11/14] remove unneeded call to api for GetSSHHostname --- v1/providers/sfcompute/instance.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index 5c9cd9a8..ff147b81 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -42,10 +42,6 @@ func mapSFCStatus(s string) v1.LifecycleStatus { } func (c *SFCClient) GetSSHHostname(ctx context.Context, id v1.CloudProviderInstanceID) (string, error) { - _, err := c.client.Nodes.Get(ctx, string(id)) - if err != nil { - panic(err.Error()) - } ssh, err := c.client.VMs.SSH(ctx, sfcnodes.VMSSHParams{VMID: string(id)}) if err != nil { panic(err.Error()) From a556e6b04a201306cddf19d1bc1773a7dcbf2e57 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Fri, 7 Nov 2025 10:39:41 -0800 Subject: [PATCH 12/14] use VM ID instead of instance ID to retrieve SSH Hostname --- v1/providers/sfcompute/instance.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index ff147b81..ced0141f 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -90,8 +90,13 @@ func (c *SFCClient) GetInstance(ctx context.Context, id v1.CloudProviderInstance if err != nil { panic(err.Error()) } + var vmID string + if len(node.VMs.Data) > 0 { + vmID = node.VMs.Data[0].ID + fmt.Println(vmID) + } - ssh, err := c.client.VMs.SSH(ctx, sfcnodes.VMSSHParams{VMID: string(id)}) + ssh, err := c.client.VMs.SSH(ctx, sfcnodes.VMSSHParams{VMID: vmID}) if err != nil { panic(err.Error()) } From d6f3729a6bb18d7d359778a2b97c82d91bb57771 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Fri, 7 Nov 2025 10:54:12 -0800 Subject: [PATCH 13/14] remove get ssh hostname function --- v1/providers/sfcompute/instance.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index ced0141f..b18a6423 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -41,14 +41,6 @@ func mapSFCStatus(s string) v1.LifecycleStatus { } } -func (c *SFCClient) GetSSHHostname(ctx context.Context, id v1.CloudProviderInstanceID) (string, error) { - ssh, err := c.client.VMs.SSH(ctx, sfcnodes.VMSSHParams{VMID: string(id)}) - if err != nil { - panic(err.Error()) - } - return ssh.SSHHostname, nil -} - func (c *SFCClient) CreateInstance(ctx context.Context, attrs v1.CreateInstanceAttrs) (*v1.Instance, error) { resp, err := c.client.Nodes.New(ctx, sfcnodes.NodeNewParams{ CreateNodesRequest: sfcnodes.CreateNodesRequestParam{ From 42771b906eab1fbf7f0852a2d3138d8f178f03c7 Mon Sep 17 00:00:00 2001 From: Brian Lechthaler Date: Fri, 7 Nov 2025 13:16:44 -0800 Subject: [PATCH 14/14] implement ListInstances --- v1/providers/sfcompute/instance.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/v1/providers/sfcompute/instance.go b/v1/providers/sfcompute/instance.go index b18a6423..4cc8f7b8 100644 --- a/v1/providers/sfcompute/instance.go +++ b/v1/providers/sfcompute/instance.go @@ -107,7 +107,22 @@ func (c *SFCClient) GetInstance(ctx context.Context, id v1.CloudProviderInstance } func (c *SFCClient) ListInstances(ctx context.Context, args v1.ListInstancesArgs) ([]v1.Instance, error) { - return nil, fmt.Errorf("not implemented") + resp, err := c.client.Nodes.List(ctx, sfcnodes.NodeListParams{}) + if err != nil { + return nil, err + } + + var instances []v1.Instance + for _, node := range resp.Data { + inst, err := c.GetInstance(ctx, v1.CloudProviderInstanceID(node.ID)) + if err != nil { + return nil, err + } + if inst != nil { + instances = append(instances, *inst) + } + } + return instances, nil } func (c *SFCClient) TerminateInstance(ctx context.Context, id v1.CloudProviderInstanceID) error {