Skip to content

Comments

all: convert most Bash scripts to Go#288

Merged
enocom merged 80 commits intogoogle:masterfrom
ijt:goscripts
Aug 21, 2018
Merged

all: convert most Bash scripts to Go#288
enocom merged 80 commits intogoogle:masterfrom
ijt:goscripts

Conversation

@ijt
Copy link
Contributor

@ijt ijt commented Aug 6, 2018

Fixes #257

@googlebot googlebot added the cla: yes Google CLA has been signed! label Aug 6, 2018
@ijt
Copy link
Contributor Author

ijt commented Aug 6, 2018

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.


// The deploy program builds the Guestbook server locally and deploys it to
// GKE.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank line so it becomes package documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -0,0 +1,159 @@
// Copyright 2018 Google LLC
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I filed golang/go#26848 about this. Meanwhile, we can add // +build ignore to scripts like these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aaaand it's rejected.

)

func main() {
guestbookDir := flag.String("guestbook_dir", "..", "directory containing the guestbook example")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

".." 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

kubeCmds := [][]string{
{"kubectl", "apply", "-f", filepath.Join(tempDir, "guestbook.yaml")},
// Force pull the latest image.
{"kubectl", "scale", "--replicas", "0", filepath.Join("deployment", guestbookDir)},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument must be the literal string "deployment/guestbook". It's a Kubernetes identifier, not a file path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Build Guestbook Docker image.
log.Printf("Building %s...", imageName)
build := exec.Command("vgo", "build", "-o", "gcp/guestbook")
build.Env = []string{"GOOS=linux", "GOARCH=amd64"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Append to environment; don't replace it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

tfState := make(map[string]string)
for _, k := range keys {
cmd := exec.Command("terraform", "output", "-state", tfStatePath, k)
outb, err := cmd.CombinedOutput()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summarizing our chat: If I recall correctly, you're saying that

  1. CombinedOutput() can truncate.
  2. These commands won't be sending an overwhelming amount of output to the screen.
  3. 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.

time.Sleep(dt)
continue
}
endpoint := t.status.loadBalancer.ingress[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will succeed.. encoding/json cannot unmarshal into unexported fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
var t thing
if err := json.Unmarshal(out, t); err != nil {
dt := 5 * time.Second
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inducing sleep on getting invalid JSON output doesn't seem correct: sleep should happen after not finding an ingress point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

func deploy(guestbookDir string) error {
tfStatePath := filepath.Join(guestbookDir, "gcp", "terraform.tfstate")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're here, might as well make this a flag too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@zombiezen
Copy link
Contributor

Tangential, but have you looked at http://labix.org/pipe? It might help, but I've never used it myself.

@ijt
Copy link
Contributor Author

ijt commented Aug 8, 2018

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.

@zombiezen zombiezen requested a review from enocom August 9, 2018 21:28
@zombiezen
Copy link
Contributor

@ijt, Is this ready to review? I've added @enocom.

@ijt
Copy link
Contributor Author

ijt commented Aug 10, 2018

@zombiezen, not just yet. I still have to migrate another provision-db.sh and a test.sh script.

@ijt
Copy link
Contributor Author

ijt commented Aug 17, 2018

Okay, I made those changes. PTAL.

Copy link
Contributor

@zombiezen zombiezen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after 2 comments, but please wait for @enocom to approve as well. Please also verify your changes work on the Mac before submitting.

func main() {
log.SetFlags(0)
log.SetPrefix("aws/provision_db: ")
flags := map[string]*string{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
defer func() {
if _, err := run("docker", "kill", proxyContainerID); err != nil {
panic(fmt.Sprintf("killing docker container for proxy: %v", err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, still here.

Copy link
Contributor Author

@ijt ijt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, well, this time for real.

func main() {
log.SetFlags(0)
log.SetPrefix("aws/provision_db: ")
flags := map[string]*string{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@enocom enocom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small nit. Otherwise, LGTM.


```shell
./localdb.sh
go run localdb/main.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since running this program requires additional Go libraries, you should probably add a go get ./localdb/... instruction above to avoid confusing the reader.

@enocom enocom merged commit aa83f53 into google:master Aug 21, 2018
@ijt ijt deleted the goscripts branch August 21, 2018 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes Google CLA has been signed!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants