-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add functionality to gitops beta run command #2411
Add functionality to gitops beta run command #2411
Conversation
It's a WIP, will clean up error handling, vars, and comments later. This is a prototype code. It works in general (checks, connection to cluster and if Flux is installed and if not it installs Flux), but I'm not sure if it works correctly. Based on step-by-step @ozamosi 's suggestions: |
cmd/gitops/beta/run/cmd.go
Outdated
@@ -50,6 +74,266 @@ func betaRunCommandPreRunE(endpoint *string) func(*cobra.Command, []string) erro | |||
|
|||
func betaRunCommandRunE(opts *config.Options, client *resty.Client) func(*cobra.Command, []string) error { | |||
return func(cmd *cobra.Command, args []string) error { | |||
// If there is no cluster in the kube config, return an error. | |||
cfg, ctx, err := kube.RestConfig() |
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.
Does using RestConfig
work equivalently/errs the same way to the outOfClusterConfig
(@ozamosi suggested) in our case?
Or do I need to copy-paste/expose code from the outOfClusterConfig
?
How is InClusterConfig
different from outOfClusterConfig
, why int he RestConfig
we first try to create the first and if it fails the second one?
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.
They're different in that InClusterConfig connects to the kube API as if it's running inside the cluster, and outOfClusterConfig will connect as if it's running outside the cluster. This is a legacy of how all code used to work whether you called it from the web API or the CLI.
We expect this to run outside the cluster, so as far as I know InClusterConfig won't do anything, but it might end up returning a different error message in the end? But since the error you return is ErrGetKubeClient, it doesn't matter.
This should work fine 👍
cmd/gitops/beta/run/cmd.go
Outdated
} | ||
|
||
// If there is a valid connection to a cluster when the command is run, connect to the currently selected cluster. | ||
kubeClient, err := kube.NewKubeHTTPClientWithConfig(cfg, ctx) |
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 am creating a client to list objects simply, like that, and then reuse it (by passing it to installFlux
, and thus apply
and newManager
).
Do I need to create it in a more complex way with more settings, KubeConfig
, like in the Flux code?
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 frankly don't know - I would use the same client, unless we discover a reason not to.
cmd/gitops/beta/run/cmd.go
Outdated
Kind: "Namespace", | ||
}) | ||
|
||
ctx2 := context.Background() |
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 context I have from creating the RestConfig
is a string. So, I need another context here. Is it OK to use context.Background()
for getting the list of objects and passing it to installFlux
? Finally, it will be passed to ApplyAll
.
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 can use context.Background()
because we would not need to set its deadline or cancel it.
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 context I have from creating the RestConfig is a string
Yeah, that context seems to be a clusterName rather than a context
in the context module sense - I would not call that string "context", because I find it confusing.
Is it OK to use context.Background() for getting the list of objects and passing it to installFlux?
Yes. Contexts are about describing a "work unit" across threads - so you have a way to connect e.g. which web request caused a server to run which database queries, if that makes sense.
The "work unit" here is the command itself, so the command's context is the right one to attach to any other work the command generates.
cmd/gitops/beta/run/cmd.go
Outdated
ctx2 := context.Background() | ||
|
||
listOptions := ctrlclient.MatchingLabels{ | ||
coretypes.PartOfLabel: "flux", |
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.
Is it OK to use coretypes
here in a CLI command?
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.
Yes
cmd/gitops/beta/run/cmd.go
Outdated
return fmt.Errorf("couldn't create file %+v", err) | ||
} | ||
|
||
err = os.WriteFile(filepath.Join(filePath, shortFilename), content, 0666) |
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.
Am I making the dir and writing the file with the correct permission?
And should I use the gitops command directory or some working directory. If the latter, how should I get it. (I think I heard smth. mentioned about the working directory during the last standup but it was about another issue.)
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.
Besides, do we still need to write the file if we are not actually reading the file for applying it to the cluster and are just passing the manifest's content (which is in memory in a var) to apply
.
Or do we need to read the manifest contents from file/will the file be read later?
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.
Am I making the dir and writing the file with the correct permission?
Let's use 0775 and 0664 to remove world write permissions.
And should I use the gitops command directory or some working directory
Eventually, we need logic to find the root git directory and use that as a default, then add a command line option to override that, and then use that directory. But that's out of scope for this PR.
I think using os.Getwd
here is enough to demonstrate it works.
do we still need to write the file if we are not actually reading the file
Yes - when you've finished running this command, you should be able to just run git commit -a; git push
and have that give you the same git repository that flux bootstrap
would have.
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.
hmm. I think this behavour + the locaton of Flux's manifest need refinement.
cmd/gitops/beta/run/cmd.go
Outdated
// kubeclientOptions.BindFlags(rootCmd.PersistentFlags()) | ||
|
||
// rootCmd.RegisterFlagCompletionFunc("context", contextsCompletionFunc) | ||
// rootCmd.RegisterFlagCompletionFunc("namespace", resourceNamesCompletionFunc(corev1.SchemeGroupVersion.WithKind("Namespace"))) |
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 should I do with global flags (if any of those supported by the gitops
command are provided) in the run
command here? I've just copy-pasted and commented out code from the Flux instal command.
Which of this or original gitops
flags do I need to add here?
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.
Oh god, do I have to make decisions 😱
So, I looked at what flags flux install --help
and gitops --help
prints out and tried to look at what's important Note: this is written without looking at the code, so if I say something that is not fairly straight-forward copy-paste, then the correct answer is "that's very interesting Robin, I suggest you make a ticket if you feel strongly about it, because that's not part of this ticket's description".
--version
makes sense - especially as that'd let us set and print a default in the help text, so it's possible to figure out what we'd do. We should have a default set at compiletime in pkg/version/version.go
already. Maybe it should be called --flux-version
instead here, though.
I think maybe --components-extra
and --components
would be useful as well, again as a bit of documentation in the help text what the thing will do.
There's already a global --namespace
in gitops, so we should use that here - especially since I've come to grow opinions about namespaces and gitops recently 😊
We need a timeout set to something or install doesn't work, and what I hard-coded isn't a good timeout. Let's just add a --timeout
flag?
--insecure-skip-tls-verify
is a global arg in both gitops and flux, is it easy to hook up?
The rest of the global gitops flags are irrelevant.
There's loads of global flux flags which I'm sure are important to some people (personally, I really want --context
), but they all require changes to the kube config/client bits, so I think they're out of scope for now.
cmd/gitops/beta/run/cmd.go
Outdated
|
||
var kubeconfigArgs = genericclioptions.NewConfigFlags(false) | ||
|
||
var kubeclientOptions = new(runclient.Options) |
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 am not sure should configure kubeconfigArgs
and kubeclientOptions
here. This is code I copy-pasted from Flux and simplified.
What exactly should I configure here for our purpose?
cmd/gitops/beta/run/cmd.go
Outdated
return fmt.Errorf("couldn't write flux manifests to file %+v", err) | ||
} | ||
|
||
rf := rootFlags{ |
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 remove everything related to root flags, copy-pasted and I think here this code is not needed, we already used called MakeDefaultOptions
when generating the manifests.
cmd/gitops/beta/run/cmd.go
Outdated
return nil | ||
} | ||
|
||
type rootFlags 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.
Will be removed.
cmd/gitops/beta/run/cmd.go
Outdated
} | ||
|
||
func newManager(kubeClient ctrlclient.Client, rcg genericclioptions.RESTClientGetter) (*ssa.ResourceManager, error) { | ||
restMapper, err := rcg.ToRESTMapper() |
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.
See above the question about the kube client. Can I re-use the passed kube client here or should I create a new config and client here, like in the Flux code in func newManager
?
cmd/gitops/beta/run/cmd.go
Outdated
return man.WaitForSet(changeSet.ToObjMetadataSet(), ssa.WaitOptions{Interval: 2 * time.Second, Timeout: time.Minute}) | ||
} | ||
|
||
func apply(kubeClient ctrlclient.Client, ctx context.Context, rcg genericclioptions.RESTClientGetter, opts *runclient.Options, manifestPath string, manifestContent []byte) (string, 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.
Copy-pasted, slightly simplified. Can anything be simplified here or do we need everything to apply the generated manifest to the cluster?
cmd/gitops/beta/run/cmd.go
Outdated
} | ||
|
||
if len(objs) == 0 { | ||
return "", fmt.Errorf("no Kubernetes objects found at: %s", manifestPath) |
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 be removed, probably. manifestPath
here is used only for this error message. The generated manifest should be guaranteed to be valid?
14ff86e
to
4c3aa52
Compare
cmd/gitops/beta/run/cmd.go
Outdated
if fluxVersion == "" { | ||
filePath := args[0] | ||
|
||
err = run.InstallFlux(log, ctx2, kubeClient, filepath.Join(filePath, run.FluxDirectory), run.ShortManifestFilename) |
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 we have to discuss the semantics of this flux location.
For example,
When running gitops beta run ./deploy/overlay/dev
, the filePath
variable would be ./deploy/overlay/dev
.
Then the InstallFlux
function would install Flux manifests to ./deploy/overlay/dev/flux-system
, which would not be the correct location.
Per Flux convention, an on-disk location of Flux installation would be something like
./cluster/dev/flux-system
- which is normally passed to the flux bootstrap
command.
So I think we need to refine this behavior.
09adb0f
to
786ff90
Compare
Start adding prototype code.
Add generating Flux manifests if Flux is not installed.
Fix error messages when getting the Flux version.
Remove comments.
Rework error message in the `run` command.
120b1da
to
4e3d6d2
Compare
6497310
to
ae1d9a6
Compare
@ozamosi @chanwit I've reworked code based on your suggestions.
Testing:
|
Fix error message in the `getFluxVersion` function in the version endpoint.
d20b805
to
63aae63
Compare
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.
Your description of what is missing and will be done later matches what I'd want. It's in beta, so it's not the end of the world if the code in main isn't perfect yet.
The code here looks like it'll make a good starting point for the rest of the work, so
@ozamosi great, thanks! |
pkg/run/run.go
Outdated
Timeout: 5 * time.Second, | ||
} | ||
|
||
manifest, err := install.Generate(opts, "") |
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 variable would be manifests
to be consistent with your error logs
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.
Fixed, good catch!
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 thank you @opudrovs 🥇
@chanwit thank you! 🎉 |
Closes #2355