11package view
22
33import (
4+ "encoding/json"
45 "fmt"
56 "log/slog"
67 "os"
@@ -150,59 +151,57 @@ func (v *view) preValidateExec() (*[]string, string, error) {
150151 return & args , containerName , nil
151152}
152153
153- // Start session for instance
154- // aws ssm start-session --target ${instance_id}
155- func (v * view ) instanceStartSession () {
156- if v .app .kind != InstanceKind && v .app .kind != TaskKind {
157- v .app .Notice .Warn ("Invalid kind type to start session" )
158- return
154+ func (v * view ) preValidateStartSession () (* []string , string , error ) {
155+ if v .app .kind != ContainerKind && v .app .kind != InstanceKind && v .app .kind != TaskKind {
156+ return nil , "" , fmt .Errorf ("invalid kind type to start session" )
159157 }
160158
161159 if v .app .ReadOnly {
162- v .app .Notice .Warn ("No permission to start session in read only mode" )
163- return
160+ return nil , "" , fmt .Errorf ("no permission to start session in read only mode" )
164161 }
165162
166163 _ , err := exec .LookPath (awsCli )
167164 if err != nil {
168- v .app .Notice .Warnf ("failed to find %s path, please check %s" , awsCli , "https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" )
169- return
165+ return nil , "" , fmt .Errorf ("failed to find %s path, please check %s" , awsCli , "https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" )
170166 }
171167
172168 selected , err := v .getCurrentSelection ()
173169 if err != nil {
174- v .app .Notice .Warnf ("failed to handleSelected, err: %v" , err )
175- return
170+ return nil , "" , fmt .Errorf ("failed to handleSelected, err: %v" , err )
176171 }
177172
178173 instanceId := ""
179174 if v .app .kind == InstanceKind {
180175 if selected .instance == nil {
181- v .app .Notice .Warn ("Not a valid instance" )
182- return
176+ return nil , "" , fmt .Errorf ("not a valid instance" )
177+ }
178+ if selected .instance .Ec2InstanceId == nil || * selected .instance .Ec2InstanceId == "" {
179+ return nil , "" , fmt .Errorf ("not a valid instance id" )
183180 }
184181 instanceId = * selected .instance .Ec2InstanceId
185- } else if v .app .kind == TaskKind {
182+ } else if v .app .kind == ContainerKind || v . app . kind == TaskKind {
186183 if v .app .task .ContainerInstanceArn == nil {
187- v .app .Notice .Warn ("Not a valid task with container instance" )
188- return
184+ return nil , "" , fmt .Errorf ("not a valid task with container instance" )
185+ }
186+ if v .app .Store == nil {
187+ return nil , "" , fmt .Errorf ("aws store is not initialized" )
188+ }
189+ if v .app .cluster == nil || v .app .cluster .ClusterName == nil || * v .app .cluster .ClusterName == "" {
190+ return nil , "" , fmt .Errorf ("not a valid cluster" )
189191 }
190192 instanceId , err = v .app .Store .GetTaskInstanceId (v .app .cluster .ClusterName , v .app .task .ContainerInstanceArn )
191193 if err != nil {
192- v .app .Notice .Warnf ("failed to get task instance id, err: %v" , err )
193- return
194+ return nil , "" , fmt .Errorf ("failed to get task instance id, err: %v" , err )
194195 }
195196 }
196197
197198 if instanceId == "" {
198- v .app .Notice .Warn ("Not a valid instance" )
199- return
199+ return nil , "" , fmt .Errorf ("not a valid instance" )
200200 }
201201
202202 _ , err = exec .LookPath (smpCi )
203203 if err != nil {
204- v .app .Notice .Warnf ("failed to find %s path, please check %s" , smpCi , "https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" )
205- return
204+ return nil , "" , fmt .Errorf ("failed to find %s path, please check %s" , smpCi , "https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html" )
206205 }
207206
208207 args := []string {
@@ -212,16 +211,28 @@ func (v *view) instanceStartSession() {
212211 instanceId ,
213212 }
214213
214+ return & args , instanceId , nil
215+ }
216+
217+ // Start session for instance
218+ // aws ssm start-session --target ${instance_id}
219+ func (v * view ) instanceStartSession () {
220+ args , instanceId , err := v .preValidateStartSession ()
221+ if err != nil {
222+ v .app .Notice .Warnf ("Exec command validation failed: %v" , err )
223+ return
224+ }
225+
215226 // catch ctrl+C & SIGTERM
216227 interrupt := make (chan os.Signal , 1 )
217228 signal .Notify (interrupt , os .Interrupt , syscall .SIGTERM )
218229
219230 v .app .Suspend (func () {
220231 v .app .isSuspended = true
221232 bin , _ := exec .LookPath (awsCli )
222- slog .Info ("exec" , "command" , bin + " " + strings .Join (args , " " ))
233+ slog .Info ("exec" , "command" , bin + " " + strings .Join (* args , " " ))
223234
224- cmd := exec .Command (bin , args ... )
235+ cmd := exec .Command (bin , * args ... )
225236 cmd .Stdin , cmd .Stdout , cmd .Stderr = os .Stdin , os .Stdout , os .Stderr
226237 _ , err = cmd .Stdout .Write ([]byte (fmt .Sprintf (instanceBannerFmt , * v .app .cluster .ClusterName , instanceId )))
227238 err = cmd .Run ()
@@ -232,3 +243,86 @@ func (v *view) instanceStartSession() {
232243 v .app .isSuspended = false
233244 })
234245}
246+
247+ // Start session for instance
248+ // Equivalent to
249+ // aws ssm start-session
250+ // --target ecs:${cluster_id}_${task_id}_${runtime_id}
251+ // --document-name AWS-StartInteractiveCommand
252+ // --parameters {"command":["${command}"]}
253+ func (v * view ) instanceStartSessionDocument () {
254+ args , instanceId , err := v .preValidateStartSession ()
255+ if err != nil {
256+ v .app .Notice .Warnf ("Exec command validation failed: %v" , err )
257+ return
258+ }
259+
260+ selected , err := v .getCurrentSelection ()
261+ if err != nil {
262+ v .app .Notice .Warnf ("failed to handleSelected, err: %v" , err )
263+ return
264+ }
265+ runtimeId , containerName , err := validateContainerSessionTarget (selected )
266+ if err != nil {
267+ v .app .Notice .Warn (err .Error ())
268+ return
269+ }
270+ if v .app .task .TaskArn == nil || * v .app .task .TaskArn == "" {
271+ v .app .Notice .Warn ("Not a valid task" )
272+ return
273+ }
274+ if v .app .cluster .ClusterName == nil || * v .app .cluster .ClusterName == "" {
275+ v .app .Notice .Warn ("Not a valid cluster" )
276+ return
277+ }
278+
279+ ssmCommand := "docker exec -it %s %s"
280+ if v .app .Option .SsmCustomCommand != "" {
281+ ssmCommand = v .app .Option .SsmCustomCommand
282+ }
283+ params := map [string ][]string {
284+ "command" : {fmt .Sprintf (ssmCommand , runtimeId , v .app .Option .Shell )},
285+ }
286+ parameterJson , _ := json .Marshal (params )
287+
288+ extraArgs := []string {
289+ "--document-name" ,
290+ "AWS-StartInteractiveCommand" ,
291+ "--parameters" ,
292+ string (parameterJson ),
293+ }
294+
295+ cmdArgs := append (* args , extraArgs ... )
296+ // catch ctrl+C & SIGTERM
297+ interrupt := make (chan os.Signal , 1 )
298+ signal .Notify (interrupt , os .Interrupt , syscall .SIGTERM )
299+
300+ v .app .Suspend (func () {
301+ v .app .isSuspended = true
302+ bin , _ := exec .LookPath (awsCli )
303+ slog .Info ("exec" , "command" , bin + " " + strings .Join (cmdArgs , " " ))
304+
305+ cmd := exec .Command (bin , cmdArgs ... )
306+ cmd .Stdin , cmd .Stdout , cmd .Stderr = os .Stdin , os .Stdout , os .Stderr
307+ _ , err = cmd .Stdout .Write ([]byte (fmt .Sprintf (execBannerFmt , * v .app .cluster .ClusterName , instanceId , utils .ArnToName (v .app .task .TaskArn ), containerName )))
308+ err = cmd .Run ()
309+
310+ // return signal
311+ signal .Stop (interrupt )
312+ close (interrupt )
313+ v .app .isSuspended = false
314+ })
315+ }
316+
317+ func validateContainerSessionTarget (selected Entity ) (runtimeId string , containerName string , err error ) {
318+ if selected .container == nil {
319+ return "" , "" , fmt .Errorf ("not a valid container" )
320+ }
321+ if selected .container .RuntimeId == nil || * selected .container .RuntimeId == "" {
322+ return "" , "" , fmt .Errorf ("not a valid runtime id" )
323+ }
324+ if selected .container .Name == nil || * selected .container .Name == "" {
325+ return "" , "" , fmt .Errorf ("not a valid container name" )
326+ }
327+ return * selected .container .RuntimeId , * selected .container .Name , nil
328+ }
0 commit comments