Skip to content
This repository was archived by the owner on Mar 27, 2024. It is now read-only.

Option to write output to file #275

Closed
vsoch opened this issue Nov 13, 2018 · 5 comments · Fixed by #279
Closed

Option to write output to file #275

vsoch opened this issue Nov 13, 2018 · 5 comments · Fixed by #279

Comments

@vsoch
Copy link
Contributor

vsoch commented Nov 13, 2018

I've been fairly happy with piping output into file or into a string for parsing from a program (with a custom logging setting of panic and other quiet flags) but recently ran into an issue I'm having a hard time getting around - fairly challenging use when I want to capture output in a cluster environment. When I issue the command interactively I can still capture it, but on headless nodes (where it's nontrivial to give them a display) I'm getting empty strings.

So ! Instead of diving into the buggy pit of debugging the output or this one specific cluster, I think it would be more straight forward to write directly to file. This is especially appropriate for the --json flag. Is there any interest here?

@nkubala
Copy link
Contributor

nkubala commented Nov 16, 2018

@vsoch thanks for another good issue, I'm a +1 on this. let me know if you'd be interested in sending another PR :)

in https://github.com/GoogleContainerTools/container-diff/blob/master/util/format_utils.go, we should be able to modify all the methods that actually write to os.Stdout to instead write to an io.Writer be passing it to the function calls, i.e.

func TemplateOutput(diff interface{}, templateType string)

would become

func TemplateOutput(out io.Writer, diff interface{}, templateType string) error

then it's just a matter of creating the right writer from the entrypoint (parsing a flag for an output file, opening it for writing if it exists, otherwise passing os.Stdout and plumbing that down to the right places.

@vsoch
Copy link
Contributor Author

vsoch commented Nov 16, 2018

I'd again love to! I appreciate the tips on the methods and how to go about it, I'll probably be a noob for a while longer. I'll see what I can figure out from your guidance and post an update either here or via a PR.

@vsoch
Copy link
Contributor Author

vsoch commented Nov 28, 2018

hey @nkubala ! I'm struggling a little bit with where functions should be defined, and how variables should be passed (and their scope). If I define a function to "getWriter" in the utils package, this means that I need to pass the command line variable (a string for a file) into any corresponding functions in util (such as TemplateOutput, and all the other in the TemplateOutput*) family, and then that extends to
anything that uses it in util/diff_output_utils.go or the same for analyze. Is there a more logical way to have the writer defined at a higher level of scope across the utils package? I generally need some help with understanding the application flow and scope (before I make too many changes and just mess everything up). I haven't even gotten to the understanding point of "is an io.Writer something that can also be an os.Stdout) but will get there when the time comes.

@nkubala
Copy link
Contributor

nkubala commented Dec 4, 2018

hey @vsoch, sorry for the late reply here.

Generally speaking, anything that consumes a top-level CLI flag should live inside the cmd package. In this case, since we're dealing with output, you'll want to have some logic in root.go that figures out where we should be writing our output to (based on the provided flag), and sets that to a package-local variable that gets consumed by whatever is handling the actual output.

Fortunately, we already have a handy outputResults() function in root.go that is used by both diff() and analyze(), so we know that all output is going to happen through this. This function calls some stuff that lives in the util package (namely, util.Result.OutputText() and other stuff), so that function and the other ones that get called from here are the ones you're gonna wanna pass the new io.Writer to. Most of those make calls to some templating util functions (e.g. TemplateOutputFromFormat()), which are using os.Stdout as the output source. This is where you'll want to pass your newly created writer to.

is an io.Writer something that can also be an os.Stdout

Good question :) from the docs, io.Writer is an interface that has one function: Write(b []byte) (int, error). Anything that you want to use as an io.Writer needs to implement that method. In Golang, os.Stdout is just a pointer to a File which is opened at /dev/stdout on your filesystem. Fortunately, File implements the Write([]byte) (int, err) function, so we can use it as an io.Writer whenever we want to! This works perfect for us, because the other thing we want to pass instead of os.Stdout is some sort of file: so we just open up a File on /path/to/container-diff/output, pass that instead of os.Stdout, and we're good to go 👍

Hope this was helpful! We're getting into the inner workings of how Golang handles writing to different output streams here, so let me know if you need some more guidance (or if you want me to just do this 😆)

@vsoch
Copy link
Contributor Author

vsoch commented Dec 7, 2018

This is really helpful! And I'm enjoying it and learning a ton! And heck no, I want to see if I can do it, worst case scenario I'll ask for more guidance. Thank you for your help so far! I'll see if I can get a PR soon.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants