@@ -3,74 +3,223 @@ package v1
33import (
44 "context"
55 "fmt"
6+ "strings"
67 "time"
78
9+ openapi "github.com/brevdev/cloud/internal/lambdalabs/gen/lambdalabs"
810 v1 "github.com/brevdev/compute/pkg/v1"
911)
1012
13+ const lambdaLabsTimeNameFormat = "2006-01-02-15-04-05Z07-00"
14+
1115// CreateInstance creates a new instance in Lambda Labs
1216// Supported via: POST /api/v1/instance-operations/launch
13- func (c * LambdaLabsClient ) CreateInstance (_ context.Context , attrs v1.CreateInstanceAttrs ) (* v1.Instance , error ) {
14- // TODO: Implement Lambda Labs instance creation
15- // This would typically involve:
16- // 1. Validating the instance type and location
17- // 2. Creating the instance via Lambda Labs API
18- // 3. Waiting for the instance to be ready
19- // 4. Returning the instance details
17+ func (c * LambdaLabsClient ) CreateInstance (ctx context.Context , attrs v1.CreateInstanceAttrs ) (* v1.Instance , error ) {
18+ keyPairName := attrs .RefID
19+ if attrs .KeyPairName != nil {
20+ keyPairName = * attrs .KeyPairName
21+ }
2022
21- return & v1.Instance {
22- Name : attrs .Name ,
23- RefID : attrs .RefID ,
24- CreatedAt : time .Now (),
25- CloudID : v1 .CloudProviderInstanceID ("lambda-instance-id" ), // TODO: Get from API response
26- Location : attrs .Location ,
27- SubLocation : attrs .SubLocation ,
28- InstanceType : attrs .InstanceType ,
29- ImageID : attrs .ImageID ,
30- DiskSize : attrs .DiskSize ,
31- Status : v1.Status {
32- LifecycleStatus : v1 .LifecycleStatusRunning ,
33- },
34- Tags : attrs .Tags ,
35- }, nil
23+ if attrs .PublicKey != "" {
24+ request := openapi.AddSSHKeyRequest {
25+ Name : keyPairName ,
26+ PublicKey : & attrs .PublicKey ,
27+ }
28+
29+ _ , resp , err := c .client .DefaultAPI .AddSSHKey (c .makeAuthContext (ctx )).AddSSHKeyRequest (request ).Execute ()
30+ if resp != nil {
31+ defer func () { _ = resp .Body .Close () }()
32+ }
33+ if err != nil && ! strings .Contains (err .Error (), "name must be unique" ) {
34+ return nil , fmt .Errorf ("failed to add SSH key: %w" , err )
35+ }
36+ }
37+
38+ location := attrs .Location
39+ if location == "" {
40+ location = "us-west-1"
41+ }
42+
43+ quantity := int32 (1 )
44+ request := openapi.LaunchInstanceRequest {
45+ RegionName : location ,
46+ InstanceTypeName : attrs .InstanceType ,
47+ SshKeyNames : []string {keyPairName },
48+ Quantity : & quantity ,
49+ FileSystemNames : []string {},
50+ }
51+
52+ name := fmt .Sprintf ("%s--%s" , c .GetReferenceID (), time .Now ().UTC ().Format (lambdaLabsTimeNameFormat ))
53+ if attrs .Name != "" {
54+ name = fmt .Sprintf ("%s--%s--%s" , c .GetReferenceID (), attrs .Name , time .Now ().UTC ().Format (lambdaLabsTimeNameFormat ))
55+ }
56+ request .Name = * openapi .NewNullableString (& name )
57+
58+ resp , httpResp , err := c .client .DefaultAPI .LaunchInstance (c .makeAuthContext (ctx )).LaunchInstanceRequest (request ).Execute ()
59+ if httpResp != nil {
60+ defer func () { _ = httpResp .Body .Close () }()
61+ }
62+ if err != nil {
63+ return nil , fmt .Errorf ("failed to launch instance: %w" , err )
64+ }
65+
66+ if len (resp .Data .InstanceIds ) != 1 {
67+ return nil , fmt .Errorf ("expected 1 instance ID, got %d" , len (resp .Data .InstanceIds ))
68+ }
69+
70+ instanceID := v1 .CloudProviderInstanceID (resp .Data .InstanceIds [0 ])
71+ return c .GetInstance (ctx , instanceID )
3672}
3773
3874// GetInstance retrieves an instance by ID
3975// Supported via: GET /api/v1/instances/{id}
40- func (c * LambdaLabsClient ) GetInstance (_ context.Context , _ v1.CloudProviderInstanceID ) (* v1.Instance , error ) {
41- // TODO: Implement Lambda Labs instance retrieval
42- return nil , fmt .Errorf ("not implemented" )
76+ func (c * LambdaLabsClient ) GetInstance (ctx context.Context , instanceID v1.CloudProviderInstanceID ) (* v1.Instance , error ) {
77+ resp , httpResp , err := c .client .DefaultAPI .GetInstance (c .makeAuthContext (ctx ), string (instanceID )).Execute ()
78+ if httpResp != nil {
79+ defer func () { _ = httpResp .Body .Close () }()
80+ }
81+ if err != nil {
82+ return nil , fmt .Errorf ("failed to get instance: %w" , err )
83+ }
84+
85+ return convertLambdaLabsInstanceToV1Instance (resp .Data ), nil
4386}
4487
4588// TerminateInstance terminates an instance
4689// Supported via: POST /api/v1/instance-operations/terminate
47- func (c * LambdaLabsClient ) TerminateInstance (_ context.Context , _ v1.CloudProviderInstanceID ) error {
48- // TODO: Implement Lambda Labs instance termination
49- return fmt .Errorf ("not implemented" )
90+ func (c * LambdaLabsClient ) TerminateInstance (ctx context.Context , instanceID v1.CloudProviderInstanceID ) error {
91+ request := openapi.TerminateInstanceRequest {
92+ InstanceIds : []string {string (instanceID )},
93+ }
94+
95+ _ , httpResp , err := c .client .DefaultAPI .TerminateInstance (c .makeAuthContext (ctx )).TerminateInstanceRequest (request ).Execute ()
96+ if httpResp != nil {
97+ defer func () { _ = httpResp .Body .Close () }()
98+ }
99+ if err != nil {
100+ return fmt .Errorf ("failed to terminate instance: %w" , err )
101+ }
102+
103+ return nil
50104}
51105
52106// ListInstances lists all instances
53107// Supported via: GET /api/v1/instances
54- func (c * LambdaLabsClient ) ListInstances (_ context.Context , _ v1.ListInstancesArgs ) ([]v1.Instance , error ) {
55- // TODO: Implement Lambda Labs instance listing
56- return nil , fmt .Errorf ("not implemented" )
108+ func (c * LambdaLabsClient ) ListInstances (ctx context.Context , _ v1.ListInstancesArgs ) ([]v1.Instance , error ) {
109+ resp , httpResp , err := c .client .DefaultAPI .ListInstances (c .makeAuthContext (ctx )).Execute ()
110+ if httpResp != nil {
111+ defer func () { _ = httpResp .Body .Close () }()
112+ }
113+ if err != nil {
114+ return nil , fmt .Errorf ("failed to list instances: %w" , err )
115+ }
116+
117+ instances := make ([]v1.Instance , 0 , len (resp .Data ))
118+ for _ , llInstance := range resp .Data {
119+ instance := convertLambdaLabsInstanceToV1Instance (llInstance )
120+ instances = append (instances , * instance )
121+ }
122+
123+ return instances , nil
57124}
58125
59126// RebootInstance reboots an instance
60127// Supported via: POST /api/v1/instance-operations/restart
61- func (c * LambdaLabsClient ) RebootInstance (_ context.Context , _ v1.CloudProviderInstanceID ) error {
62- // TODO: Implement Lambda Labs instance rebooting
63- return fmt .Errorf ("not implemented" )
128+ func (c * LambdaLabsClient ) RebootInstance (ctx context.Context , instanceID v1.CloudProviderInstanceID ) error {
129+ request := openapi.RestartInstanceRequest {
130+ InstanceIds : []string {string (instanceID )},
131+ }
132+
133+ _ , httpResp , err := c .client .DefaultAPI .RestartInstance (c .makeAuthContext (ctx )).RestartInstanceRequest (request ).Execute ()
134+ if httpResp != nil {
135+ defer func () { _ = httpResp .Body .Close () }()
136+ }
137+ if err != nil {
138+ return fmt .Errorf ("failed to reboot instance: %w" , err )
139+ }
140+
141+ return nil
64142}
65143
66144// MergeInstanceForUpdate merges instance data for updates
145+ func convertLambdaLabsInstanceToV1Instance (llInstance openapi.Instance ) * v1.Instance {
146+ var publicIP , privateIP , hostname , name string
147+
148+ if llInstance .Ip .IsSet () {
149+ publicIP = * llInstance .Ip .Get ()
150+ }
151+ if llInstance .PrivateIp .IsSet () {
152+ privateIP = * llInstance .PrivateIp .Get ()
153+ }
154+ if llInstance .Hostname .IsSet () {
155+ hostname = * llInstance .Hostname .Get ()
156+ }
157+ if llInstance .Name .IsSet () {
158+ name = * llInstance .Name .Get ()
159+ }
160+
161+ var cloudCredRefID string
162+ var createdAt time.Time
163+ if name != "" {
164+ parts := strings .Split (name , "--" )
165+ if len (parts ) > 0 {
166+ cloudCredRefID = parts [0 ]
167+ }
168+ if len (parts ) > 1 {
169+ createdAt , _ = time .Parse ("2006-01-02-15-04-05Z07-00" , parts [1 ])
170+ }
171+ }
172+
173+ refID := ""
174+ if len (llInstance .SshKeyNames ) > 0 {
175+ refID = llInstance .SshKeyNames [0 ]
176+ }
177+
178+ return & v1.Instance {
179+ Name : name ,
180+ RefID : refID ,
181+ CloudCredRefID : cloudCredRefID ,
182+ CreatedAt : createdAt ,
183+ CloudID : v1 .CloudProviderInstanceID (llInstance .Id ),
184+ PublicIP : publicIP ,
185+ PrivateIP : privateIP ,
186+ PublicDNS : publicIP ,
187+ Hostname : hostname ,
188+ InstanceType : llInstance .InstanceType .Name ,
189+ Status : v1.Status {
190+ LifecycleStatus : convertLambdaLabsStatusToV1Status (llInstance .Status ),
191+ },
192+ Location : llInstance .Region .Name ,
193+ SSHUser : "ubuntu" ,
194+ SSHPort : 22 ,
195+ Stoppable : false ,
196+ Rebootable : true ,
197+ }
198+ }
199+
200+ func convertLambdaLabsStatusToV1Status (status string ) v1.LifecycleStatus {
201+ switch status {
202+ case "booting" :
203+ return v1 .LifecycleStatusPending
204+ case "active" :
205+ return v1 .LifecycleStatusRunning
206+ case "terminating" :
207+ return v1 .LifecycleStatusTerminating
208+ case "terminated" :
209+ return v1 .LifecycleStatusTerminated
210+ case "error" :
211+ return v1 .LifecycleStatusFailed
212+ case "unhealthy" :
213+ return v1 .LifecycleStatusRunning
214+ default :
215+ return v1 .LifecycleStatusPending
216+ }
217+ }
218+
67219func (c * LambdaLabsClient ) MergeInstanceForUpdate (_ v1.Instance , newInst v1.Instance ) v1.Instance {
68- // TODO: Implement instance merging logic
69220 return newInst
70221}
71222
72- // MergeInstanceTypeForUpdate merges instance type data for updates
73223func (c * LambdaLabsClient ) MergeInstanceTypeForUpdate (_ v1.InstanceType , newIt v1.InstanceType ) v1.InstanceType {
74- // TODO: Implement instance type merging logic
75224 return newIt
76225}
0 commit comments