Skip to content

Commit 1988c49

Browse files
committed
Add deploy-last command
1 parent d2754b4 commit 1988c49

3 files changed

Lines changed: 198 additions & 37 deletions

File tree

spawnctl/deployLast.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"encoding/gob"
6+
"flag"
7+
"fmt"
8+
"os"
9+
10+
"github.com/pkg/errors"
11+
"github.com/urfave/cli"
12+
)
13+
14+
type deployment struct {
15+
BW2Entity string
16+
URI string
17+
Name string
18+
Configuration string
19+
Timeout string
20+
}
21+
22+
func actionDeployLast(c *cli.Context) error {
23+
sourceFile := getDeploymentHistoryFile()
24+
oldDeployments, err := readDeployments(sourceFile)
25+
if err != nil {
26+
fmt.Printf("Failed to obtain deployment history: %s", err)
27+
os.Exit(1)
28+
}
29+
currentDir, err := os.Getwd()
30+
if err != nil {
31+
fmt.Printf("Failed to get current working directory: %s", err)
32+
os.Exit(1)
33+
}
34+
35+
prevDeployment, ok := oldDeployments[currentDir]
36+
if !ok {
37+
fmt.Println("No previous deployment known for this directory")
38+
os.Exit(1)
39+
}
40+
41+
var command string
42+
if len(prevDeployment.Timeout) > 0 {
43+
command = fmt.Sprintf("spawnctl -e %s deploy -u %s -c %s -n %s -t %s", prevDeployment.BW2Entity,
44+
prevDeployment.URI, prevDeployment.Configuration, prevDeployment.Name, prevDeployment.Timeout)
45+
} else {
46+
command = fmt.Sprintf("spawnctl -e %s deploy -u %s -c %s -n %s", prevDeployment.BW2Entity,
47+
prevDeployment.URI, prevDeployment.Configuration, prevDeployment.Name)
48+
}
49+
50+
proceed := c.Bool("yes")
51+
if !proceed {
52+
fmt.Println("This will run:")
53+
fmt.Printf("\t%s\n", command)
54+
fmt.Println("Proceed? [Y/n]")
55+
reader := bufio.NewReader(os.Stdin)
56+
input, _ := reader.ReadString('\n')
57+
proceed = (input == "y\n" || input == "Y\n" || input == "\n")
58+
}
59+
60+
if proceed {
61+
flags := flag.NewFlagSet("spawnctl", 0)
62+
flags.String("uri", prevDeployment.URI, "")
63+
flags.String("configuration", prevDeployment.Configuration, "")
64+
flags.String("name", prevDeployment.Name, "")
65+
flags.String("timeout", prevDeployment.Timeout, "")
66+
c.GlobalSet("entity", prevDeployment.BW2Entity)
67+
cliCtxt := cli.NewContext(c.App, flags, c)
68+
return actionDeploy(cliCtxt)
69+
}
70+
71+
return nil
72+
}
73+
74+
func getDeploymentHistoryFile() string {
75+
histFile := os.Getenv(deploymentHistVar)
76+
//Environment variable not set, fall back to default
77+
if len(histFile) == 0 {
78+
histFile = os.Getenv("HOME") + string(os.PathSeparator) + ".spawnpoint_history"
79+
}
80+
return histFile
81+
}
82+
83+
func saveDeployment(directory string, dep deployment, fileName string) error {
84+
oldDeployments, err := readDeployments(fileName)
85+
if err != nil {
86+
return errors.Wrap(err, "Failed to read old deployments")
87+
}
88+
oldDeployments[directory] = dep
89+
if err = writeDeployments(oldDeployments, fileName); err != nil {
90+
return errors.Wrap(err, "Failed to write new deployment record")
91+
}
92+
return nil
93+
}
94+
95+
func readDeployments(fileName string) (map[string]deployment, error) {
96+
var oldDeployments map[string]deployment
97+
reader, err := os.Open(fileName)
98+
if err != nil {
99+
if !os.IsNotExist(err) {
100+
return nil, errors.Wrap(err, "Failed to open history file")
101+
}
102+
oldDeployments = make(map[string]deployment)
103+
return oldDeployments, nil
104+
}
105+
106+
defer reader.Close()
107+
decoder := gob.NewDecoder(reader)
108+
if err = decoder.Decode(&oldDeployments); err != nil {
109+
return nil, err
110+
}
111+
112+
return oldDeployments, nil
113+
}
114+
115+
func writeDeployments(deployments map[string]deployment, fileName string) error {
116+
writer, err := os.Create(fileName)
117+
if err != nil {
118+
return errors.Wrap(err, "Failed to create history file")
119+
}
120+
121+
defer writer.Close()
122+
encoder := gob.NewEncoder(writer)
123+
if err = encoder.Encode(deployments); err != nil {
124+
return errors.Wrap(err, "Failed to encode deployments")
125+
}
126+
return nil
127+
}

spawnctl/main.go

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212

1313
"github.com/SoftwareDefinedBuildings/spawnpoint/service"
1414
"github.com/SoftwareDefinedBuildings/spawnpoint/spawnclient"
15-
"github.com/SoftwareDefinedBuildings/spawnpoint/spawnd/daemon"
1615
"github.com/SoftwareDefinedBuildings/spawnpoint/spawnd/util"
1716
"github.com/pkg/errors"
1817
"github.com/urfave/cli"
1918
)
2019

20+
const deploymentHistVar = "SPAWNCTL_HISTORY_FILE"
21+
2122
func main() {
2223
app := cli.NewApp()
2324
app.Name = "spawnctl"
@@ -66,6 +67,17 @@ func main() {
6667
},
6768
},
6869
},
70+
{
71+
Name: "deploy-last",
72+
Usage: "Run the last deploy command executed from the current working directory",
73+
Action: actionDeployLast,
74+
Flags: []cli.Flag{
75+
cli.BoolFlag{
76+
Name: "yes, y",
77+
Usage: "Skip deployment confirmation",
78+
},
79+
},
80+
},
6981
{
7082
Name: "restart",
7183
Usage: "Restart a running service",
@@ -198,6 +210,19 @@ func actionDeploy(c *cli.Context) error {
198210
}
199211
}
200212

213+
currentDir, err := os.Getwd()
214+
if err != nil {
215+
fmt.Println("Warning: Unable to get current working directory to save info for deploy-last")
216+
} else if err = saveDeployment(currentDir, deployment{
217+
BW2Entity: entity,
218+
URI: spawnpointURI,
219+
Name: svcName,
220+
Configuration: cfgFile,
221+
Timeout: timeoutStr,
222+
}, getDeploymentHistoryFile()); err != nil {
223+
fmt.Println("Warning: Failed to save deployment info for deploy-last")
224+
}
225+
201226
spawnClient, err := spawnclient.New(c.GlobalString("router"), entity)
202227
if err != nil {
203228
fmt.Printf("Could not create spawnpoint client: %s\n", err)
@@ -423,39 +448,3 @@ func parseSvcConfig(configFile string) (*service.Configuration, error) {
423448

424449
return &svcConfig, nil
425450
}
426-
427-
func printSpawnpointStatus(uri string, hb *daemon.Heartbeat) {
428-
tokens := strings.Split(uri, "/")
429-
alias := tokens[len(tokens)-1]
430-
lastSeen := time.Unix(0, hb.Time)
431-
duration := time.Now().Sub(lastSeen) / (10 * time.Millisecond) * (10 * time.Millisecond)
432-
433-
fmt.Printf("[%s] seen %s (%s) ago at %s\n", alias, lastSeen.Format(time.RFC822), duration.String(), uri)
434-
fmt.Printf("Available CPU Shares: %v/%v\n", hb.AvailableCPU, hb.TotalCPU)
435-
fmt.Printf("Available Memory: %v/%v\n", hb.AvailableMemory, hb.TotalMemory)
436-
437-
fmt.Printf("%v Running Service(s)\n", len(hb.Services))
438-
for _, service := range hb.Services {
439-
fmt.Printf(" • %s\n", service)
440-
}
441-
}
442-
443-
func printSpawnpointDetails(uri string, daemonHb *daemon.Heartbeat, svcHbs map[string]daemon.ServiceHeartbeat) {
444-
tokens := strings.Split(uri, "/")
445-
alias := tokens[len(tokens)-1]
446-
lastSeen := time.Unix(0, daemonHb.Time)
447-
duration := time.Now().Sub(lastSeen) / (10 * time.Millisecond) * (10 * time.Millisecond)
448-
449-
fmt.Printf("[%s] seen %s (%s) ago at %s\n", alias, lastSeen.Format(time.RFC822), duration.String(), uri)
450-
fmt.Printf("Available CPU Shares: %v/%v\n", daemonHb.AvailableCPU, daemonHb.TotalCPU)
451-
fmt.Printf("Available Memory: %v/%v\n", daemonHb.AvailableMemory, daemonHb.TotalMemory)
452-
453-
fmt.Printf("%v Running Service(s)\n", len(daemonHb.Services))
454-
for name, svcHb := range svcHbs {
455-
lastSeen := time.Unix(0, svcHb.Time)
456-
duration := time.Now().Sub(lastSeen) / (10 * time.Millisecond) * (10 * time.Millisecond)
457-
fmt.Printf("• [%s] seen %s (%s) ago.\n", name, lastSeen.Format(time.RFC822), duration.String())
458-
fmt.Printf(" CPU: ~%.2f/%d Shares. Memory: %.2f/%d MiB\n", svcHb.UsedCPUShares, svcHb.CPUShares,
459-
svcHb.UsedMemory, svcHb.Memory)
460-
}
461-
}

spawnctl/printing.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/SoftwareDefinedBuildings/spawnpoint/spawnd/daemon"
9+
)
10+
11+
func printSpawnpointStatus(uri string, hb *daemon.Heartbeat) {
12+
tokens := strings.Split(uri, "/")
13+
alias := tokens[len(tokens)-1]
14+
lastSeen := time.Unix(0, hb.Time)
15+
duration := time.Now().Sub(lastSeen) / (10 * time.Millisecond) * (10 * time.Millisecond)
16+
17+
fmt.Printf("[%s] seen %s (%s) ago at %s\n", alias, lastSeen.Format(time.RFC822), duration.String(), uri)
18+
fmt.Printf("Available CPU Shares: %v/%v\n", hb.AvailableCPU, hb.TotalCPU)
19+
fmt.Printf("Available Memory: %v/%v\n", hb.AvailableMemory, hb.TotalMemory)
20+
21+
fmt.Printf("%v Running Service(s)\n", len(hb.Services))
22+
for _, service := range hb.Services {
23+
fmt.Printf(" • %s\n", service)
24+
}
25+
}
26+
27+
func printSpawnpointDetails(uri string, daemonHb *daemon.Heartbeat, svcHbs map[string]daemon.ServiceHeartbeat) {
28+
tokens := strings.Split(uri, "/")
29+
alias := tokens[len(tokens)-1]
30+
lastSeen := time.Unix(0, daemonHb.Time)
31+
duration := time.Now().Sub(lastSeen) / (10 * time.Millisecond) * (10 * time.Millisecond)
32+
33+
fmt.Printf("[%s] seen %s (%s) ago at %s\n", alias, lastSeen.Format(time.RFC822), duration.String(), uri)
34+
fmt.Printf("Available CPU Shares: %v/%v\n", daemonHb.AvailableCPU, daemonHb.TotalCPU)
35+
fmt.Printf("Available Memory: %v/%v\n", daemonHb.AvailableMemory, daemonHb.TotalMemory)
36+
37+
fmt.Printf("%v Running Service(s)\n", len(daemonHb.Services))
38+
for name, svcHb := range svcHbs {
39+
lastSeen := time.Unix(0, svcHb.Time)
40+
duration := time.Now().Sub(lastSeen) / (10 * time.Millisecond) * (10 * time.Millisecond)
41+
fmt.Printf("• [%s] seen %s (%s) ago.\n", name, lastSeen.Format(time.RFC822), duration.String())
42+
fmt.Printf(" CPU: ~%.2f/%d Shares. Memory: %.2f/%d MiB\n", svcHb.UsedCPUShares, svcHb.CPUShares,
43+
svcHb.UsedMemory, svcHb.Memory)
44+
}
45+
}

0 commit comments

Comments
 (0)