all: convert most Bash scripts to Go#288
all: convert most Bash scripts to Go#288enocom merged 80 commits intogoogle:masterfrom ijt:goscripts
Conversation
|
This is a work in progress. So far it shows conversion of one script to get feedback, and then based on that I'll convert the remaining scripts. |
samples/guestbook/gcp/deploy.go
Outdated
|
|
||
| // The deploy program builds the Guestbook server locally and deploys it to | ||
| // GKE. | ||
|
|
There was a problem hiding this comment.
Remove blank line so it becomes package documentation.
samples/guestbook/gcp/deploy.go
Outdated
| @@ -0,0 +1,159 @@ | |||
| // Copyright 2018 Google LLC | |||
There was a problem hiding this comment.
Hm. If we have multiple "scripts" like this in the samples/guestbook/gcp directory, then the Go compiler is going to complain about redeclaration of main function.
There was a problem hiding this comment.
Ok, I filed golang/go#26848 about this. Meanwhile, we can add // +build ignore to scripts like these.
samples/guestbook/gcp/deploy.go
Outdated
| ) | ||
|
|
||
| func main() { | ||
| guestbookDir := flag.String("guestbook_dir", "..", "directory containing the guestbook example") |
There was a problem hiding this comment.
".." adds a new assumption around working directory that the bash script didn't have... I don't see a good way around this, but it's worth noting.
samples/guestbook/gcp/deploy.go
Outdated
| keys := []string{"project", "cluster_name", "cluster_zone", "bucket", "database_instance", "database_region", "motd_var_config", "motd_var_name"} | ||
| tfState := make(map[string]string) | ||
| for _, k := range keys { | ||
| cmd := exec.Command("terraform", "output", "-state", tfStatePath, k) |
There was a problem hiding this comment.
Ah, this is one place where we can make the Go version more efficient: use the -json flag to get this all at once and use a struct to unmarshal the object.
samples/guestbook/gcp/deploy.go
Outdated
| kubeCmds := [][]string{ | ||
| {"kubectl", "apply", "-f", filepath.Join(tempDir, "guestbook.yaml")}, | ||
| // Force pull the latest image. | ||
| {"kubectl", "scale", "--replicas", "0", filepath.Join("deployment", guestbookDir)}, |
There was a problem hiding this comment.
The argument must be the literal string "deployment/guestbook". It's a Kubernetes identifier, not a file path.
samples/guestbook/gcp/deploy.go
Outdated
| // Build Guestbook Docker image. | ||
| log.Printf("Building %s...", imageName) | ||
| build := exec.Command("vgo", "build", "-o", "gcp/guestbook") | ||
| build.Env = []string{"GOOS=linux", "GOARCH=amd64"} |
There was a problem hiding this comment.
Append to environment; don't replace it.
samples/guestbook/gcp/deploy.go
Outdated
| tfState := make(map[string]string) | ||
| for _, k := range keys { | ||
| cmd := exec.Command("terraform", "output", "-state", tfStatePath, k) | ||
| outb, err := cmd.CombinedOutput() |
There was a problem hiding this comment.
Here and elsewhere: it seems like it would be more beneficial to just connect the subprocess's stderr directly to the parent process's stderr.
There was a problem hiding this comment.
I'm skeptical about this. I usually find it's better to log a high-level summary of what's going on and either send the subcommand outputs to files or only display them when errors occur. Let's discuss.
There was a problem hiding this comment.
Summarizing our chat: If I recall correctly, you're saying that
- CombinedOutput() can truncate.
- These commands won't be sending an overwhelming amount of output to the screen.
- It will help to see progress instead of it looking like the program has hung.
I agreed to forward the command stderrs to os.Stderr, possibly revisiting the decision if the signal-to-noise ratio gets too low and there's too much output.
samples/guestbook/gcp/deploy.go
Outdated
| time.Sleep(dt) | ||
| continue | ||
| } | ||
| endpoint := t.status.loadBalancer.ingress[0] |
There was a problem hiding this comment.
I don't think this will succeed.. encoding/json cannot unmarshal into unexported fields.
samples/guestbook/gcp/deploy.go
Outdated
| } | ||
| var t thing | ||
| if err := json.Unmarshal(out, t); err != nil { | ||
| dt := 5 * time.Second |
There was a problem hiding this comment.
Inducing sleep on getting invalid JSON output doesn't seem correct: sleep should happen after not finding an ingress point.
samples/guestbook/gcp/deploy.go
Outdated
| } | ||
|
|
||
| func deploy(guestbookDir string) error { | ||
| tfStatePath := filepath.Join(guestbookDir, "gcp", "terraform.tfstate") |
There was a problem hiding this comment.
While we're here, might as well make this a flag too.
|
Tangential, but have you looked at http://labix.org/pipe? It might help, but I've never used it myself. |
|
I hadn't seen that pipe lib before. It looks handy, although I can't seem to find where it would be useful in this not-script anymore. |
This means it can be run like `go1.11beta1 run ./localdb`.
This still does not work. It just hangs on the last call to nc -z. Maybe it will work to make Go try to connect to the port instead.
This way does not hang.
The MySQL command is still failing though.
|
@zombiezen, not just yet. I still have to migrate another provision-db.sh and a test.sh script. |
|
Okay, I made those changes. PTAL. |
| func main() { | ||
| log.SetFlags(0) | ||
| log.SetPrefix("aws/provision_db: ") | ||
| flags := map[string]*string{ |
There was a problem hiding this comment.
(Here and elsewhere:) Using a map loses the compile safety of passing these variables directly. Use variables directly or perhaps a struct to group them.
| } | ||
| defer func() { | ||
| if _, err := run("docker", "kill", proxyContainerID); err != nil { | ||
| panic(fmt.Sprintf("killing docker container for proxy: %v", err)) |
ijt
left a comment
There was a problem hiding this comment.
Okay, changes are up. PTAL.
| } | ||
| defer func() { | ||
| if _, err := run("docker", "kill", proxyContainerID); err != nil { | ||
| panic(fmt.Sprintf("killing docker container for proxy: %v", err)) |
There was a problem hiding this comment.
Hrm, well, this time for real.
| func main() { | ||
| log.SetFlags(0) | ||
| log.SetPrefix("aws/provision_db: ") | ||
| flags := map[string]*string{ |
enocom
left a comment
There was a problem hiding this comment.
One small nit. Otherwise, LGTM.
|
|
||
| ```shell | ||
| ./localdb.sh | ||
| go run localdb/main.go |
There was a problem hiding this comment.
Since running this program requires additional Go libraries, you should probably add a go get ./localdb/... instruction above to avoid confusing the reader.
Fixes #257