-
Notifications
You must be signed in to change notification settings - Fork 262
Cli tool #2806
Cli tool #2806
Conversation
|
Can one of the admins verify this patch? |
squat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few changes I'd like to see. Overall, the code probably works for most uses but it needs some cleanup
BUILD.bazel
Outdated
| ) | ||
|
|
||
| exports_files(["config.tf"]) | ||
| PLATFORMS = ["aws", "azure", "gcp", "govcloud", "metal", "openstack/neutron", "vmware"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something funny happened with the rebase: these definitions are out of date with ut2-integration. We no longer define PLATFORMS in BUILD.bazel
BUILD.bazel
Outdated
| exports_files(["config.tf"]) | ||
| PLATFORMS = ["aws", "azure", "gcp", "govcloud", "metal", "openstack/neutron", "vmware"] | ||
|
|
||
| [genrule( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, these rules are no defined in examples/BUILD.bazel and Documentation/BUILD.bazel
| ], | ||
| ) | ||
|
|
||
| go_binary( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should add explicit rules for darwin and linux. can be done in a followup but it is a requirement before the task is done.
installer/cmd/tectonic/main.go
Outdated
| buildPath := *deleteClusterDir | ||
| pathStat, err := os.Stat(buildPath) | ||
| // TODO: add deeper checking of the path for having cluster state | ||
| if os.IsNotExist(err) || !pathStat.IsDir() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about any other syscall-related errors? is this error actually safe to ignore?
installer/pkg/tectonic/buildstate.go
Outdated
| scanner := bufio.NewScanner(vf) | ||
| for scanner.Scan() { | ||
| line := scanner.Text() | ||
| if strings.HasPrefix(line, "tectonic_cluster_name") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tectonic_cluster_name should be made into a constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole method is only meant to be around until we merge Daniel's config object parser. Then we scrap this and get the name from the config object.
I agree though, technically "tectonic_cluster_name" should be a constant. I also expect the compiler would optimize towards that, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Im suggesting it from code maintainability perspective.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've got a valid point. But this method is disposable and should only be in here until Daniel gets to merge his stuff in. I'll add the constant thought.
installer/pkg/workflow/workflow.go
Outdated
| Execute() error | ||
| } | ||
|
|
||
| type Metadata map[string]interface{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
likewise, these types are implementation details of the Workflowtype type and should not be exported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Making it unexported.
installer/pkg/workflow/workflow.go
Outdated
|
|
||
| type Metadata map[string]interface{} | ||
|
|
||
| type Step interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than model Step as an interface, why not model it as a function type? This way defining the list of steps in install.go would be easier and you wouldn't need to define throwaway structs just to define methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simple steps appear as overkill with this approach I know, but I can imagine more complex ones would be split across multiple functions that would want to share some state. Even then we could explicitly pass around all necessary state as arguments (functional style) and still use a function type as entrypoint.
I tend to think this form is easier to reason with for most people so I'm tempted to keep it. If you feel strongly the other way, we can switch though.
Either way, empty structs should get optimised away by the compiler (unless they are being very "original") so they should not bloat in any way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on the compiler angle. However, in order to avoid premature optimization for a potential future complexity, I would have gone with the function style first. It's possible we may need to make the change in the future, though it's also possible that if we go down the current route, we will never need the structs to carry any state and we'll have struct steps simply because we thought we'd need the complexity. Not a big problem either way. Again, not concerned about the binary performance but more about the legibility and maintainability
installer/pkg/workflow/workflow.go
Outdated
| } | ||
|
|
||
| // Context represents the state of a workflow | ||
| type WorkflowType struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type should strictly be unexported, otherwise there is no real point to exporting the interface. If the Workflow interface is the API for all other packages, then this type should be internal, i.e. an implementation that is of no concern to users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename this type; it is odd in Go to suffix any type with Type just as we do not suffix interfaces with IFace or the like. If this is truly going to be one of several implementations then I suggest that this type be named after that implementation. On the other hand, if this type is going to be the only implementation, then there should be no interface at all and this should be the only exported type from this package. In this case this type should be named simply Workflow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same dilema myself.
The reality is we (or I) don't know for sure if this is going to suffice as a sole implementation or not in the long run. That's why the interface is there and it's being referenced via the interface. I think this is the safer design choice at this point.
How about we call this implementation SimpleWorkflow for now? I'm not great with picking good names..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure call it something like basicWorkflow/simpleWorkflow but the type should really be unexported
installer/pkg/workflow/workflow.go
Outdated
| } | ||
|
|
||
| func (w WorkflowType) Execute() error { | ||
| var stepError error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just name this err?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Old habit from other more talkative languages. I'll make it 'err'.
installer/pkg/workflow/workflow.go
Outdated
|
|
||
| func (w WorkflowType) Execute() error { | ||
| var stepError error | ||
| for _, oneStep := range w.steps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's convention in Go to name the range value either the singular of the splice, i.e. step, or the first letter, i.e. s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change them.
|
@squat Can you have a second look? |
|
Looks like there may be some unnecessary commit in there. Can we rebase and drop dee61c5? that was never merged into any branch. |
installer/cmd/tectonic/main.go
Outdated
|
|
||
| var ( | ||
| dryRunFlag = kingpin.Flag("dry-run", "Just pretend, but don't do anything.").Bool() | ||
| clusterInstallCommand = kingpin.Command("install", "Create a new Tectonic cluster.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the descriptions should all end without a period or all end with a period. I prefer no period but either way is ok as long as it is consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, I'll drop the periods.
| w := workflow.NewDestroyWorkflow(*deleteClusterDir) | ||
| w.Execute() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we have a default case that shows the help?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand this right, but the Kingpin library actually produces automatic help based on those descriptions strings of each command. Here's how it looks today:
bazel-bin/installer/cmd/tectonic/darwin_amd64_stripped/tectonic --help
usage: tectonic [<flags>] <command> [<args> ...]
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
--dry-run Just pretend, but don't do anything.
Commands:
help [<command>...]
Show help.
install --config=CONFIG
Create a new Tectonic cluster.
delete [<dir>]
Delete an existing Tectonic cluster.
We could obviously add additional information to it, but I presume this is enough to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens if just execute tectonic? Will it show the help as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same help output is shown.
This is all autogenerated by the kingpin CLI framework.
$ bazel-bin/installer/cmd/tectonic/darwin_amd64_stripped/tectonic
usage: tectonic [<flags>] <command> [<args> ...]
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
--dry-run Just pretend, but don't do anything.
Commands:
help [<command>...]
Show help.
install --config=CONFIG
Create a new Tectonic cluster.
delete [<dir>]
Delete an existing Tectonic cluster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
installer/pkg/workflow/workflow.go
Outdated
| return nil | ||
| } | ||
|
|
||
| func (m *metadata) SetValue(key string, value interface{}) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods should be unexported as well. Also, why are we wrapping the map with setters and getters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two reasons I had in mind when I did this.
The practical one is avoid the ugly syntax of dereferencing the m pointer every time since this is going to be done in a lot of places. May be a personal bias though.
The other one is a bit more on the hypothetical side: make sure we don't have to sweep through the whole code if we want to swap the map out for something else.
You think we should switch to accessing the map directly?
| clusterInstallCommand = kingpin.Command("install", "Create a new Tectonic cluster.") | ||
| clusterDeleteCommand = kingpin.Command("delete", "Delete an existing Tectonic cluster.") | ||
| deleteClusterDir = clusterDeleteCommand.Arg("dir", "The name of the cluster to delete").String() | ||
| clusterConfigFlag = clusterInstallCommand.Flag("config", "Cluster specification file").Required().ExistingFile() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a . at the end of the description. like the other 😄
a060f4e to
b6b3195
Compare
|
Just for having an starting point to discuss once this gets in, it will need to be integrated with #2802 so new organic abstractions will arise and in particular for Cloud specific step logic. |
installer/pkg/tectonic/buildstate.go
Outdated
| var err error | ||
| pwd, err := os.Getwd() | ||
| if err != nil { | ||
| log.Fatalf("Failed to current directory because: %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing a word in this sentence "failed to ____ current directory..."
squat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few questions/concerns
installer/pkg/workflow/workflow.go
Outdated
|
|
||
| type Step func(*metadata) error | ||
|
|
||
| // Context represents the state of a workflow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What “Context”?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leftover. needs fixing.
installer/pkg/workflow/workflow.go
Outdated
| type Step func(*metadata) error | ||
|
|
||
| // Context represents the state of a workflow | ||
| type SimpleWorkflow struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type should be unexported. The exports type is the interface. No one outside of the workflow package should care about or depend on this type at all
| } | ||
|
|
||
| func terraformPrepareStep(m *metadata) error { | ||
| if m.statePath == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems odd to me that the steps mutate the metadata. This should happen in the install workflow constructor IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my mind that was the whole point of passing the metadata object around.
It was supposed to provide a vehicle for passing data between steps, without coupling the steps to each other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to decouple the steps then the metadata must be an invariant that is completely filled during construction. This implementation is the opposite. IMO. All steps that require the statePath metadata are now dependent on this step.
|
|
||
| func terraformApplyStep(m *metadata) error { | ||
| log.Printf("Installation is running...") | ||
| tfInit := exec.Command("terraform", "apply", tectonic.FindTemplatesForType("aws")) // TODO: get from cluster config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tfInit sounds like a carry over name from previews terraformInitStep
in a follow up PR we can probably
func runTfCommand(buildPath string, args ...string) error {
tfCommand := exec.Command("terraform", args...)
tfCommand.Dir = buildPath
tfCommand.Stdin = os.Stdin
tfCommand.Stdout = os.Stdout
tfCommand.Stderr = os.Stderr
err := tfCommand.Run()
if err != nil {
return err
}
return nil
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I deliberately chose to stay away from abstracting this just yet, because I wanted to get a feel of what the interface to the TF runner abstraction would need to look like once we have a few workflows in place.
I'll incorporate your suggestion and we'll watch how it maps to some real workflows and evolve it from there if needed.
|
Go Lint is failing due to improper commenting on a few funcs: othewise looks good for now |
squat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm. Want to squash? otherwise merge on green. we can do followups to clean up the bazel files in another pr
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
* CLI MVP * Kingpin bazel build * Bazel: per-arch targets and top-level alias
The typo is from 1b4bb62 (Cli tool, 2018-02-05, coreos/tectonic-installer#2806).
This code is descended from terraformPrepareStep, which landed in 1b4bb62 (Cli tool, 2018-02-05, coreos/tectonic-installer#2806). The workspace version was factored out into copyFile in 2b82fbe (installer: Integrate multistep cli with configuration, 2018-02-16, coreos/tectonic-installer#2960). The config-generator version was copy/pasted (or independently developed?) in 1d54899 (installer/pkg/config-generator/tls: generate root CA's with go, 2018-06-28, coreos/tectonic-installer#3316). This commit DRYs that up by pulling the duplicate code out into a shared package. I've also changed O_RDWR to O_WRONLY. The O_RDWR is originally from 1b4bb62, but we do not require the ability to read from the target file. Also add a unit test to exercise this code.
Same as original, but correctly rebased.