@@ -20,6 +20,7 @@ import (
20
20
"bufio"
21
21
"fmt"
22
22
"os"
23
+ "path/filepath"
23
24
"strings"
24
25
25
26
"github.com/NVIDIA/holodeck/api/holodeck/v1alpha1"
@@ -38,6 +39,7 @@ import (
38
39
type options struct {
39
40
provision bool
40
41
cachePath string
42
+ cacheFile string
41
43
envFile string
42
44
kubeconfig string
43
45
@@ -120,7 +122,7 @@ func (m command) run(c *cli.Context, opts *options) error {
120
122
// Create instance manager and generate unique ID
121
123
manager := instances .NewManager (m .log , opts .cachePath )
122
124
instanceID := manager .GenerateInstanceID ()
123
- opts .cachePath = manager .GetInstanceCacheFile (instanceID )
125
+ opts .cacheFile = manager .GetInstanceCacheFile (instanceID )
124
126
125
127
// Add instance ID to environment metadata
126
128
if opts .cfg .Labels == nil {
@@ -142,7 +144,7 @@ func (m command) run(c *cli.Context, opts *options) error {
142
144
// SUSE: ec2-user
143
145
opts .cfg .Spec .Username = "ubuntu"
144
146
}
145
- provider , err = aws .New (m .log , opts .cfg , opts .cachePath )
147
+ provider , err = aws .New (m .log , opts .cfg , opts .cacheFile )
146
148
if err != nil {
147
149
return err
148
150
}
@@ -161,7 +163,7 @@ func (m command) run(c *cli.Context, opts *options) error {
161
163
}
162
164
163
165
// Read cache after creating the environment
164
- opts .cache , err = jyaml.UnmarshalFromFile [v1alpha1.Environment ](opts .cachePath )
166
+ opts .cache , err = jyaml.UnmarshalFromFile [v1alpha1.Environment ](opts .cacheFile )
165
167
if err != nil {
166
168
return fmt .Errorf ("failed to read cache file: %v" , err )
167
169
}
@@ -170,15 +172,74 @@ func (m command) run(c *cli.Context, opts *options) error {
170
172
err := runProvision (m .log , opts )
171
173
if err != nil {
172
174
// Handle provisioning failure with user interaction
173
- return m .handleProvisionFailure (instanceID , opts .cachePath , err )
175
+ return m .handleProvisionFailure (instanceID , opts .cacheFile , err )
174
176
}
175
177
}
176
178
177
- m .log .Info ("\n Created instance %s" , instanceID )
179
+ // Show helpful success message with connection instructions
180
+ m .showSuccessMessage (instanceID , opts )
178
181
return nil
179
182
}
180
183
181
- func (m * command ) handleProvisionFailure (instanceID , cachePath string , provisionErr error ) error {
184
+ func (m * command ) showSuccessMessage (instanceID string , opts * options ) {
185
+ m .log .Info ("\n ✅ Successfully created instance: %s\n " , instanceID )
186
+
187
+ // Get public DNS name for AWS instances
188
+ var publicDnsName string
189
+ if opts .cfg .Spec .Provider == v1alpha1 .ProviderAWS {
190
+ for _ , p := range opts .cache .Status .Properties {
191
+ if p .Name == aws .PublicDnsName {
192
+ publicDnsName = p .Value
193
+ break
194
+ }
195
+ }
196
+ } else if opts .cfg .Spec .Provider == v1alpha1 .ProviderSSH {
197
+ publicDnsName = opts .cfg .Spec .HostUrl
198
+ }
199
+
200
+ // Show SSH connection instructions if we have a public DNS name
201
+ if publicDnsName != "" && opts .cfg .Spec .Username != "" && opts .cfg .Spec .PrivateKey != "" {
202
+ m .log .Info ("📋 SSH Connection:" )
203
+ m .log .Info (" ssh -i %s %s@%s" , opts .cfg .Spec .PrivateKey , opts .cfg .Spec .Username , publicDnsName )
204
+ m .log .Info (" (If you get permission denied, run: chmod 600 %s)\n " , opts .cfg .Spec .PrivateKey )
205
+ }
206
+
207
+ // Show kubeconfig instructions if Kubernetes was installed
208
+ switch {
209
+ case opts .cfg .Spec .Kubernetes .Install && opts .provision && opts .kubeconfig != "" :
210
+ // Only show kubeconfig instructions if provisioning was done and kubeconfig was requested
211
+ absPath , err := filepath .Abs (opts .kubeconfig )
212
+ if err != nil {
213
+ absPath = opts .kubeconfig
214
+ }
215
+
216
+ // Check if the kubeconfig file actually exists
217
+ if _ , err := os .Stat (absPath ); err == nil {
218
+ m .log .Info ("📋 Kubernetes Access:" )
219
+ m .log .Info (" Kubeconfig saved to: %s\n " , absPath )
220
+ m .log .Info (" Option 1 - Copy to default location:" )
221
+ m .log .Info (" cp %s ~/.kube/config\n " , absPath )
222
+ m .log .Info (" Option 2 - Set KUBECONFIG environment variable:" )
223
+ m .log .Info (" export KUBECONFIG=%s\n " , absPath )
224
+ m .log .Info (" Option 3 - Use with kubectl directly:" )
225
+ m .log .Info (" kubectl --kubeconfig=%s get nodes\n " , absPath )
226
+ }
227
+ case opts .cfg .Spec .Kubernetes .Install && opts .provision && (opts .cfg .Spec .Kubernetes .KubernetesInstaller == "microk8s" || opts .cfg .Spec .Kubernetes .KubernetesInstaller == "kind" ):
228
+ m .log .Info ("📋 Kubernetes Access:" )
229
+ m .log .Info (" Note: For %s, access kubeconfig on the instance after SSH\n " , opts .cfg .Spec .Kubernetes .KubernetesInstaller )
230
+ case opts .cfg .Spec .Kubernetes .Install && ! opts .provision :
231
+ m .log .Info ("📋 Kubernetes Access:" )
232
+ m .log .Info (" Note: Run with --provision flag to install Kubernetes and download kubeconfig\n " )
233
+ }
234
+
235
+ // Show next steps
236
+ m .log .Info ("📋 Next Steps:" )
237
+ m .log .Info (" - List instances: holodeck list" )
238
+ m .log .Info (" - Get instance status: holodeck status %s\n " , instanceID )
239
+ m .log .Info (" - Delete instance: holodeck delete %s" , instanceID )
240
+ }
241
+
242
+ func (m * command ) handleProvisionFailure (instanceID , cacheFile string , provisionErr error ) error {
182
243
m .log .Info ("\n ❌ Provisioning failed: %v\n " , provisionErr )
183
244
184
245
// Check if we're in a non-interactive environment
@@ -204,7 +265,9 @@ func (m *command) handleProvisionFailure(instanceID, cachePath string, provision
204
265
205
266
if response == "y" || response == "yes" {
206
267
// Delete the instance
207
- manager := instances .NewManager (m .log , cachePath )
268
+ // Extract the directory path from the cache file path
269
+ cacheDir := filepath .Dir (cacheFile )
270
+ manager := instances .NewManager (m .log , cacheDir )
208
271
if err := manager .DeleteInstance (instanceID ); err != nil {
209
272
m .log .Info ("Failed to delete instance: %v" , err )
210
273
return m .provideCleanupInstructions (instanceID , provisionErr )
@@ -275,7 +338,7 @@ func runProvision(log *logger.FunLogger, opts *options) error {
275
338
if err != nil {
276
339
return fmt .Errorf ("failed to marshal environment: %v" , err )
277
340
}
278
- if err := os .WriteFile (opts .cachePath , data , 0600 ); err != nil {
341
+ if err := os .WriteFile (opts .cacheFile , data , 0600 ); err != nil {
279
342
return fmt .Errorf ("failed to update cache file with provisioning status: %v" , err )
280
343
}
281
344
return fmt .Errorf ("failed to run provisioner: %v" , err )
@@ -287,7 +350,7 @@ func runProvision(log *logger.FunLogger, opts *options) error {
287
350
if err != nil {
288
351
return fmt .Errorf ("failed to marshal environment: %v" , err )
289
352
}
290
- if err := os .WriteFile (opts .cachePath , data , 0600 ); err != nil {
353
+ if err := os .WriteFile (opts .cacheFile , data , 0600 ); err != nil {
291
354
return fmt .Errorf ("failed to update cache file with provisioning status: %v" , err )
292
355
}
293
356
0 commit comments