-
Notifications
You must be signed in to change notification settings - Fork 80
STOR-2870: Implement creation of LocalVolumeDeviceLink objects #592
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
Changes from all commits
82a6828
44d9b79
5570695
eb66f30
e5a2005
2b33356
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,13 +79,12 @@ type LocalVolumeDeviceLinkStatus struct { | |
| // preferredLinkTarget is the /dev/disk/by-id symlink for the device that the local storage | ||
| // operator evaluated as the most stable and the least error prone. The local storage operator | ||
| // recommends using this symlink, after a careful review by the cluster admin. | ||
| // +required | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +optional | ||
| // +kubebuilder:validation:MaxLength=4096 | ||
| PreferredLinkTarget string `json:"preferredLinkTarget,omitempty"` | ||
|
Comment on lines
+82
to
84
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it be a pointer, if it's allowed to be empty?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to - https://github.com/openshift/enhancements/blob/master/dev-guide/api-conventions.md "In custom resource based APIs specifically, we advise to avoid making fields pointers unless there is an absolute need to do so. An absolute need being the need to distinguish between the zero value and an unset value", it should be fine to keep it optional and still a string type i think. |
||
| // validLinkTargets is the list of /dev/disk/by-id symlinks for the device that the local | ||
| // storage operator considers as valid. The list may contain at most 256 entries. | ||
| // +required | ||
| // +optional | ||
| // +listType=set | ||
| // +kubebuilder:validation:MaxItems=256 | ||
| ValidLinkTargets []string `json:"validLinkTargets,omitempty"` | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to relax these validations, because I realized that in some environments, we may not get any preferredLink targers in |
||
|
|
@@ -113,6 +112,10 @@ type LocalVolumeDeviceLinkList struct { | |
| Items []LocalVolumeDeviceLink `json:"items"` | ||
| } | ||
|
|
||
| func init() { | ||
| SchemeBuilder.Register(&LocalVolumeDeviceLink{}, &LocalVolumeDeviceLinkList{}) | ||
| } | ||
|
|
||
| // DeviceLinkPolicy defines how symlinks for given volumes should be treated | ||
| // by the LSO. Valid values are - None, CurrentLinkTarget and PreferredLinkTarget | ||
| // +kubebuilder:validation:Enum=None;CurrentLinkTarget;PreferredLinkTarget | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import ( | |
| "path/filepath" | ||
|
|
||
| localv1 "github.com/openshift/local-storage-operator/api/v1" | ||
| "github.com/openshift/local-storage-operator/pkg/internal" | ||
| corev1 "k8s.io/api/core/v1" | ||
| storagev1 "k8s.io/api/storage/v1" | ||
| "k8s.io/apimachinery/pkg/api/meta" | ||
|
|
@@ -42,19 +43,35 @@ func GenerateMountMap(runtimeConfig *provCommon.RuntimeConfig) (sets.String, err | |
| return mountPointMap, nil | ||
| } | ||
|
|
||
| // CreateLocalPVArgs holds the arguments for CreateLocalPV. | ||
| type CreateLocalPVArgs struct { | ||
| LocalVolumeLikeObject runtime.Object | ||
| RuntimeConfig *provCommon.RuntimeConfig | ||
| StorageClass storagev1.StorageClass | ||
| MountPointMap sets.String | ||
| Client client.Client | ||
| SymLinkPath string | ||
| IDExists bool | ||
| ExtraLabelsForPV map[string]string | ||
|
|
||
| // CurrentSymlink points to source to which SymLinkPath points to. | ||
| CurrentSymlink string | ||
| // BlockDevice is the block device backing this PV. | ||
| BlockDevice internal.BlockDevice | ||
| } | ||
|
|
||
| // CreateLocalPV is used to create a local PV against a symlink | ||
| // after passing the same validations against that symlink that local-static-provisioner uses | ||
| func CreateLocalPV( | ||
| obj runtime.Object, | ||
| runtimeConfig *provCommon.RuntimeConfig, | ||
| storageClass storagev1.StorageClass, | ||
| mountPointMap sets.String, | ||
| client client.Client, | ||
| symLinkPath string, | ||
| deviceName string, | ||
| idExists bool, | ||
| extraLabelsForPV map[string]string, | ||
| ) error { | ||
| func CreateLocalPV(ctx context.Context, args CreateLocalPVArgs) error { | ||
| obj := args.LocalVolumeLikeObject | ||
| runtimeConfig := args.RuntimeConfig | ||
| storageClass := args.StorageClass | ||
| mountPointMap := args.MountPointMap | ||
| client := args.Client | ||
| symLinkPath := args.SymLinkPath | ||
| deviceName := args.BlockDevice.KName | ||
| idExists := args.IDExists | ||
| extraLabelsForPV := args.ExtraLabelsForPV | ||
| nodeLabels := runtimeConfig.Node.GetLabels() | ||
| hostname, found := nodeLabels[corev1.LabelHostname] | ||
| if !found { | ||
|
|
@@ -91,9 +108,28 @@ func CreateLocalPV( | |
| return fmt.Errorf("could not read the device's volume mode from the node: %w", err) | ||
| } | ||
|
|
||
| accessor, err := meta.Accessor(obj) | ||
| if err != nil { | ||
| return fmt.Errorf("could not get object metadata accessor from obj: %+v", obj) | ||
| } | ||
| name := accessor.GetName() | ||
| namespace := accessor.GetNamespace() | ||
| kind := obj.GetObjectKind().GroupVersionKind().Kind | ||
| if len(name) == 0 || len(namespace) == 0 || len(kind) == 0 { | ||
| return fmt.Errorf("name: %q, namespace: %q, or kind: %q is empty for obj: %+v", name, namespace, kind, obj) | ||
| } | ||
|
|
||
| deviceHandler := internal.NewDeviceLinkHandler(args.CurrentSymlink, client) | ||
|
|
||
| _, err = deviceHandler.Create(ctx, pvName, runtimeConfig.Namespace, obj) | ||
|
gnufied marked this conversation as resolved.
|
||
| if err != nil { | ||
| return fmt.Errorf("error creating localvolumedevicelink object: %w", err) | ||
| } | ||
|
Comment on lines
+122
to
+127
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create the LVDL only after the remaining PV validations pass. Lines 124-127 persist the Patch sketch deviceHandler := internal.NewDeviceLinkHandler(args.CurrentSymlink, client)
-
- _, err = deviceHandler.Create(ctx, pvName, runtimeConfig.Namespace, obj)
- if err != nil {
- return fmt.Errorf("error creating localvolumedevicelink object: %w", err)
- }
// Do not attempt to create or update existing PV's that have been released
existingPV := &corev1.PersistentVolume{ObjectMeta: metav1.ObjectMeta{Name: pvName}}
err = client.Get(ctx, types.NamespacedName{Name: pvName}, existingPV)
@@
newPV := provCommon.CreateLocalPVSpec(localPVConfig)
// Add finalizer for diskmaker symlink cleanup
controllerutil.AddFinalizer(newPV, LSOSymlinkDeleterFinalizer)
+
+ _, err = deviceHandler.Create(ctx, pvName, runtimeConfig.Namespace, obj)
+ if err != nil {
+ return fmt.Errorf("error creating localvolumedevicelink object: %w", err)
+ }
klog.InfoS("creating PV", "pvName", pvName)Also applies to: 129-165, 255-257 🤖 Prompt for AI Agents |
||
|
|
||
| // Do not attempt to create or update existing PV's that have been released | ||
| existingPV := &corev1.PersistentVolume{ObjectMeta: metav1.ObjectMeta{Name: pvName}} | ||
| err = client.Get(context.TODO(), types.NamespacedName{Name: pvName}, existingPV) | ||
| err = client.Get(ctx, types.NamespacedName{Name: pvName}, existingPV) | ||
|
|
||
| if err == nil && existingPV.Status.Phase == corev1.VolumeReleased { | ||
| klog.InfoS("PV is still being cleaned, not going to recreate it", "pvName", pvName, "disk", deviceName) | ||
| // Caller should try again soon | ||
|
|
@@ -128,19 +164,6 @@ func CreateLocalPV( | |
| return fmt.Errorf("path %q has unexpected volume type %q", symLinkPath, actualVolumeMode) | ||
| } | ||
|
|
||
| accessor, err := meta.Accessor(obj) | ||
| if err != nil { | ||
|
|
||
| return fmt.Errorf("could not get object metadata accessor from obj: %+v", obj) | ||
| } | ||
|
|
||
| name := accessor.GetName() | ||
| namespace := accessor.GetNamespace() | ||
| kind := obj.GetObjectKind().GroupVersionKind().Kind | ||
| if len(name) == 0 || len(namespace) == 0 || len(kind) == 0 { | ||
| return fmt.Errorf("name: %q, namespace: %q, or kind: %q is empty for obj: %+v", name, namespace, kind, obj) | ||
| } | ||
|
|
||
| labels := map[string]string{ | ||
| corev1.LabelHostname: hostname, | ||
| PVOwnerKindLabel: kind, | ||
|
|
@@ -229,8 +252,16 @@ func CreateLocalPV( | |
| if opRes != controllerutil.OperationResultNone { | ||
| klog.InfoS("PV changed", "pvName", pvName, "status", opRes) | ||
| } | ||
| if err != nil { | ||
| return fmt.Errorf("error creating pv %s: %w", pvName, err) | ||
| } | ||
|
|
||
| _, err = deviceHandler.ApplyStatus(ctx, pvName, runtimeConfig.Namespace, args.BlockDevice, obj) | ||
| if err != nil { | ||
| return fmt.Errorf("error updating localvolumedevicelink object: %w", err) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to be logical to do
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The event already gets reported up the chain from place where
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gnufied , sorry, I cannot find where it gets reported up the chain from place where CreateLocalPV gets called. Please give me file name and line of code. I also made some debugging adding artificial error in the end of CreateLocalPV(): The result is expected: And LVDL object is crippled (no Status part at all): But
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In - This will log any errors coming from It looks like for
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, in new version in #598. All errors from Hope that answers your question. |
||
| } | ||
|
|
||
| return err | ||
| return nil | ||
| } | ||
|
|
||
| // GeneratePVName is used to generate a PV name based on the filename, node, and storageclass | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,15 +142,13 @@ func (r *LocalVolumeReconciler) syncLocalVolumeProvider(ctx context.Context, ins | |
| return r.addFailureCondition(instance, o, err) | ||
| } | ||
|
|
||
| if diskMakerDS != nil { | ||
| children = append(children, operatorv1.GenerationStatus{ | ||
| Group: appsv1.GroupName, | ||
| Resource: "DaemonSet", | ||
| Namespace: diskMakerDS.Namespace, | ||
| Name: diskMakerDS.Name, | ||
| LastGeneration: diskMakerDS.Generation, | ||
| }) | ||
| } | ||
| children = append(children, operatorv1.GenerationStatus{ | ||
| Group: appsv1.GroupName, | ||
| Resource: "DaemonSet", | ||
| Namespace: diskMakerDS.Namespace, | ||
| Name: diskMakerDS.Name, | ||
| LastGeneration: diskMakerDS.Generation, | ||
| }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is this change relevant to the goal of this PR? Assuming we get rid of this change, would anything become broken?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not. I will fix
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have fixed this in #598 |
||
|
|
||
| o.Status.Generations = children | ||
| o.Status.State = operatorv1.Managed | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.