-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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 "virtctl create clone" command #9596
Conversation
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.
/approve
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: jean-edouard The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
pkg/virtctl/create/clone/clone.go
Outdated
{{ProgramName}} create clone --name my-clone --source sourceVM --target targetVM | ||
|
||
# The above is a shortcut to: | ||
{{ProgramName}} create clone --name my-clone --source name:sourceVM,apiGroup:kubevirt.io,kind:VirtualMachine --target name:targetVM,apiGroup:kubevirt.io,kind:VirtualMachine |
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 do we need to specify the apiGroup
and kind:VirtualMachine
? Couldn't we simply specify an additional flag --type
and there you map type to the k8s object? This version seems a bit lengthy and not very straightforward to me.
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.
IMO, it will be nice to understand from the help option which kind of k8s objects I can clone
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 do we need to specify the apiGroup and kind:VirtualMachine? Couldn't we simply specify an additional flag --type and there you map type to the k8s object? This version seems a bit lengthy and not very straightforward to me.
TBH I'm in a dilemma here :)
The rationale for the flags as they are now is that it's fully generic in the sense that when the clone API would expand and change in the future, for example by supporting more source and target types, virtctl create clone
command wouldn't have to be changed. If we would go with something like --type
, then every supported type would need to be added to the mapping once supported, which might lead to inconsistencies and for harder time when expanding the API.
In addition, there's a need to specify a type for both the source and the target, which might be of different types. For example, the source could be a vmSnapshot while the target is a VM. TBH, I'm not sure how this can be mapped by order ,e.g. virtctl create clone --source sourceSnapshot --type snapshot --target targetVM --type vm
, how can we know that the first --type
refers to the source in this case?
Instead, we can simply omit the apiGroup
and map it out automatically, e.g. virtctl create clone --source name:sourceSnapshot,type:snapshot --target name:targetVM,type:vm
, but this is not a lot nicer and suffers from the disadvantage I laid out above.
Last point is that at least I've made VMs as the default, so cloning VMs can be done much for elegantly (virtctl create clone --source sourceVM --target targetVM
).
But on the other hand - you're right - it is lengthy as for now.
What do you think? I'd appreciate your feedback 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.
IMO, it will be nice to understand from the help option which kind of k8s objects I can clone
Same concern as above. While I think this is very valuable, I'm a bit concerned about not having one single source of truth. Updating in many places once a new type is supported gets me a bit uncomfortable.
WDYT about adding a link to the relevant user-guide entry instead?
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 addition, there's a need to specify a type for both the source and the target, which might be of different types. For example, the source could be a vmSnapshot while the target is a VM. TBH, I'm not sure how this can be mapped by order ,e.g.
virtctl create clone --source sourceSnapshot --type snapshot --target targetVM --type vm
, how can we know that the first--type
refers to the source in this case?
For this, if we can avoid the lengthy option, I still think a --type-source
and --type-target
are still more elegant :). If nothing is specified, you can always assume VirtualMachine
.
The rationale for the flags as they are now is that it's fully generic in the sense that when the clone API would expand and change in the future, for example by supporting more source and target types, virtctl create clone command wouldn't have to be changed. If we would go with something like --type, then every supported type would need to be added to the mapping once supported, which might lead to inconsistencies and for harder time when expanding the API.
Ok, yes this is the real question. Do you expect many k8s object types to be snapshottable/clonable?
Additionally, if we have control of the type, we could validate a certain field. At least, here you are adding a new mac address option. This flag is only valid if the target is a VM. I'm not sure if the target (at least) can be an arbitrary k8s object. Same thing for the smbios
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.
IMO, it will be nice to understand from the help option which kind of k8s objects I can clone
Same concern as above. While I think this is very valuable, I'm a bit concerned about not having one single source of truth. Updating in many places once a new type is supported gets me a bit uncomfortable.
WDYT about adding a link to the relevant user-guide entry instead?
Well, if you have it in the code, for me that's the most reliable source of the truth :). I'd avoid putting links, they get outdated quickly. Let's try to understand what we prefer if an implicit or explicit list of the clonable types :).
I don't want necessarily block this PR, only understand the reasoning behind
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.
Ok, yes this is the real question. Do you expect many k8s object types to be snapshottable?
What I currently have in mind is:
- VMI should be supported as both source and target types
- We were once talking about supporting PVC as the source types. The main idea was that a VM state could reside in the PVC in some format, along with the VM manifest and perhaps a configuration file. We can also utilize the Export API for that.
It's hard to know which more object would need to be supported in the future. For example, maybe a vmpool
could be cloned too?
But you're right, maybe we're not talking about a lot of new supported types.
I think I'm convinced :)
Thank you for this discussion
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 hard to know which more object would need to be supported in the future. For example, maybe a vmpool could be cloned too?
Maybe but if we really realize that the list starts to grow too much and we want to give more flexibility, we can always add an arbitrary type with the logic you currently have, but I would avoid it if not necessary
flags = addFlag(flags, clone.TargetFlag, targetVM) | ||
} | ||
|
||
expectErr := !addSourceFlag || !addTargetFlag |
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: I'd simply move this condition in the if statement below for readability
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.
done :)
thanks!
e0b984e
to
22d779d
Compare
/hold |
f26347d
to
eb0a920
Compare
It's now addressed :) |
eb0a920
to
d6c55f0
Compare
pkg/virtctl/create/clone/clone.go
Outdated
|
||
mustBeSpecifiedErrFmt = "%s must be specified" | ||
|
||
supportedSourceTypes = "vm, snapshot" |
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: I'd use vmsnapshot
to differentiate from a snapshot of a pvc
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.
Done
pkg/virtctl/create/clone/clone.go
Outdated
{{ProgramName}} create clone --source-name sourceVM --annotation-filter "*" --annotation-filter "!some/key" | ||
|
||
# Create a manifest for a clone with new MAC addresses: | ||
{{ProgramName}} create clone --source-name sourceVM --new-mac-address interfaceName:interface1,newMacAddress:00-11-22 --new-mac-address interfaceName:interface2,newMacAddress:00-11-33 |
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: a possible alternative could be to drop the param name like: --new-mac-address interface1:00-11-22 --new-mac-address interface2:00-11-33
. Do we already have some similar options in virtctl
?
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.
Great advice. Thanks!
done
@iholder101 it looks good to me, I let a couple of nit comments, feel free to ignore them if you like. Just remove the hold when you feel ready. /lgtm |
Signed-off-by: Itamar Holder <iholder@redhat.com>
Signed-off-by: Itamar Holder <iholder@redhat.com>
Also, replace literal lists with nil ones. Signed-off-by: Itamar Holder <iholder@redhat.com>
This file uses reflection and isn't straight-forward to figure out. I've added documentation so that it would be easier for others to understand. Signed-off-by: Itamar Holder <iholder@redhat.com>
d6c55f0
to
7a9ef38
Compare
Addressed your comments. /unhold |
/lgtm |
/test pull-kubevirt-prom-rules-verify |
@iholder101: The following tests failed, say
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed 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.
Apologies I sat on these review comments for most of the day, missing that this had already been approved. I've got a few nits and comments, all addressable in follow ups if you think they are worth it.
|
||
cmd := &cobra.Command{ | ||
Use: Clone, | ||
Short: "Create a clone object manifest", |
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 - VirtualMachineClone
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 see #9596 (comment)
} | ||
|
||
switch sourceOrTargetType { | ||
case "vm", "VM", "VirtualMachine", "virtualmachine": |
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 - Could we document these in the help output somehow? You're only listing vm
and vmsnapshot
at the moment.
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 do in a follow-up PR 👍
Entry("snapshot source, vm target", "snapshot", snapshotKind, snapshotApiGroup, "vm", vmKind, vmApiGroup), | ||
Entry("VirtualMachineSnapshot source, vm target", "VirtualMachineSnapshot", snapshotKind, snapshotApiGroup, "vm", vmKind, vmApiGroup), | ||
Entry("vmsnapshot source, vm target", "vmsnapshot", snapshotKind, snapshotApiGroup, "vm", vmKind, vmApiGroup), | ||
Entry("VMSnapshot source, vm target", "VMSnapshot", snapshotKind, snapshotApiGroup, "vm", vmKind, vmApiGroup), |
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 snapshot source and target isn't valid right? AFAICT that isn't being rejected by the command at least at the moment, seems like an easy win.
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 is being rejected here.
Do you think we need another DescribeTable
for source/target kinds that are invalid?
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 that would be useful coverage yeah.
) | ||
|
||
const ( | ||
Clone = "clone" |
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.
supernit - this is more of a nit against the naming of the CRD itself but this command creates VirtualMachineClone
manifests. create clone
works as a command here, it's just a shame we didn't call the actual resource Clone
IMHO given the generic type support you included.
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 just a shame we didn't call the actual resource
Clone
I couldn't agree more.
Actually, I still plan to chance the CRD's name one day (this can be easily done by now since the API is still alpha). That's the reason in this PR I decided to refer to it as a "clone object" instead of the full CRD name.
Thanks for raising this up!
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.
Added some comments which could be addressed in a follow up.
Good work! 👍
const emptyValue = "" | ||
const supportsMultipleFlags = "Can be provided multiple times." | ||
|
||
cmd.Flags().StringVar(&c.name, NameFlag, emptyValue, "Specify the name of the clone. If not specified, name would be randomized.") |
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.
Instead of using emptyValue
you could use the corresponding struct field (e.g. c.name
). If not initialized otherwise it is already empty.
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.
TBH, I don't agree that this makes the situation better. From my perspective, emptyValue
(or nil
) as a default value is much clearer than providing an uninitialized struct field. It's confusing.
In addition, in your suggested approach we rely on the fact that the fields are indeed uninitialized. That's an assumption that might lead to bugs in the future if the assumption changes for some reason.
Is there any added value to your approach that I'm missing? WDYT?
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 useful e.g. when you want to provide default values from a function which initializes the struct. But in your case it does not add that much value so let's keep it for now.
cmd.Flags().StringVar(&c.sourceType, SourceTypeFlag, emptyValue, "Specify the clone's source type. Default type is VM. Supported types: "+supportedSourceTypes) | ||
cmd.Flags().StringVar(&c.targetType, TargetTypeFlag, emptyValue, "Specify the clone's target type. Default type is VM. Supported types: "+supportedTargetTypes) | ||
cmd.Flags().StringArrayVar(&c.labelFilters, LabelFilterFlag, nil, "Specify clone's label filters. "+supportsMultipleFlags) | ||
cmd.Flags().StringArrayVar(&c.annotationFilters, AnnotationFilterFlag, nil, "Specify clone's annotation filters. "+supportsMultipleFlags) |
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.
Similar to above: nil
vs. c.annotationFilters
cmd.Flags().StringArrayVar(&c.newMacAddresses, NewMacAddressesFlag, nil, "Specify clone's new mac addresses. For example: 'interfaceName0:newAddress0'") | ||
cmd.Flags().StringVar(&c.newSmbiosSerial, NewSMBiosSerialFlag, emptyValue, "Specify the clone's new smbios serial") | ||
|
||
_ = cmd.MarkFlagRequired(SourceNameFlag) |
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 panicking instead of ignoring the error?
} | ||
|
||
func withNewMacAddresses(c *createClone, cloneSpec *cloneSpec) error { | ||
const flag = NewMacAddressesFlag |
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.
Unused const?
{{ProgramName}} create clone --name my-clone --source-name sourceVM --target-name targetVM | ||
|
||
# Create a manifest for a clone with a randomized target name (target name is omitted): | ||
{{ProgramName}} create --source-name sourceVM |
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 clone
after create
.
{{ProgramName}} create clone --source-name sourceVM --label-filter "*" --label-filter "!some/key" | ||
|
||
# Create a manifest for a clone with annotation filters: | ||
{{ProgramName}} create clone --source-name sourceVM --annotation-filter "*" --annotation-filter "!some/key" |
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.
!
as arg might result in unwanted behavior by the user's shell. I would recommend using single quotes ('!some/key'
) to make sure the command works as expected.
) | ||
}) | ||
|
||
It("new mac addresses", func() { |
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 test case for a wrong arg to the new mac addresses flag would be good.
}) | ||
}) | ||
|
||
Context("source and target", func() { |
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 could add a negative test case for an empty source name.
@@ -96,8 +96,8 @@ func NewCommand() *cobra.Command { | |||
cmd.Flags().StringArrayVar(&c.gpus, GPUFlag, c.gpus, "Specify the list of vGPUs to passthrough. Can be provided multiple times.") | |||
cmd.Flags().StringArrayVar(&c.hostDevices, HostDeviceFlag, c.hostDevices, "Specify list of HostDevices to passthrough. Can be provided multiple times.") | |||
|
|||
cmd.MarkFlagRequired(CPUFlag) | |||
cmd.MarkFlagRequired(MemoryFlag) | |||
_ = cmd.MarkFlagRequired(CPUFlag) |
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.
Panick here too?
What this PR does / why we need it:
This PR adds a
virtctl create clone
command.Here's the full usage details:
Release note: