-
Notifications
You must be signed in to change notification settings - Fork 222
Support ps subcommand on podcvd #2814
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,8 +17,11 @@ package internal | |
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "regexp" | ||
| "sort" | ||
| "strings" | ||
| "text/tabwriter" | ||
|
|
||
| "github.com/go-playground/validator/v10" | ||
| ) | ||
|
|
@@ -91,16 +94,114 @@ func UpdateCvdGroupJsonRaw(data any, podcvdHomeDir, ipAddr string) { | |
| } | ||
| } | ||
|
|
||
| var cvdPathRegex = regexp.MustCompile(`^/var/tmp/cvd/[0-9]+/[0-9]+/home`) | ||
| func PrintPsOutputs(results []HostExecResult, out io.Writer) error { | ||
| var allRows []map[string]string | ||
| colMap := make(map[string]int) | ||
| for _, res := range results { | ||
| rows, cols, err := parsePsOutput(string(res.Stdout), res.IP) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to parse cvd ps output from group %q: %w", res.GroupName, err) | ||
| } | ||
| allRows = append(allRows, rows...) | ||
| for _, col := range cols { | ||
| if _, exists := colMap[col.name]; !exists { | ||
| colMap[col.name] = col.start | ||
| } | ||
| } | ||
| } | ||
| var colNames []string | ||
| for name := range colMap { | ||
| colNames = append(colNames, name) | ||
| } | ||
| sort.Slice(colNames, func(i, j int) bool { | ||
| return colMap[colNames[i]] < colMap[colNames[j]] | ||
| }) | ||
| return combinePsOutputs(allRows, colNames, out) | ||
| } | ||
|
|
||
| func updateStringOnCvdGroupJsonRaw(data, podcvdHomeDir, ipAddr string) string { | ||
| operatorEndpointOnHost := fmt.Sprintf("%s:%d", ipAddr, portOperatorHttpsOnHost) | ||
| func updateIPAndPortString(data, ipAddr string) string { | ||
| operatorIpAndPortOnHost := fmt.Sprintf("%s:%d", ipAddr, portOperatorHttpsOnHost) | ||
| for _, host := range []string{"0.0.0.0", "localhost", "127.0.0.1"} { | ||
| data = strings.ReplaceAll(data, fmt.Sprintf("%s:%d", host, portOperatorHttps), operatorEndpointOnHost) | ||
| data = strings.ReplaceAll(data, fmt.Sprintf("%s:%d", host, portOperatorHttps), operatorIpAndPortOnHost) | ||
| data = strings.ReplaceAll(data, host, ipAddr) | ||
| } | ||
| if cvdPathRegex.MatchString(data) { | ||
| data = cvdPathRegex.ReplaceAllString(data, podcvdHomeDir) | ||
| } | ||
| return data | ||
| } | ||
|
|
||
| var cvdPathRegex = regexp.MustCompile(`^/var/tmp/cvd/[0-9]+/[0-9]+/home`) | ||
|
|
||
| func updateStringOnCvdGroupJsonRaw(data, podcvdHomeDir, ipAddr string) string { | ||
| data = updateIPAndPortString(data, ipAddr) | ||
| return cvdPathRegex.ReplaceAllString(data, podcvdHomeDir) | ||
| } | ||
|
|
||
| type psColumn struct { | ||
| name string | ||
| start int | ||
| } | ||
|
|
||
| var psHeaderRegex = regexp.MustCompile(`\S+`) | ||
|
|
||
| func parsePsHeader(headerLine string) ([]psColumn, error) { | ||
| matches := psHeaderRegex.FindAllStringIndex(headerLine, -1) | ||
| if len(matches) == 0 { | ||
| return nil, fmt.Errorf("invalid header line %q: no column headers found", headerLine) | ||
| } | ||
| var cols []psColumn | ||
| for _, m := range matches { | ||
| cols = append(cols, psColumn{ | ||
| name: headerLine[m[0]:m[1]], | ||
| start: m[0], | ||
| }) | ||
| } | ||
| return cols, nil | ||
| } | ||
|
|
||
| func parsePsRow(line string, cols []psColumn, ipAddr string) map[string]string { | ||
| rowMap := make(map[string]string) | ||
| lineLen := len(line) | ||
| for i, col := range cols { | ||
| if col.start >= lineLen { | ||
| rowMap[col.name] = "" | ||
| continue | ||
| } | ||
| end := lineLen | ||
| if i < len(cols)-1 && cols[i+1].start < lineLen { | ||
| end = cols[i+1].start | ||
| } | ||
| rowMap[col.name] = updateIPAndPortString(strings.TrimSpace(line[col.start:end]), ipAddr) | ||
| } | ||
| return rowMap | ||
| } | ||
|
|
||
| func parsePsOutput(stdout, ipAddr string) ([]map[string]string, []psColumn, error) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is re-string parsing the human-readable output of
But before that, do podcvd commands need to be formatted a specific way? If not, you may be able to just directly feed the output of
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The only requirement is Leaving a representative example why not parsing the human-readable output can be problematic, difference on the padding.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also I should have considered potential format changes in the future. In case of having long-running Cuttlefish instance group via podcvd, its container instance has older version of cvd and there can have another running container instance with newer version of cvd perhpas. Even for such case, From |
||
| lines := strings.Split(strings.TrimSpace(stdout), "\n") | ||
| if len(lines) < 1 || lines[0] == "" { | ||
| return nil, nil, fmt.Errorf("empty cvd ps output: missing header") | ||
| } | ||
| cols, err := parsePsHeader(lines[0]) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| var rows []map[string]string | ||
| for _, line := range lines[1:] { | ||
| rows = append(rows, parsePsRow(line, cols, ipAddr)) | ||
| } | ||
| return rows, cols, nil | ||
| } | ||
|
|
||
| func combinePsOutputs(allRows []map[string]string, colNames []string, out io.Writer) error { | ||
| w := tabwriter.NewWriter(out, 0, 0, 3, ' ', 0) | ||
| fmt.Fprintln(w, strings.Join(colNames, "\t")) | ||
| for _, rowMap := range allRows { | ||
| var rowValues []string | ||
| for _, colName := range colNames { | ||
| rowValues = append(rowValues, rowMap[colName]) | ||
| } | ||
| fmt.Fprintln(w, strings.Join(rowValues, "\t")) | ||
| } | ||
| if err := w.Flush(); err != nil { | ||
| return fmt.Errorf("failed to flush output: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.