1+ // Package utils provides utility functions for Cloud Foundry CLI Java plugin operations.
12package utils
23
34import (
@@ -22,6 +23,8 @@ type Version struct {
2223 Build int
2324}
2425
26+ // CFAppEnv represents the environment configuration for a Cloud Foundry application,
27+ // including environment variables, staging configuration, and system services.
2528type CFAppEnv struct {
2629 EnvironmentVariables struct {
2730 JbpConfigSpringAutoReconfiguration string `json:"JBP_CONFIG_SPRING_AUTO_RECONFIGURATION"`
@@ -78,7 +81,9 @@ type CFAppEnv struct {
7881func GenerateUUID () string {
7982 // Generate 16 random bytes
8083 bytes := make ([]byte , 16 )
81- rand .Read (bytes )
84+ if _ , err := rand .Read (bytes ); err != nil {
85+ panic (err ) // This should never happen with crypto/rand
86+ }
8287
8388 // Set version (4) and variant bits according to RFC 4122
8489 bytes [6 ] = (bytes [6 ] & 0x0f ) | 0x40 // Version 4
@@ -99,7 +104,7 @@ func readAppEnv(app string) ([]byte, error) {
99104 return nil , err
100105 }
101106
102- env , err := exec .Command ("cf" , "curl" , fmt .Sprintf ("/v3/apps/%s/env" , strings .Trim (string (guid [:] ), "\n " ))).Output ()
107+ env , err := exec .Command ("cf" , "curl" , fmt .Sprintf ("/v3/apps/%s/env" , strings .Trim (string (guid ), "\n " ))).Output ()
103108 if err != nil {
104109 return nil , err
105110 }
@@ -112,20 +117,22 @@ func checkUserPathAvailability(app string, path string) (bool, error) {
112117 return false , err
113118 }
114119
115- if strings .Contains (string (output [:] ), "exists and read-writeable" ) {
120+ if strings .Contains (string (output ), "exists and read-writeable" ) {
116121 return true , nil
117122 }
118123
119124 return false , nil
120125}
121126
127+ // FindReasonForAccessError determines the reason why accessing a Cloud Foundry app failed,
128+ // providing helpful diagnostic information and suggestions.
122129func FindReasonForAccessError (app string ) string {
123130 out , err := exec .Command ("cf" , "apps" ).Output ()
124131 if err != nil {
125132 return "cf is not logged in, please login and try again"
126133 }
127134 // Find all app names
128- lines := strings .Split (string (out [:] ), "\n " )
135+ lines := strings .Split (string (out ), "\n " )
129136 appNames := []string {}
130137 foundHeader := false
131138 for _ , line := range lines {
@@ -145,6 +152,8 @@ func FindReasonForAccessError(app string) string {
145152 return "Could not find " + app + ". Did you mean " + matches [0 ] + "?"
146153}
147154
155+ // CheckRequiredTools verifies that the necessary tools and permissions are available
156+ // for the specified Cloud Foundry application, including SSH access.
148157func CheckRequiredTools (app string ) (bool , error ) {
149158 guid , err := exec .Command ("cf" , "app" , app , "--guid" ).Output ()
150159 if err != nil {
@@ -155,7 +164,7 @@ func CheckRequiredTools(app string) (bool, error) {
155164 return false , err
156165 }
157166 var result map [string ]any
158- if err := json .Unmarshal ([] byte ( output ) , & result ); err != nil {
167+ if err := json .Unmarshal (output , & result ); err != nil {
159168 return false , err
160169 }
161170
@@ -166,6 +175,8 @@ func CheckRequiredTools(app string) (bool, error) {
166175 return true , nil
167176}
168177
178+ // GetAvailablePath determines the best available path for operations on the Cloud Foundry app,
179+ // preferring user-specified paths and falling back to volume mounts or /tmp.
169180func GetAvailablePath (data string , userpath string ) (string , error ) {
170181 if len (userpath ) > 0 {
171182 valid , _ := checkUserPathAvailability (data , userpath )
@@ -178,7 +189,7 @@ func GetAvailablePath(data string, userpath string) (string, error) {
178189
179190 env , err := readAppEnv (data )
180191 if err != nil {
181- return "/tmp" , nil
192+ return "/tmp" , err
182193 }
183194
184195 var cfAppEnv CFAppEnv
@@ -197,12 +208,18 @@ func GetAvailablePath(data string, userpath string) (string, error) {
197208 return "/tmp" , nil
198209}
199210
211+ // CopyOverCat copies a remote file to a local destination using the cf ssh command and cat.
200212func CopyOverCat (args []string , src string , dest string ) error {
201- f , err := os .OpenFile (dest , os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0o666 )
213+ f , err := os .OpenFile (dest , os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0o600 )
202214 if err != nil {
203215 return errors .New ("Error creating local file at " + dest + ". Please check that you are allowed to create files at the given local path." )
204216 }
205- defer f .Close ()
217+ defer func () {
218+ if closeErr := f .Close (); closeErr != nil {
219+ // Log the error, but don't override the main function's error
220+ fmt .Fprintf (os .Stderr , "Warning: failed to close file %s: %v\n " , dest , closeErr )
221+ }
222+ }()
206223
207224 args = append (args , "cat " + src )
208225 cat := exec .Command ("cf" , args ... )
@@ -222,6 +239,7 @@ func CopyOverCat(args []string, src string, dest string) error {
222239 return nil
223240}
224241
242+ // DeleteRemoteFile removes a file from the remote Cloud Foundry application container.
225243func DeleteRemoteFile (args []string , path string ) error {
226244 args = append (args , "rm -fr " + path )
227245 _ , err := exec .Command ("cf" , args ... ).Output ()
@@ -232,14 +250,18 @@ func DeleteRemoteFile(args []string, path string) error {
232250 return nil
233251}
234252
253+ // FindHeapDumpFile locates heap dump files (*.hprof) in the specified path on the remote container.
235254func FindHeapDumpFile (args []string , fullpath string , fspath string ) (string , error ) {
236255 return FindFile (args , fullpath , fspath , "*.hprof" )
237256}
238257
258+ // FindJFRFile locates Java Flight Recorder files (*.jfr) in the specified path on the remote container.
239259func FindJFRFile (args []string , fullpath string , fspath string ) (string , error ) {
240260 return FindFile (args , fullpath , fspath , "*.jfr" )
241261}
242262
263+ // FindFile searches for files matching the given pattern in the remote container,
264+ // returning the most recently modified file that matches.
243265func FindFile (args []string , fullpath string , fspath string , pattern string ) (string , error ) {
244266 cmd := " [ -f '" + fullpath + "' ] && echo '" + fullpath + "' || find " + fspath + " -name '" + pattern + "' -printf '%T@ %p\\ 0' | sort -zk 1nr | sed -z 's/^[^ ]* //' | tr '\\ 0' '\\ n' | head -n 1 "
245267
@@ -256,17 +278,18 @@ func FindFile(args []string, fullpath string, fspath string, pattern string) (st
256278 return "" , errors .New ("error while checking the generated file: " + errorStr )
257279 }
258280
259- return strings .Trim (string (output [:] ), "\n " ), nil
281+ return strings .Trim (string (output ), "\n " ), nil
260282}
261283
284+ // ListFiles retrieves a list of files in the specified directory on the remote container.
262285func ListFiles (args []string , path string ) ([]string , error ) {
263286 cmd := "ls " + path
264287 args = append (args , cmd )
265288 output , err := exec .Command ("cf" , args ... ).Output ()
266289 if err != nil {
267- return nil , errors .New ("error occurred while listing files: " + string (output [:] ))
290+ return nil , errors .New ("error occurred while listing files: " + string (output ))
268291 }
269- files := strings .Split (strings .Trim (string (output [:] ), "\n " ), "\n " )
292+ files := strings .Split (strings .Trim (string (output ), "\n " ), "\n " )
270293 // Filter all empty strings
271294 j := 0
272295 for _ , s := range files {
@@ -278,9 +301,9 @@ func ListFiles(args []string, path string) ([]string, error) {
278301 return files [:j ], nil
279302}
280303
281- // FuzzySearch returns up to `max ` words from `words` that are closest in
304+ // FuzzySearch returns up to `maxResults ` words from `words` that are closest in
282305// Levenshtein distance to `needle`.
283- func FuzzySearch (needle string , words []string , max int ) []string {
306+ func FuzzySearch (needle string , words []string , maxResults int ) []string {
284307 type match struct {
285308 distance int
286309 word string
@@ -298,12 +321,12 @@ func FuzzySearch(needle string, words []string, max int) []string {
298321 return matches [i ].distance < matches [j ].distance
299322 })
300323
301- if max > len (matches ) {
302- max = len (matches )
324+ if maxResults > len (matches ) {
325+ maxResults = len (matches )
303326 }
304327
305- results := make ([]string , 0 , max )
306- for i := range max {
328+ results := make ([]string , 0 , maxResults )
329+ for i := range maxResults {
307330 results = append (results , matches [i ].word )
308331 }
309332
0 commit comments