Skip to content

Commit 255a964

Browse files
authored
Merge pull request #167 from covexo/devspace-status-sync
Implement devspace status sync (Resolve #91)
2 parents 830317a + 7787e19 commit 255a964

2 files changed

Lines changed: 192 additions & 6 deletions

File tree

cmd/status_sync.go

Lines changed: 190 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,208 @@
11
package cmd
22

33
import (
4+
"encoding/json"
5+
"io/ioutil"
6+
"math"
7+
"os"
8+
"path/filepath"
9+
"regexp"
10+
"strconv"
11+
"strings"
12+
"time"
13+
14+
"github.com/covexo/devspace/pkg/util/log"
415
"github.com/spf13/cobra"
516
)
617

18+
var initialSyncCompleted = regexp.MustCompile(`^\[Sync\] Initial sync completed\. Processed (\d+) changes$`)
19+
var syncStopped = regexp.MustCompile(`^\[Sync\] Sync stopped$`)
20+
var downstreamChanges = regexp.MustCompile(`^\[Downstream\] Successfully processed (\d+) change\(s\)$`)
21+
var upstreamChanges = regexp.MustCompile(`^\[Upstream\] Successfully processed (\d+) change\(s\)$`)
22+
23+
type syncStatus struct {
24+
Status string
25+
Pod string
26+
Local string
27+
Container string
28+
29+
LastActivity string
30+
LastActivityTime string
31+
Error string
32+
33+
TotalChanges int
34+
}
35+
736
// RunStatusSync executes the devspace status sync commad logic
837
func (cmd *StatusCmd) RunStatusSync(cobraCmd *cobra.Command, args []string) {
9-
// config := configutil.GetConfig(false)
10-
1138
// Read syncLog
12-
/*cwd, err := os.Getwd()
39+
cwd, err := os.Getwd()
1340
if err != nil {
1441
log.Fatal(err)
1542
}
1643

17-
syncLogPath := path.Join(cwd, ".devspace", "logs", "syncLog.log")
44+
syncLogPath := filepath.Join(cwd, ".devspace", "logs", "sync.log")
1845
data, err := ioutil.ReadFile(syncLogPath)
1946
if err != nil {
20-
log.Fatalf("Couldn't read %s. Do you have a sync path configured?", syncLogPath)
47+
log.Fatalf("Couldn't read %s. Do you have a sync path configured? (check `devspace list sync`)", syncLogPath)
48+
}
49+
50+
syncMap := make(map[string]*syncStatus)
51+
lines := strings.Split(string(data), "\n")
52+
53+
for _, line := range lines {
54+
if line == "" {
55+
continue
56+
}
57+
58+
jsonMap := make(map[string]string)
59+
err = json.Unmarshal([]byte(line), &jsonMap)
60+
if err != nil {
61+
log.Fatal(err)
62+
}
63+
if isSyncJSONMapInvalid(jsonMap) {
64+
log.Fatalf("Error parsing %s: Json object is invalid %s", syncLogPath, line)
65+
}
66+
67+
err = updateSyncMap(syncMap, jsonMap)
68+
if err != nil {
69+
log.Fatal(err)
70+
}
71+
}
72+
73+
if len(syncMap) == 0 {
74+
log.Info("No sync activity found. Did you run `devspace up`?")
75+
return
76+
}
77+
78+
// Print table
79+
header := []string{
80+
"Status",
81+
"Pod",
82+
"Local",
83+
"Container",
84+
"Latest Activity",
85+
"Total Changes",
86+
}
87+
88+
values := make([][]string, 0, len(syncMap))
89+
90+
for _, status := range syncMap {
91+
latestActivity := status.LastActivity
92+
93+
if status.Error != "" {
94+
latestActivity = status.Error
95+
}
96+
97+
parsedTime, _ := time.Parse(time.RFC3339, status.LastActivityTime)
98+
latestActivity += " (" + intToTimeString(int(time.Now().Unix()-parsedTime.Unix())) + " ago)"
99+
100+
syncStatus := status.Status
101+
if syncStatus == "" {
102+
syncStatus = "Active"
103+
}
104+
105+
values = append(values, []string{
106+
syncStatus,
107+
status.Pod,
108+
status.Local,
109+
status.Container,
110+
latestActivity,
111+
strconv.Itoa(status.TotalChanges),
112+
})
113+
}
114+
115+
log.PrintTable(header, values)
116+
}
117+
118+
func intToTimeString(timeDifference int) string {
119+
days := math.Floor(float64(timeDifference) / (60.0 * 60.0 * 24.0))
120+
if days > 0 {
121+
if days == 1 {
122+
return "1 day"
123+
}
124+
125+
return strconv.Itoa(int(days)) + " days"
126+
}
127+
128+
hours := math.Floor(float64(timeDifference) / (60.0 * 60.0))
129+
if hours > 0 {
130+
if hours == 1 {
131+
return "1 hour"
132+
}
133+
134+
return strconv.Itoa(int(hours)) + " hours"
135+
}
136+
137+
minutes := math.Floor(float64(timeDifference) / 60.0)
138+
if minutes > 0 {
139+
if minutes == 1 {
140+
return "1 minute"
141+
}
142+
143+
return strconv.Itoa(int(minutes)) + " minutes"
144+
}
145+
146+
if timeDifference > 0 {
147+
if timeDifference == 1 {
148+
return "1 seconds"
149+
}
150+
151+
return strconv.Itoa(timeDifference) + " seconds"
152+
}
153+
154+
return "0 seconds"
155+
}
156+
157+
func isSyncJSONMapInvalid(jsonMap map[string]string) bool {
158+
return jsonMap["container"] == "" || jsonMap["local"] == "" || jsonMap["pod"] == "" || jsonMap["level"] == "" || jsonMap["time"] == "" || jsonMap["msg"] == ""
159+
}
160+
161+
func updateSyncMap(syncMap map[string]*syncStatus, jsonMap map[string]string) error {
162+
pod := jsonMap["pod"]
163+
local := jsonMap["local"]
164+
container := jsonMap["container"]
165+
message := jsonMap["msg"]
166+
level := jsonMap["level"]
167+
time := jsonMap["time"]
168+
169+
identifier := pod + ":" + local + ":" + container
170+
171+
if syncMap[identifier] == nil {
172+
syncMap[identifier] = &syncStatus{
173+
Pod: pod,
174+
Container: container,
175+
Local: local,
176+
}
177+
}
178+
179+
if level == "error" {
180+
syncMap[identifier].Status = "Error"
181+
syncMap[identifier].Error = message
182+
syncMap[identifier].LastActivityTime = time
183+
} else if matches := initialSyncCompleted.FindStringSubmatch(message); len(matches) == 2 {
184+
syncMap[identifier].LastActivity = "Initially transferred " + matches[1] + " changes"
185+
syncMap[identifier].LastActivityTime = time
186+
187+
changes, _ := strconv.Atoi(matches[1])
188+
syncMap[identifier].TotalChanges += changes
189+
} else if matches := downstreamChanges.FindStringSubmatch(message); len(matches) == 2 {
190+
syncMap[identifier].LastActivity = "Downloaded " + matches[1] + " changes"
191+
syncMap[identifier].LastActivityTime = time
192+
193+
changes, _ := strconv.Atoi(matches[1])
194+
syncMap[identifier].TotalChanges += changes
195+
} else if matches := upstreamChanges.FindStringSubmatch(message); len(matches) == 2 {
196+
syncMap[identifier].LastActivity = "Uploaded " + matches[1] + " changes"
197+
syncMap[identifier].LastActivityTime = time
198+
199+
changes, _ := strconv.Atoi(matches[1])
200+
syncMap[identifier].TotalChanges += changes
201+
} else if syncStopped.MatchString(message) {
202+
syncMap[identifier].Status = "Stopped"
203+
syncMap[identifier].LastActivity = "Sync stopped"
204+
syncMap[identifier].LastActivityTime = time
21205
}
22206

23-
_ = strings.Split(string(data), "\n")*/
207+
return nil
24208
}

pkg/devspace/sync/sync_config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,6 @@ func (s *SyncConfig) Stop() {
393393
}
394394
}
395395
}
396+
397+
s.Logln("[Sync] Sync stopped")
396398
}

0 commit comments

Comments
 (0)