From c2d4d368457aafb880934ef80875286523a688c5 Mon Sep 17 00:00:00 2001 From: "Bellizzi, David" Date: Tue, 14 Oct 2025 14:08:56 -0400 Subject: [PATCH 01/30] stable/v25.10 --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4146fa88b..1bce1bfbd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ -@Library(['tools@master', 'tools-override@main']) _ +@Library(['tools@master', 'tools-override@stable/v25.10']) _ node { execute_pipeline(repository: 'trident') From c7d6a5940e78e4d75ddc0fe5d290cd20fe8fb8f4 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 15 Oct 2025 12:56:52 -0600 Subject: [PATCH 02/30] Fix automatic failover comment spelling --- operator/controllers/orchestrator/installer/installer.go | 4 ++-- operator/controllers/orchestrator/installer/uninstaller.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/operator/controllers/orchestrator/installer/installer.go b/operator/controllers/orchestrator/installer/installer.go index ec06c9e23..71357131f 100644 --- a/operator/controllers/orchestrator/installer/installer.go +++ b/operator/controllers/orchestrator/installer/installer.go @@ -995,7 +995,7 @@ func (i *Installer) InstallOrPatchTrident( } } - // Create or update NodeRemediation resouces for automatic force-detach + // Create or update NodeRemediation resources for automatic force-detach if enableForceDetach { returnError = i.createOrPatchNodeRemediationResources() if returnError != nil { @@ -1003,7 +1003,7 @@ func (i *Installer) InstallOrPatchTrident( return nil, "", "", returnError } } else { - // Remove TridentNodeRemediation resouces if enableForceDeach was toggled off + // Remove TridentNodeRemediation resources if enableForceDetach was toggled off if err := i.client.DeleteTridentNodeRemediationResources(i.namespace); err != nil { Log().Warn("could not remove TridentNodeRemediation resources; %v", err) } diff --git a/operator/controllers/orchestrator/installer/uninstaller.go b/operator/controllers/orchestrator/installer/uninstaller.go index 35346c647..27b5ce7f4 100644 --- a/operator/controllers/orchestrator/installer/uninstaller.go +++ b/operator/controllers/orchestrator/installer/uninstaller.go @@ -46,7 +46,7 @@ func (i *Installer) UninstallTrident() error { return fmt.Errorf("could not delete Trident CSI driver custom resource; %v", err) } - // Delete TridentNodeRemediation resouces + // Delete TridentNodeRemediation resources if err := i.client.DeleteTridentNodeRemediationResources(i.namespace); err != nil { return fmt.Errorf("could not delete TridentNodeRemediation resources; %v", err) } From 289b6108463bd340bca470bbabe9fd7252a7a421 Mon Sep 17 00:00:00 2001 From: Albin Johns Date: Sat, 18 Oct 2025 10:56:56 +0530 Subject: [PATCH 03/30] Added TP-BlueXP connector presence in ONTAP telemetry and fixed ANF and CVS tag issue --- config/config.go | 16 +- .../controller_helpers/kubernetes/plugin.go | 57 +++- .../kubernetes/plugin_test.go | 246 ++++++++++++++++-- storage_drivers/azure/azure_anf.go | 18 +- storage_drivers/azure/azure_anf_test.go | 64 +++++ storage_drivers/gcp/gcp_cvs.go | 18 +- storage_drivers/gcp/gcp_cvs_test.go | 29 +++ 7 files changed, 398 insertions(+), 50 deletions(-) diff --git a/config/config.go b/config/config.go index 7fc6ff967..01e08773a 100644 --- a/config/config.go +++ b/config/config.go @@ -26,13 +26,14 @@ type ( ) type Telemetry struct { - TridentVersion string `json:"version"` - TridentBackendUUID string `json:"backendUUID"` - Platform string `json:"platform"` - PlatformVersion string `json:"platformVersion"` - PlatformUID string `json:"platformUID,omitempty"` - PlatformNodeCount int `json:"platformNodeCount,omitempty"` - TridentProtectVersion string `json:"tridentProtectVersion,omitempty"` + TridentVersion string `json:"version"` + TridentBackendUUID string `json:"backendUUID"` + Platform string `json:"platform"` + PlatformVersion string `json:"platformVersion"` + PlatformUID string `json:"platformUID,omitempty"` + PlatformNodeCount int `json:"platformNodeCount,omitempty"` + TridentProtectVersion string `json:"tridentProtectVersion,omitempty"` + TridentProtectConnectorPresent bool `json:"tridentProtectConnectorPresent,omitempty"` } // TelemetryUpdater is a function type for updating dynamic telemetry fields @@ -188,6 +189,7 @@ const ( TridentProtectAppNameLabel = "app.kubernetes.io/name=trident-protect" TridentProtectVersionLabel = "app.kubernetes.io/version" TridentProtectControllerName = "controller-manager" + TridentProtectConnectorLabel = "app=connector.protect.trident.netapp.io" // CSIUnixSocketPermissions CSI socket file needs rw access only for user CSIUnixSocketPermissions = 0o600 diff --git a/frontend/csi/controller_helpers/kubernetes/plugin.go b/frontend/csi/controller_helpers/kubernetes/plugin.go index aed20da1e..125cc90fe 100644 --- a/frontend/csi/controller_helpers/kubernetes/plugin.go +++ b/frontend/csi/controller_helpers/kubernetes/plugin.go @@ -1422,15 +1422,20 @@ func (h *helper) getK8sNodeCount(ctx context.Context) (int, error) { return nodeCount, nil } -// getTridentProtectVersion retrieves the version of Trident Protect if installed in the cluster -func (h *helper) getTridentProtectVersion(ctx context.Context) (string, error) { +// getTridentProtectVersion retrieves the version of Trident Protect and checks for connector presence if controller is found +func (h *helper) getTridentProtectVersion(ctx context.Context) (string, bool, error) { // Search for Trident Protect pods across all namespaces, specifically looking for the controller manager pods, err := h.kubeClient.CoreV1().Pods("").List(ctx, metav1.ListOptions{ LabelSelector: config.TridentProtectAppNameLabel, }) if err != nil { Logc(ctx).WithError(err).Debug("Failed to get Trident Protect pods across all namespaces.") - return "", err + return "", false, err + } + + if len(pods.Items) == 0 { + Logc(ctx).Debugf("No pods found with %s across all namespaces.", config.TridentProtectAppNameLabel) + return "", false, nil } // Look for the controller manager pod specifically @@ -1438,13 +1443,47 @@ func (h *helper) getTridentProtectVersion(ctx context.Context) (string, error) { if strings.Contains(pod.Name, config.TridentProtectControllerName) { if version, exists := pod.Labels[config.TridentProtectVersionLabel]; exists { Logc(ctx).WithField("version", version).WithField("podName", pod.Name).Debug("Found Trident Protect version from controller manager.") - return version, nil + + // Controller found, now check for connector + connectorPresent, err := h.getTridentProtectConnectorPresent(ctx) + if err != nil { + Logc(ctx).WithError(err).Debug("Failed to check connector presence after finding controller.") + // Return version but connector check failed + return version, false, nil + } + + return version, connectorPresent, nil } } } Logc(ctx).Debug("No Trident Protect controller manager pod or version found.") - return "", nil + return "", false, nil +} + +// getTridentProtectConnectorPresent checks if trident-protect-connector pod exists across all namespaces +func (h *helper) getTridentProtectConnectorPresent(ctx context.Context) (bool, error) { + // Search across all namespaces for connector pods + pods, err := h.kubeClient.CoreV1().Pods("").List(ctx, metav1.ListOptions{ + LabelSelector: config.TridentProtectConnectorLabel, + Limit: 1, // We only need to know if one exists + }) + if err != nil { + Logc(ctx).WithError(err).Debug("Failed to get Trident Protect connector pods across all namespaces.") + return false, err + } + + connectorPresent := len(pods.Items) > 0 + if connectorPresent { + Logc(ctx).WithFields(LogFields{ + "podName": pods.Items[0].Name, + "namespace": pods.Items[0].Namespace, + }).Debug("Found Trident Protect connector.") + } else { + Logc(ctx).Debug("No Trident Protect connector found.") + } + + return connectorPresent, nil } // getK8sPlatformVersion retrieves the current Kubernetes cluster version dynamically @@ -1482,13 +1521,15 @@ func (h *helper) updateTelemetryFields(ctx context.Context, telemetry *config.Te Logc(ctx).WithError(err).Debug("Failed to get dynamic platform version for telemetry.") } - // Update TridentProtectVersion dynamically - if tridentProtectVersion, err := h.getTridentProtectVersion(ctx); err == nil { + // Update TridentProtectVersion and connector presence dynamically + if tridentProtectVersion, connectorPresent, err := h.getTridentProtectVersion(ctx); err == nil { telemetry.TridentProtectVersion = tridentProtectVersion + telemetry.TridentProtectConnectorPresent = connectorPresent } else { Logc(ctx).WithError(err).Debug("Failed to get dynamic Trident Protect version for telemetry.") - // Clear the field if Trident Protect is not found + // Clear both fields if Trident Protect is not found telemetry.TridentProtectVersion = "" + telemetry.TridentProtectConnectorPresent = false } } diff --git a/frontend/csi/controller_helpers/kubernetes/plugin_test.go b/frontend/csi/controller_helpers/kubernetes/plugin_test.go index 0123a499f..47fdfca13 100644 --- a/frontend/csi/controller_helpers/kubernetes/plugin_test.go +++ b/frontend/csi/controller_helpers/kubernetes/plugin_test.go @@ -1706,16 +1706,18 @@ func TestGetTridentProtectVersion(t *testing.T) { _, plugin := newMockPlugin(t) tests := []struct { - name string - pods []v1.Pod - expectError bool - expected string + name string + pods []v1.Pod + expectError bool + expectedVersion string + expectedConnector bool }{ { - name: "No Trident Protect pods", - pods: []v1.Pod{}, - expectError: false, - expected: "", + name: "No Trident Protect pods", + pods: []v1.Pod{}, + expectError: false, + expectedVersion: "", + expectedConnector: false, }, { name: "Trident Protect controller manager pod with version in trident-protect namespace", @@ -1731,8 +1733,9 @@ func TestGetTridentProtectVersion(t *testing.T) { }, }, }, - expectError: false, - expected: "100.2506.0", + expectError: false, + expectedVersion: "100.2506.0", + expectedConnector: false, }, { name: "Trident Protect controller manager pod with version in different namespace", @@ -1748,8 +1751,63 @@ func TestGetTridentProtectVersion(t *testing.T) { }, }, }, - expectError: false, - expected: "100.2507.1", + expectError: false, + expectedVersion: "100.2507.1", + expectedConnector: false, + }, + { + name: "Controller manager with connector present", + pods: []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-controller-manager-abc123", + Namespace: "trident-protect", + Labels: map[string]string{ + "app.kubernetes.io/name": "trident-protect", + "app.kubernetes.io/version": "100.2506.0", + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-58d5bf7644-6zsjc", + Namespace: "trident-protect", + Labels: map[string]string{ + "app": "connector.protect.trident.netapp.io", + }, + }, + }, + }, + expectError: false, + expectedVersion: "100.2506.0", + expectedConnector: true, + }, + { + name: "Controller manager with connector in different namespace", + pods: []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-controller-manager-abc123", + Namespace: "trident-protect", + Labels: map[string]string{ + "app.kubernetes.io/name": "trident-protect", + "app.kubernetes.io/version": "100.2506.0", + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-58d5bf7644-6zsjc", + Namespace: "other-namespace", + Labels: map[string]string{ + "app": "connector.protect.trident.netapp.io", + }, + }, + }, + }, + expectError: false, + expectedVersion: "100.2506.0", + expectedConnector: true, }, { name: "Multiple Trident Protect pods across namespaces - returns first controller manager found", @@ -1784,8 +1842,9 @@ func TestGetTridentProtectVersion(t *testing.T) { }, }, }, - expectError: false, - expected: "100.2508.0", // First controller manager found (kube-system comes before trident-protect alphabetically) + expectError: false, + expectedVersion: "100.2508.0", // First controller manager found (kube-system comes before trident-protect alphabetically) + expectedConnector: false, }, { name: "Trident Protect pod without controller manager", @@ -1800,8 +1859,9 @@ func TestGetTridentProtectVersion(t *testing.T) { }, }, }, - expectError: false, - expected: "", + expectError: false, + expectedVersion: "", + expectedConnector: false, }, { name: "Controller manager without version label", @@ -1816,8 +1876,9 @@ func TestGetTridentProtectVersion(t *testing.T) { }, }, }, - expectError: false, - expected: "", + expectError: false, + expectedVersion: "", + expectedConnector: false, }, { name: "Pods without proper app.kubernetes.io/name label are ignored", @@ -1832,8 +1893,9 @@ func TestGetTridentProtectVersion(t *testing.T) { }, }, }, - expectError: false, - expected: "", + expectError: false, + expectedVersion: "", + expectedConnector: false, }, } @@ -1848,14 +1910,131 @@ func TestGetTridentProtectVersion(t *testing.T) { plugin.kubeClient = fakeClientSet - version, err := plugin.getTridentProtectVersion(ctx) + version, connectorPresent, err := plugin.getTridentProtectVersion(ctx) if test.expectError { assert.Error(t, err) assert.Empty(t, version) + assert.False(t, connectorPresent) } else { assert.NoError(t, err) - assert.Equal(t, test.expected, version) + assert.Equal(t, test.expectedVersion, version) + assert.Equal(t, test.expectedConnector, connectorPresent) + } + }) + } +} + +func TestGetTridentProtectConnectorPresent(t *testing.T) { + ctx := context.Background() + _, plugin := newMockPlugin(t) + + tests := []struct { + name string + pods []v1.Pod + expectError bool + expectedConnector bool + }{ + { + name: "No connector pods", + pods: []v1.Pod{}, + expectError: false, + expectedConnector: false, + }, + { + name: "Connector pod present", + pods: []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-58d5bf7644-6zsjc", + Namespace: "trident-protect", + Labels: map[string]string{ + "app": "connector.protect.trident.netapp.io", + }, + }, + }, + }, + expectError: false, + expectedConnector: true, + }, + { + name: "Multiple connector pods across namespaces", + pods: []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-abc123", + Namespace: "namespace1", + Labels: map[string]string{ + "app": "connector.protect.trident.netapp.io", + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-def456", + Namespace: "namespace2", + Labels: map[string]string{ + "app": "connector.protect.trident.netapp.io", + }, + }, + }, + }, + expectError: false, + expectedConnector: true, + }, + { + name: "Pod with different label - should not detect", + pods: []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "some-other-connector-pod", + Namespace: "default", + Labels: map[string]string{ + "app": "some.other.connector", + }, + }, + }, + }, + expectError: false, + expectedConnector: false, + }, + { + name: "Pod with connector in name but wrong label", + pods: []v1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-wrong-label", + Namespace: "trident-protect", + Labels: map[string]string{ + "app.kubernetes.io/name": "trident-protect", + }, + }, + }, + }, + expectError: false, + expectedConnector: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Create a fake clientset with the test pods + objs := make([]runtime.Object, len(test.pods)) + for i := range test.pods { + objs[i] = &test.pods[i] + } + fakeClientSet := k8sfake.NewSimpleClientset(objs...) + + plugin.kubeClient = fakeClientSet + + connectorPresent, err := plugin.getTridentProtectConnectorPresent(ctx) + + if test.expectError { + assert.Error(t, err) + assert.False(t, connectorPresent) + } else { + assert.NoError(t, err) + assert.Equal(t, test.expectedConnector, connectorPresent) } }) } @@ -1940,8 +2119,18 @@ func TestUpdateTelemetryFields(t *testing.T) { }, } + connectorPod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "trident-protect-connector-58d5bf7644-6zsjc", + Namespace: "trident-protect", + Labels: map[string]string{ + "app": "connector.protect.trident.netapp.io", + }, + }, + } + // Setup fake clientset with test objects - objs := []runtime.Object{kubeSystemNS, tridentProtectPod} + objs := []runtime.Object{kubeSystemNS, tridentProtectPod, connectorPod} for i := range nodes { objs = append(objs, &nodes[i]) } @@ -1972,14 +2161,16 @@ func TestUpdateTelemetryFields(t *testing.T) { assert.Equal(t, "test-cluster-uid-12345", telemetry.PlatformUID, "should set cluster UID from kube-system namespace") assert.Equal(t, 3, telemetry.PlatformNodeCount, "should set node count from cached nodes") assert.Equal(t, "100.2506.0", telemetry.TridentProtectVersion, "should set Trident Protect version from controller manager pod label") + assert.True(t, telemetry.TridentProtectConnectorPresent, "should set connector present to true when connector pod exists") assert.NotEmpty(t, telemetry.PlatformVersion, "should set platform version from Kubernetes server version") // Test with a telemetry object that has existing values to ensure they get overwritten existingTelemetry := &config.Telemetry{ - PlatformUID: "old-uid", - PlatformNodeCount: 999, - PlatformVersion: "old-version", - TridentProtectVersion: "old-protect-version", + PlatformUID: "e48f1b9c-ac99-430-82d2-d89815e2ebd1", + PlatformNodeCount: 5, + PlatformVersion: "v1.31.8", + TridentProtectVersion: "25.07.0-preview", + TridentProtectConnectorPresent: false, } plugin.updateTelemetryFields(ctx, existingTelemetry) @@ -1988,6 +2179,7 @@ func TestUpdateTelemetryFields(t *testing.T) { assert.Equal(t, "test-cluster-uid-12345", existingTelemetry.PlatformUID, "should overwrite existing cluster UID") assert.Equal(t, 3, existingTelemetry.PlatformNodeCount, "should overwrite existing node count") assert.Equal(t, "100.2506.0", existingTelemetry.TridentProtectVersion, "should overwrite existing Trident Protect version") + assert.True(t, existingTelemetry.TridentProtectConnectorPresent, "should overwrite existing connector presence") assert.NotEmpty(t, existingTelemetry.PlatformVersion, "should overwrite existing platform version") assert.NotEqual(t, "old-version", existingTelemetry.PlatformVersion, "platform version should be updated, not kept as old value") } diff --git a/storage_drivers/azure/azure_anf.go b/storage_drivers/azure/azure_anf.go index 70cfec0a9..c8e3a2700 100644 --- a/storage_drivers/azure/azure_anf.go +++ b/storage_drivers/azure/azure_anf.go @@ -1544,13 +1544,23 @@ func (d *NASStorageDriver) Rename(ctx context.Context, name, newName string) err return nil } -// getTelemetryLabels builds the standard telemetry labels that are set on each volume. +// getTelemetryLabels builds essential telemetry labels that fit within Azure's 256-character limit. func (d *NASStorageDriver) getTelemetryLabels(ctx context.Context) string { - telemetry := map[string]Telemetry{drivers.TridentLabelTag: *d.telemetry} + // Use only essential fields to stay within Azure tag size limit (256 chars) + essentialTelemetry := map[string]interface{}{ + drivers.TridentLabelTag: map[string]interface{}{ + "version": d.telemetry.TridentVersion, + "backendUUID": d.telemetry.TridentBackendUUID, + "platform": d.telemetry.Platform, + "platformVersion": d.telemetry.PlatformVersion, + "plugin": d.telemetry.Plugin, + }, + } - telemetryJSON, err := json.Marshal(telemetry) + telemetryJSON, err := json.Marshal(essentialTelemetry) if err != nil { - Logc(ctx).Errorf("Failed to marshal telemetry: %+v", telemetry) + Logc(ctx).Errorf("Failed to marshal essential telemetry; %v", err) + return "" } return strings.ReplaceAll(string(telemetryJSON), " ", "") diff --git a/storage_drivers/azure/azure_anf_test.go b/storage_drivers/azure/azure_anf_test.go index aed561c78..2c809c0a8 100644 --- a/storage_drivers/azure/azure_anf_test.go +++ b/storage_drivers/azure/azure_anf_test.go @@ -5,6 +5,7 @@ package azure import ( "context" "encoding/json" + "fmt" "io" "os" "regexp" @@ -5678,7 +5679,70 @@ func TestGetTelemetryLabels(t *testing.T) { result := driver.getTelemetryLabels(ctx) + // Validate JSON structure assert.True(t, strings.HasPrefix(result, `{"trident":{`)) + + // Validate essential fields are present + assert.Contains(t, result, `"version"`) + assert.Contains(t, result, `"backendUUID"`) + assert.Contains(t, result, `"platform"`) + assert.Contains(t, result, `"plugin"`) + // Validate excluded fields are NOT present (they were causing size issues) + assert.Contains(t, result, `"platformVersion"`) + // Validate excluded fields are NOT present (they were causing size issues) + assert.NotContains(t, result, `"platformUID"`) + assert.NotContains(t, result, `"platformNodeCount"`) + assert.NotContains(t, result, `"tridentProtectVersion"`) + assert.NotContains(t, result, `"tridentProtectConnectorPresent"`) +} + +func TestGetTelemetryLabels_SizeConstraint(t *testing.T) { + _, driver := newMockANFDriver(t) + driver.initializeTelemetry(ctx, BackendUUID) + + result := driver.getTelemetryLabels(ctx) + + // Validate the telemetry fits within Azure's 256-character tag limit + assert.LessOrEqual(t, len(result), 256, + "Telemetry JSON exceeds Azure's 256-character tag limit: %d characters", len(result)) + + // Validate it's not empty + assert.Greater(t, len(result), 0, "Telemetry should not be empty") + + // Log the actual size for reference + t.Logf("Telemetry size: %d characters (limit: 256)", len(result)) + t.Logf("Telemetry content: %s", result) +} + +func TestTelemetryIntegration_VolumeCreation(t *testing.T) { + _, driver := newMockANFDriver(t) + driver.initializeTelemetry(ctx, BackendUUID) + + // Test that telemetry labels are properly included in volume creation context + labels := make(map[string]string) + labels[drivers.TridentLabelTag] = driver.getTelemetryLabels(ctx) + + // Validate the telemetry label exists and is properly formatted + telemetryLabel, exists := labels[drivers.TridentLabelTag] + assert.True(t, exists, "Trident label should exist in volume labels") + assert.NotEmpty(t, telemetryLabel, "Telemetry label should not be empty") + + // Validate size constraint for Azure tags + assert.LessOrEqual(t, len(telemetryLabel), 256, + "Telemetry label exceeds Azure tag size limit") + + // Validate JSON structure + assert.True(t, strings.HasPrefix(telemetryLabel, `{"trident":{`), + "Telemetry should be properly formatted JSON") + + // Validate essential fields are present + essentialFields := []string{"version", "backendUUID", "platform", "plugin"} + for _, field := range essentialFields { + assert.Contains(t, telemetryLabel, fmt.Sprintf(`"%s"`, field), + "Essential field %s should be present in telemetry", field) + } + + t.Logf("Volume creation telemetry validation passed. Size: %d chars", len(telemetryLabel)) } func TestUpdateTelemetryLabels(t *testing.T) { diff --git a/storage_drivers/gcp/gcp_cvs.go b/storage_drivers/gcp/gcp_cvs.go index 254937564..9f52235ca 100644 --- a/storage_drivers/gcp/gcp_cvs.go +++ b/storage_drivers/gcp/gcp_cvs.go @@ -1238,13 +1238,23 @@ func (d *NFSStorageDriver) Rename(ctx context.Context, name, newName string) err return nil } -// getTelemetryLabels builds the labels that are set on each volume. +// getTelemetryLabels builds essential telemetry labels that fit within GCP's 255-character limit. func (d *NFSStorageDriver) getTelemetryLabels(ctx context.Context) string { - telemetry := map[string]Telemetry{drivers.TridentLabelTag: *d.getTelemetry()} + // Use only essential fields to stay within GCP label size limit (255 chars) + essentialTelemetry := map[string]interface{}{ + drivers.TridentLabelTag: map[string]interface{}{ + "version": d.telemetry.TridentVersion, + "backendUUID": d.telemetry.TridentBackendUUID, + "platform": d.telemetry.Platform, + "platformVersion": d.telemetry.PlatformVersion, + "plugin": d.telemetry.Plugin, + }, + } - telemetryJSON, err := json.Marshal(telemetry) + telemetryJSON, err := json.Marshal(essentialTelemetry) if err != nil { - Logc(ctx).Errorf("Failed to marshal telemetry: %+v", telemetry) + Logc(ctx).Errorf("Failed to marshal essential telemetry; %v", err) + return "" } return strings.ReplaceAll(string(telemetryJSON), " ", "") diff --git a/storage_drivers/gcp/gcp_cvs_test.go b/storage_drivers/gcp/gcp_cvs_test.go index 9e97b455e..096b337b4 100644 --- a/storage_drivers/gcp/gcp_cvs_test.go +++ b/storage_drivers/gcp/gcp_cvs_test.go @@ -1673,6 +1673,35 @@ func TestImport_NotManagedZoneRedundantVolume(t *testing.T) { assert.NoError(t, err, "Volume import failed") } +func TestGetTelemetryLabels_SizeConstraint(t *testing.T) { + _, driver := newMockGCPDriver(t) + + result := driver.getTelemetryLabels(ctx) + + // Validate the telemetry fits within GCP's 255-character label limit + assert.LessOrEqual(t, len(result), 255, + "Telemetry JSON exceeds GCP's 255-character label limit: %d characters", len(result)) + + // Validate it's not empty + assert.Greater(t, len(result), 0, "Telemetry should not be empty") + + // Validate JSON structure + assert.True(t, strings.HasPrefix(result, `{"trident":{`)) + + // Validate essential fields are present + assert.Contains(t, result, `"version"`) + assert.Contains(t, result, `"backendUUID"`) + assert.Contains(t, result, `"platform"`) + assert.Contains(t, result, `"platformVersion"`) + assert.Contains(t, result, `"plugin"`) + + // Validate excluded fields are NOT present (they were causing size issues) + assert.NotContains(t, result, `"platformUID"`) + assert.NotContains(t, result, `"platformNodeCount"`) + assert.NotContains(t, result, `"tridentProtectVersion"`) + assert.NotContains(t, result, `"tridentProtectConnectorPresent"`) +} + func getTelemetryMapString(key string) string { telemetry := tridentconfig.OrchestratorTelemetry telemetry.TridentBackendUUID = "backend-id" From f6c7db3f1b30585f54d913563a9ca761bfd3c625 Mon Sep 17 00:00:00 2001 From: Utkarsh Jha <167739637+Utkarshjh@users.noreply.github.com> Date: Sat, 18 Oct 2025 13:44:49 +0530 Subject: [PATCH 04/30] clone of imported ontap san economy with no rename flag set fails --- storage_drivers/ontap/ontap_san_economy.go | 5 +++ .../ontap/ontap_san_economy_test.go | 36 ++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/storage_drivers/ontap/ontap_san_economy.go b/storage_drivers/ontap/ontap_san_economy.go index e778ebf92..b8b71c9bd 100644 --- a/storage_drivers/ontap/ontap_san_economy.go +++ b/storage_drivers/ontap/ontap_san_economy.go @@ -1011,6 +1011,11 @@ func (d *SANEconomyStorageDriver) Import( // Managed import with no rename only supported for csi workflow if volConfig.ImportNoRename && d.Config.DriverContext != tridentconfig.ContextDocker { + if !strings.HasPrefix(flexvol.Name, d.FlexvolNamePrefix()) { + // Reject import if the Flexvol is not following naming conventions. + return fmt.Errorf("could not import volume/LUN, volume is named incorrectly: %s, expected pattern: %s*", + flexvol.Name, d.FlexvolNamePrefix()) + } volConfig.InternalName = originalLUNName targetPath = "/vol/" + originalFlexvolName + "/" + volConfig.InternalName // This is critical so that subsequent operations can find the LUN in case of no rename import diff --git a/storage_drivers/ontap/ontap_san_economy_test.go b/storage_drivers/ontap/ontap_san_economy_test.go index aa905f451..d93aa3063 100644 --- a/storage_drivers/ontap/ontap_san_economy_test.go +++ b/storage_drivers/ontap/ontap_san_economy_test.go @@ -2020,9 +2020,15 @@ func TestOntapSanEconomyVolumeImport_UnsupportedNameLength(t *testing.T) { func TestOntapSanEconomyVolumeImport_ManagedNoRename(t *testing.T) { mockAPI, d := newMockOntapSanEcoDriver(t) - - d.flexvolNamePrefix = "test_lun_pool_" d.Config.SVM = "test_svm" + var storagePrefix string + if d.Config.StoragePrefix != nil { + storagePrefix = *d.Config.StoragePrefix + } else { + storagePrefix = "" + } + d.flexvolNamePrefix = fmt.Sprintf("trident_lun_pool_%s_", storagePrefix) + d.Config.DriverContext = tridentconfig.ContextCSI tests := []struct { name string // Name of the test case @@ -2042,12 +2048,12 @@ func TestOntapSanEconomyVolumeImport_ManagedNoRename(t *testing.T) { FileSystem: "xfs", ImportNoRename: true, }, - volToImport: "original_vol/original_lun", + volToImport: d.FlexvolNamePrefix() + "12345/original_lun", mocks: func(mockAPI *mockapi.MockOntapAPI) { mockAPI.EXPECT().VolumeInfo(gomock.Any(), gomock.Any()). - Return(&api.Volume{Name: "original_vol", AccessType: "rw"}, nil) + Return(&api.Volume{Name: d.FlexvolNamePrefix() + "12345", AccessType: "rw"}, nil) mockAPI.EXPECT().LunGetByName(gomock.Any(), gomock.Any()). - Return(&api.Lun{Name: "/vol/original_vol/original_lun", State: "online", Size: "1073741824"}, nil) + Return(&api.Lun{Name: "/vol/" + d.FlexvolNamePrefix() + "12345/original_lun", State: "online", Size: "1073741824"}, nil) mockAPI.EXPECT().LunListIgroupsMapped(gomock.Any(), gomock.Any()).Return(nil, nil) }, wantErr: assert.NoError, @@ -2056,7 +2062,7 @@ func TestOntapSanEconomyVolumeImport_ManagedNoRename(t *testing.T) { // With ImportNoRename, InternalName should be set to original LUN name assert.Equal(t, "original_lun", volConfig.InternalName, "InternalName should be original LUN name") // InternalID should be set properly - expectedID := "/svm/test_svm/flexvol/original_vol/lun/original_lun" + expectedID := "/svm/test_svm/flexvol/" + d.FlexvolNamePrefix() + "12345/lun/original_lun" assert.Equal(t, expectedID, volConfig.InternalID, "InternalID should be set correctly") }, }, @@ -2069,12 +2075,12 @@ func TestOntapSanEconomyVolumeImport_ManagedNoRename(t *testing.T) { FileSystem: "xfs", ImportNoRename: true, }, - volToImport: "original_vol/original_lun", + volToImport: d.FlexvolNamePrefix() + "67890/original_lun", mocks: func(mockAPI *mockapi.MockOntapAPI) { mockAPI.EXPECT().VolumeInfo(gomock.Any(), gomock.Any()). - Return(&api.Volume{Name: "original_vol", AccessType: "rw"}, nil) + Return(&api.Volume{Name: d.FlexvolNamePrefix() + "67890", AccessType: "rw"}, nil) mockAPI.EXPECT().LunGetByName(gomock.Any(), gomock.Any()). - Return(&api.Lun{Name: "/vol/original_vol/original_lun", State: "online", Size: "1073741824"}, nil) + Return(&api.Lun{Name: "/vol/" + d.FlexvolNamePrefix() + "67890/original_lun", State: "online", Size: "1073741824"}, nil) mockAPI.EXPECT().LunListIgroupsMapped(gomock.Any(), gomock.Any()). Return(nil, errors.New("igroup list failed")) }, @@ -2083,7 +2089,7 @@ func TestOntapSanEconomyVolumeImport_ManagedNoRename(t *testing.T) { validate: func(t *testing.T, volConfig *storage.VolumeConfig) { // Validate InternalName and InternalID are still set even on failure assert.Equal(t, "original_lun", volConfig.InternalName, "InternalName should be original LUN name") - expectedID := "/svm/test_svm/flexvol/original_vol/lun/original_lun" + expectedID := "/svm/test_svm/flexvol/" + d.FlexvolNamePrefix() + "67890/lun/original_lun" assert.Equal(t, expectedID, volConfig.InternalID, "InternalID should be set correctly") }, }, @@ -2102,15 +2108,11 @@ func TestOntapSanEconomyVolumeImport_ManagedNoRename(t *testing.T) { Return(&api.Volume{Name: "non_conforming_vol", AccessType: "rw"}, nil) mockAPI.EXPECT().LunGetByName(gomock.Any(), gomock.Any()). Return(&api.Lun{Name: "/vol/non_conforming_vol/my_lun", State: "online", Size: "1073741824"}, nil) - mockAPI.EXPECT().LunListIgroupsMapped(gomock.Any(), gomock.Any()).Return(nil, nil) }, - wantErr: assert.NoError, - testOut: "Import succeeded", + wantErr: assert.Error, + testOut: "Import should fail with non-conforming Flexvol name", validate: func(t *testing.T, volConfig *storage.VolumeConfig) { - // With ImportNoRename, no rename should happen regardless of naming convention - assert.Equal(t, "my_lun", volConfig.InternalName, "InternalName should be original LUN name") - expectedID := "/svm/test_svm/flexvol/non_conforming_vol/lun/my_lun" - assert.Equal(t, expectedID, volConfig.InternalID, "InternalID should use original Flexvol and LUN names") + // Validation removed as import should fail before this point }, }, } From ab4a35fef8f6e8499f61946ed446d170afafd1dd Mon Sep 17 00:00:00 2001 From: emmahardison <106281452+emmahardison@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:04:13 -0600 Subject: [PATCH 05/30] Finalizer removal correction --- frontend/crd/trident_node_remediation.go | 46 +++++++++++++++++------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/frontend/crd/trident_node_remediation.go b/frontend/crd/trident_node_remediation.go index ec964dd6d..a17d70a69 100644 --- a/frontend/crd/trident_node_remediation.go +++ b/frontend/crd/trident_node_remediation.go @@ -30,7 +30,7 @@ func (c *TridentCrdController) handleTridentNodeRemediation(keyItem *KeyItem) (r Logc(ctx).Debug(">>>> TridentCrdController#handleNodeRemediation") defer Logc(ctx).Debug("<<<< TridentCrdController#handleNodeRemediation") - // Esnure the volume attachment indexer cache is synced + // Ensure the volume attachment indexer cache is synced if ok := c.indexers.VolumeAttachmentIndexer().WaitForCacheSync(ctx); !ok { Logc(ctx).Warn("Failed to sync volume attachment cache. Not all volume attachments may be found.") } @@ -202,6 +202,21 @@ func (c *TridentCrdController) validateTridentNodeRemediationCR( return actionCR, nil } +// updateNodeRemediationCRWithErrorHandling performs a CR update and handles common error cases +func (c *TridentCrdController) updateNodeRemediationCRWithErrorHandling( + ctx context.Context, cr *netappv1.TridentNodeRemediation, namespace string, +) (*netappv1.TridentNodeRemediation, error) { + updatedCR, err := c.crdClientset.TridentV1().TridentNodeRemediations(namespace).Update(ctx, cr, updateOpts) + if apierrors.IsNotFound(err) { + Logc(ctx).Debug("Node remediation in work queue no longer exists.") + return nil, err + } + if err != nil { + return nil, errors.WrapWithReconcileDeferredError(err, "reconcile deferred") + } + return updatedCR, nil +} + func (c *TridentCrdController) updateNodeRemediationCR( ctx context.Context, namespace, name string, statusUpdate *netappv1.TridentNodeRemediationStatus, ) error { @@ -215,23 +230,30 @@ func (c *TridentCrdController) updateNodeRemediationCR( return errors.WrapWithReconcileDeferredError(err, "reconcile deferred") } - if statusUpdate.State == netappv1.TridentActionStateSucceeded || statusUpdate.State == netappv1.TridentActionStateFailed { - if actionCR.HasTridentFinalizers() { - actionCR.RemoveTridentFinalizers() + // Add finalizers for in-progress states + if statusUpdate.State != netappv1.TridentActionStateSucceeded && statusUpdate.State != netappv1.TridentActionStateFailed { + if !actionCR.HasTridentFinalizers() { + actionCR.AddTridentFinalizers() } - } else if !actionCR.HasTridentFinalizers() { - actionCR.AddTridentFinalizers() } actionCR.Status = *statusUpdate - _, err = c.crdClientset.TridentV1().TridentNodeRemediations(namespace).Update(ctx, actionCR, updateOpts) - if apierrors.IsNotFound(err) { - Logc(ctx).Debug("Node remediation in work queue no longer exists.") + updatedCR, err := c.updateNodeRemediationCRWithErrorHandling(ctx, actionCR, namespace) + if err != nil { return err } - if err != nil { - return errors.WrapWithReconcileDeferredError(err, "reconcile deferred") + + // For terminal states, remove finalizers in a separate update after + // status is set, to let state update before NHC deletes CR + if statusUpdate.State == netappv1.TridentActionStateSucceeded || statusUpdate.State == netappv1.TridentActionStateFailed { + if updatedCR.HasTridentFinalizers() { + updatedCR.RemoveTridentFinalizers() + _, err = c.updateNodeRemediationCRWithErrorHandling(ctx, updatedCR, namespace) + if err != nil { + return err + } + } } return nil @@ -310,7 +332,7 @@ func (c *TridentCrdController) failoverDetach( } // Update the CR status with VAs to delete. - // This is addative, not a replacement. This way if we have already deleted pods and then crash, we will still + // This is additive, not a replacement. This way if we have already deleted pods and then crash, we will still // know which VAs to delete when we restart. Logc(ctx).WithField("tridentNodeRemediation", actionCR.Name).Info( "Adding volume attachments to delete to TridentNodeRemediation CR status.") From 442101e38c8ae19db4f0ccb9a835d71d964c8dd4 Mon Sep 17 00:00:00 2001 From: Aparna Singh Date: Tue, 21 Oct 2025 01:49:51 +0530 Subject: [PATCH 06/30] Added cloudConfiguration as config option in ANF driver --- storage_drivers/azure/api/azure.go | 86 +++++++ storage_drivers/azure/api/azure_test.go | 232 ++++++++++++++++++ storage_drivers/azure/azure_anf.go | 12 + storage_drivers/azure/azure_anf_test.go | 158 ++++++++++++ storage_drivers/types.go | 29 ++- ...ackend-anf-cloud-configuration-custom.json | 15 ++ ...ackend-anf-cloud-configuration-custom.yaml | 26 ++ .../backend-anf-cloud-configuration.json | 13 + .../backend-anf-cloud-configuration.yaml | 24 ++ 9 files changed, 585 insertions(+), 10 deletions(-) create mode 100644 trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.json create mode 100644 trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.yaml create mode 100644 trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.json create mode 100644 trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.yaml diff --git a/storage_drivers/azure/api/azure.go b/storage_drivers/azure/api/azure.go index aa1b5c64c..7f62b6081 100644 --- a/storage_drivers/azure/api/azure.go +++ b/storage_drivers/azure/api/azure.go @@ -9,11 +9,13 @@ import ( "fmt" "io" "net/http" + "net/url" "regexp" "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" netapp "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7" @@ -51,6 +53,18 @@ var ( VolumePollerCache = AzurePollerResponseCache{pollerResponseMap: make(map[PollerKey]PollerResponse)} ) +// CloudConfiguration allows users to specify Azure cloud environment. +// Either CloudName (predefined) or custom configuration (ADAuthorityHost, Audience, Endpoint) must be specified. +type CloudConfiguration struct { + // Option 1: Predefined cloud name (AzurePublic, AzureChina, AzureGovernment) + CloudName string `json:"cloudName,omitempty"` + + // Option 2: Custom cloud configuration (all three must be specified together) + ADAuthorityHost string `json:"adAuthorityHost,omitempty"` // e.g., https://login.chinacloudapi.cn/ + Audience string `json:"audience,omitempty"` // e.g., https://management.core.chinacloudapi.cn + Endpoint string `json:"endpoint,omitempty"` // e.g., https://management.chinacloudapi.cn +} + // ClientConfig holds configuration data for the API driver object. type ClientConfig struct { // Azure API authentication parameters @@ -60,6 +74,9 @@ type ClientConfig struct { StorageDriverName string TenantID string `json:"tenantId"` + // Cloud configuration + CloudConfig *CloudConfiguration `json:"cloudConfiguration,omitempty"` + // Options DebugTraceFlags map[string]bool SDKTimeout time.Duration // Timeout applied to all calls to the Azure SDK @@ -214,6 +231,68 @@ type Client struct { sdkClient *AzureClient } +// ValidateCloudConfiguration validates the cloud configuration and returns a cloud.Configuration. +func ValidateCloudConfiguration(cloudConfig *CloudConfiguration) (*cloud.Configuration, error) { + // If no cloud config provided, use default (AzurePublic) + if cloudConfig == nil { + return &cloud.AzurePublic, nil + } + + hasCloudName := cloudConfig.CloudName != "" + hasCustom := cloudConfig.ADAuthorityHost != "" || cloudConfig.Audience != "" || cloudConfig.Endpoint != "" + + // Check mutual exclusivity + if hasCloudName && hasCustom { + return nil, errors.New("cloudName and custom configuration (adAuthorityHost, audience, endpoint) are mutually exclusive") + } + + // If neither is provided, use default + if !hasCloudName && !hasCustom { + return &cloud.AzurePublic, nil + } + + // Option 1: Named cloud + if hasCloudName { + switch cloudConfig.CloudName { + case "AzurePublic": + return &cloud.AzurePublic, nil + case "AzureChina": + return &cloud.AzureChina, nil + case "AzureGovernment": + return &cloud.AzureGovernment, nil + default: + return nil, fmt.Errorf("unknown cloudName: %s (valid values: AzurePublic, AzureChina, AzureGovernment)", cloudConfig.CloudName) + } + } + + // Option 2: Custom configuration - all three fields must be provided + if cloudConfig.ADAuthorityHost == "" || cloudConfig.Audience == "" || cloudConfig.Endpoint == "" { + return nil, errors.New("when using custom cloud configuration, adAuthorityHost, audience, and endpoint are all required") + } + + // Validate URLs + if _, err := url.Parse(cloudConfig.ADAuthorityHost); err != nil { + return nil, fmt.Errorf("invalid adAuthorityHost URL: %v", err) + } + if _, err := url.Parse(cloudConfig.Audience); err != nil { + return nil, fmt.Errorf("invalid audience URL: %v", err) + } + if _, err := url.Parse(cloudConfig.Endpoint); err != nil { + return nil, fmt.Errorf("invalid endpoint URL: %v", err) + } + + // Build custom cloud configuration + return &cloud.Configuration{ + ActiveDirectoryAuthorityHost: cloudConfig.ADAuthorityHost, + Services: map[cloud.ServiceName]cloud.ServiceConfiguration{ + cloud.ResourceManager: { + Audience: cloudConfig.Audience, + Endpoint: cloudConfig.Endpoint, + }, + }, + }, nil +} + // NewDriver is a factory method for creating a new SDK interface. func NewDriver(config ClientConfig) (Azure, error) { var err error @@ -223,6 +302,12 @@ func NewDriver(config ClientConfig) (Azure, error) { return nil, errors.New("location must be specified in the config") } + // Validate and get cloud configuration + cloudConfig, err := ValidateCloudConfiguration(config.CloudConfig) + if err != nil { + return nil, fmt.Errorf("invalid cloud configuration: %v", err) + } + credential, err := GetAzureCredential(config) if err != nil { return nil, err @@ -230,6 +315,7 @@ func NewDriver(config ClientConfig) (Azure, error) { clientOptions := &arm.ClientOptions{ ClientOptions: policy.ClientOptions{ + Cloud: *cloudConfig, Retry: policy.RetryOptions{ TryTimeout: config.SDKTimeout, RetryDelay: SDKRetryDelay, diff --git a/storage_drivers/azure/api/azure_test.go b/storage_drivers/azure/api/azure_test.go index 0b741fde5..aa4e45092 100644 --- a/storage_drivers/azure/api/azure_test.go +++ b/storage_drivers/azure/api/azure_test.go @@ -1092,3 +1092,235 @@ func TestCreateKeyVaultEndpoint(t *testing.T) { assert.Equal(t, expected, result, "endpoint mismatch") } + +// TestValidateCloudConfiguration_Nil tests that nil config returns AzurePublic +func TestValidateCloudConfiguration_Nil(t *testing.T) { + config, err := ValidateCloudConfiguration(nil) + assert.NoError(t, err) + assert.NotNil(t, config) + assert.Equal(t, "https://login.microsoftonline.com/", config.ActiveDirectoryAuthorityHost) +} + +// TestValidateCloudConfiguration_Empty tests that empty config returns AzurePublic +func TestValidateCloudConfiguration_Empty(t *testing.T) { + config, err := ValidateCloudConfiguration(&CloudConfiguration{}) + assert.NoError(t, err) + assert.NotNil(t, config) + assert.Equal(t, "https://login.microsoftonline.com/", config.ActiveDirectoryAuthorityHost) +} + +// TestValidateCloudConfiguration_NamedClouds tests various named cloud configurations +func TestValidateCloudConfiguration_NamedClouds(t *testing.T) { + tests := []struct { + name string + cloudName string + expectError bool + expectedAuthority string + expectedErrorString string + }{ + { + name: "AzurePublic", + cloudName: "AzurePublic", + expectError: false, + expectedAuthority: "https://login.microsoftonline.com/", + }, + { + name: "AzureChina", + cloudName: "AzureChina", + expectError: false, + expectedAuthority: "https://login.chinacloudapi.cn/", + }, + { + name: "AzureGovernment", + cloudName: "AzureGovernment", + expectError: false, + expectedAuthority: "https://login.microsoftonline.us/", + }, + { + name: "InvalidCloudName", + cloudName: "InvalidCloud", + expectError: true, + expectedErrorString: "unknown cloudName", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := ValidateCloudConfiguration(&CloudConfiguration{CloudName: tt.cloudName}) + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, config) + assert.Contains(t, err.Error(), tt.expectedErrorString) + } else { + assert.NoError(t, err) + assert.NotNil(t, config) + assert.Equal(t, tt.expectedAuthority, config.ActiveDirectoryAuthorityHost) + } + }) + } +} + +// TestValidateCloudConfiguration_CustomValid tests valid custom configuration +func TestValidateCloudConfiguration_CustomValid(t *testing.T) { + config, err := ValidateCloudConfiguration(&CloudConfiguration{ + ADAuthorityHost: "https://login.example.com/", + Audience: "https://management.example.com", + Endpoint: "https://api.example.com", + }) + assert.NoError(t, err) + assert.NotNil(t, config) + assert.Equal(t, "https://login.example.com/", config.ActiveDirectoryAuthorityHost) + assert.Equal(t, "https://management.example.com", config.Services["resourceManager"].Audience) + assert.Equal(t, "https://api.example.com", config.Services["resourceManager"].Endpoint) +} + +// TestValidateCloudConfiguration_IncompleteCustomConfig tests incomplete custom configurations +func TestValidateCloudConfiguration_IncompleteCustomConfig(t *testing.T) { + tests := []struct { + name string + config *CloudConfiguration + }{ + { + name: "OnlyADAuthorityHost", + config: &CloudConfiguration{ + ADAuthorityHost: "https://login.example.com/", + }, + }, + { + name: "OnlyAudience", + config: &CloudConfiguration{ + Audience: "https://management.example.com", + }, + }, + { + name: "OnlyEndpoint", + config: &CloudConfiguration{ + Endpoint: "https://api.example.com", + }, + }, + { + name: "ADAuthorityHostAndAudience", + config: &CloudConfiguration{ + ADAuthorityHost: "https://login.example.com/", + Audience: "https://management.example.com", + }, + }, + { + name: "ADAuthorityHostAndEndpoint", + config: &CloudConfiguration{ + ADAuthorityHost: "https://login.example.com/", + Endpoint: "https://api.example.com", + }, + }, + { + name: "AudienceAndEndpoint", + config: &CloudConfiguration{ + Audience: "https://management.example.com", + Endpoint: "https://api.example.com", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := ValidateCloudConfiguration(tt.config) + assert.Error(t, err) + assert.Nil(t, config) + assert.Contains(t, err.Error(), "all required") + }) + } +} + +// TestValidateCloudConfiguration_MutuallyExclusive tests that cloudName and custom fields are mutually exclusive +func TestValidateCloudConfiguration_MutuallyExclusive(t *testing.T) { + tests := []struct { + name string + config *CloudConfiguration + }{ + { + name: "CloudNameWithAllCustomFields", + config: &CloudConfiguration{ + CloudName: "AzurePublic", + ADAuthorityHost: "https://login.example.com/", + Audience: "https://management.example.com", + Endpoint: "https://api.example.com", + }, + }, + { + name: "CloudNameWithADAuthorityHost", + config: &CloudConfiguration{ + CloudName: "AzureChina", + ADAuthorityHost: "https://login.example.com/", + }, + }, + { + name: "CloudNameWithAudience", + config: &CloudConfiguration{ + CloudName: "AzureGovernment", + Audience: "https://management.example.com", + }, + }, + { + name: "CloudNameWithEndpoint", + config: &CloudConfiguration{ + CloudName: "AzurePublic", + Endpoint: "https://api.example.com", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := ValidateCloudConfiguration(tt.config) + assert.Error(t, err) + assert.Nil(t, config) + assert.Contains(t, err.Error(), "mutually exclusive") + }) + } +} + +// TestValidateCloudConfiguration_InvalidURL tests invalid URL validation +func TestValidateCloudConfiguration_InvalidURL(t *testing.T) { + tests := []struct { + name string + config *CloudConfiguration + expectedErrorString string + }{ + { + name: "InvalidADAuthorityHost", + config: &CloudConfiguration{ + ADAuthorityHost: "not a valid url://", + Audience: "https://management.example.com", + Endpoint: "https://api.example.com", + }, + expectedErrorString: "invalid adAuthorityHost URL", + }, + { + name: "InvalidAudience", + config: &CloudConfiguration{ + ADAuthorityHost: "https://login.example.com/", + Audience: "not a valid url://", + Endpoint: "https://api.example.com", + }, + expectedErrorString: "invalid audience URL", + }, + { + name: "InvalidEndpoint", + config: &CloudConfiguration{ + ADAuthorityHost: "https://login.example.com/", + Audience: "https://management.example.com", + Endpoint: "not a valid url://", + }, + expectedErrorString: "invalid endpoint URL", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := ValidateCloudConfiguration(tt.config) + assert.Error(t, err) + assert.Nil(t, config) + assert.Contains(t, err.Error(), tt.expectedErrorString) + }) + } +} diff --git a/storage_drivers/azure/azure_anf.go b/storage_drivers/azure/azure_anf.go index c8e3a2700..81daf613e 100644 --- a/storage_drivers/azure/azure_anf.go +++ b/storage_drivers/azure/azure_anf.go @@ -605,6 +605,17 @@ func (d *NASStorageDriver) initializeAzureSDKClient( } } + // Convert driver cloud configuration to API cloud configuration + var cloudConfig *api.CloudConfiguration + if config.CloudConfiguration != nil { + cloudConfig = &api.CloudConfiguration{ + CloudName: config.CloudConfiguration.CloudName, + ADAuthorityHost: config.CloudConfiguration.ADAuthorityHost, + Audience: config.CloudConfiguration.Audience, + Endpoint: config.CloudConfiguration.Endpoint, + } + } + clientConfig := api.ClientConfig{ SubscriptionID: config.SubscriptionID, AzureAuthConfig: azclient.AzureAuthConfig{ @@ -613,6 +624,7 @@ func (d *NASStorageDriver) initializeAzureSDKClient( }, TenantID: config.TenantID, Location: config.Location, + CloudConfig: cloudConfig, StorageDriverName: config.StorageDriverName, DebugTraceFlags: config.DebugTraceFlags, SDKTimeout: sdkTimeout, diff --git a/storage_drivers/azure/azure_anf_test.go b/storage_drivers/azure/azure_anf_test.go index 2c809c0a8..68649c8a1 100644 --- a/storage_drivers/azure/azure_anf_test.go +++ b/storage_drivers/azure/azure_anf_test.go @@ -1110,6 +1110,164 @@ func TestInitialize_FailureWithEncryptionKeysBasicNetworkFeatures(t *testing.T) assert.Error(t, result, "initialize should fail with Basic networkFeature") } +func TestInitialize_WithCloudConfigurationAzureChina(t *testing.T) { + defer acp.SetAPI(acp.API()) + + mockCtrl := gomock.NewController(t) + mockAPI, driver := newMockANFDriver(t) + mockACP := mockacp.NewMockTridentACP(mockCtrl) + acp.SetAPI(mockACP) + + commonConfig := &drivers.CommonStorageDriverConfig{ + Version: 1, + StorageDriverName: "azure-netapp-files", + BackendName: "myANFBackend", + DriverContext: tridentconfig.ContextCSI, + DebugTraceFlags: debugTraceFlags, + } + + configJSON := ` + { + "version": 1, + "storageDriverName": "azure-netapp-files", + "location": "chinaeast", + "subscriptionID": "deadbeef-173f-4bf4-b5b8-f17f8d2fe43b", + "tenantID": "deadbeef-4746-4444-a919-3b34af5f0a3c", + "clientID": "deadbeef-784c-4b35-8329-460f52a3ad50", + "clientSecret": "myClientSecret", + "cloudConfiguration": { + "cloudName": "AzureChina" + }, + "serviceLevel": "Premium", + "debugTraceFlags": {"method": true, "api": true, "discovery": true}, + "capacityPools": ["RG1/NA1/CP1"], + "virtualNetwork": "VN1", + "subnet": "RG1/VN1/SN1" + }` + + pool := &api.CapacityPool{ + Name: "CP1", + Location: "chinaeast", + NetAppAccount: "NA1", + ResourceGroup: "RG1", + } + + mockAPI.EXPECT().Init(ctx, gomock.Any()).Return(nil).Times(1) + mockACP.EXPECT().IsFeatureEnabled(ctx, acp.FeatureInflightEncryption).Return(nil).AnyTimes() + mockAPI.EXPECT().CapacityPoolsForStoragePools(ctx).Return([]*api.CapacityPool{pool}).Times(1) + + result := driver.Initialize(ctx, tridentconfig.ContextCSI, configJSON, commonConfig, + map[string]string{}, BackendUUID) + + assert.NoError(t, result, "initialize failed") + assert.NotNil(t, driver.Config.CloudConfiguration, "cloud configuration is nil") + assert.Equal(t, "AzureChina", driver.Config.CloudConfiguration.CloudName) +} + +func TestInitialize_WithCloudConfigurationCustom(t *testing.T) { + defer acp.SetAPI(acp.API()) + + mockCtrl := gomock.NewController(t) + mockAPI, driver := newMockANFDriver(t) + mockACP := mockacp.NewMockTridentACP(mockCtrl) + acp.SetAPI(mockACP) + + commonConfig := &drivers.CommonStorageDriverConfig{ + Version: 1, + StorageDriverName: "azure-netapp-files", + BackendName: "myANFBackend", + DriverContext: tridentconfig.ContextCSI, + DebugTraceFlags: debugTraceFlags, + } + + configJSON := ` + { + "version": 1, + "storageDriverName": "azure-netapp-files", + "location": "local", + "subscriptionID": "deadbeef-173f-4bf4-b5b8-f17f8d2fe43b", + "tenantID": "deadbeef-4746-4444-a919-3b34af5f0a3c", + "clientID": "deadbeef-784c-4b35-8329-460f52a3ad50", + "clientSecret": "myClientSecret", + "cloudConfiguration": { + "adAuthorityHost": "https://login.microsoftonline.azurestack.contoso.com/", + "audience": "https://management.azurestack.contoso.com", + "endpoint": "https://management.azurestack.contoso.com" + }, + "serviceLevel": "Premium", + "debugTraceFlags": {"method": true, "api": true, "discovery": true}, + "capacityPools": ["RG1/NA1/CP1"], + "virtualNetwork": "VN1", + "subnet": "RG1/VN1/SN1" + }` + + pool := &api.CapacityPool{ + Name: "CP1", + Location: "local", + NetAppAccount: "NA1", + ResourceGroup: "RG1", + } + + mockAPI.EXPECT().Init(ctx, gomock.Any()).Return(nil).Times(1) + mockACP.EXPECT().IsFeatureEnabled(ctx, acp.FeatureInflightEncryption).Return(nil).AnyTimes() + mockAPI.EXPECT().CapacityPoolsForStoragePools(ctx).Return([]*api.CapacityPool{pool}).Times(1) + + result := driver.Initialize(ctx, tridentconfig.ContextCSI, configJSON, commonConfig, + map[string]string{}, BackendUUID) + + assert.NoError(t, result, "initialize failed") + assert.NotNil(t, driver.Config.CloudConfiguration, "cloud configuration is nil") + assert.Equal(t, "https://login.microsoftonline.azurestack.contoso.com/", driver.Config.CloudConfiguration.ADAuthorityHost) + assert.Equal(t, "https://management.azurestack.contoso.com", driver.Config.CloudConfiguration.Audience) + assert.Equal(t, "https://management.azurestack.contoso.com", driver.Config.CloudConfiguration.Endpoint) +} + +func TestInitialize_WithCloudConfigurationInvalid(t *testing.T) { + defer acp.SetAPI(acp.API()) + + mockCtrl := gomock.NewController(t) + _, driver := newMockANFDriver(t) + mockACP := mockacp.NewMockTridentACP(mockCtrl) + acp.SetAPI(mockACP) + + commonConfig := &drivers.CommonStorageDriverConfig{ + Version: 1, + StorageDriverName: "azure-netapp-files", + BackendName: "myANFBackend", + DriverContext: tridentconfig.ContextCSI, + DebugTraceFlags: debugTraceFlags, + } + + // Test with mutually exclusive configuration + configJSON := ` + { + "version": 1, + "storageDriverName": "azure-netapp-files", + "location": "fake-location", + "subscriptionID": "deadbeef-173f-4bf4-b5b8-f17f8d2fe43b", + "tenantID": "deadbeef-4746-4444-a919-3b34af5f0a3c", + "clientID": "deadbeef-784c-4b35-8329-460f52a3ad50", + "clientSecret": "myClientSecret", + "cloudConfiguration": { + "cloudName": "AzurePublic", + "adAuthorityHost": "https://login.example.com/" + }, + "serviceLevel": "Premium", + "debugTraceFlags": {"method": true, "api": true, "discovery": true}, + "capacityPools": ["RG1/NA1/CP1"], + "virtualNetwork": "VN1", + "subnet": "RG1/VN1/SN1" + }` + + mockACP.EXPECT().IsFeatureEnabled(ctx, acp.FeatureInflightEncryption).Return(nil).AnyTimes() + + result := driver.Initialize(ctx, tridentconfig.ContextCSI, configJSON, commonConfig, + map[string]string{}, BackendUUID) + + assert.Error(t, result, "initialize should fail with mutually exclusive cloud configuration") + assert.Contains(t, result.Error(), "mutually exclusive") +} + func TestInitialized(t *testing.T) { tests := []struct { Expected bool diff --git a/storage_drivers/types.go b/storage_drivers/types.go index ca3c74dbe..20a5da8b5 100644 --- a/storage_drivers/types.go +++ b/storage_drivers/types.go @@ -480,21 +480,30 @@ func (d SolidfireStorageDriverConfig) SpecOnlyValidation() error { type AzureNASStorageDriverConfig struct { *CommonStorageDriverConfig - SubscriptionID string `json:"subscriptionID"` - TenantID string `json:"tenantID"` - ClientID string `json:"clientID"` - ClientSecret string `json:"clientSecret"` - Location string `json:"location"` - NfsMountOptions string `json:"nfsMountOptions"` - VolumeCreateTimeout string `json:"volumeCreateTimeout"` - SDKTimeout string `json:"sdkTimeout"` - MaxCacheAge string `json:"maxCacheAge"` - CustomerEncryptionKeys map[string]string `json:"customerEncryptionKeys"` + SubscriptionID string `json:"subscriptionID"` + TenantID string `json:"tenantID"` + ClientID string `json:"clientID"` + ClientSecret string `json:"clientSecret"` + Location string `json:"location"` + NfsMountOptions string `json:"nfsMountOptions"` + VolumeCreateTimeout string `json:"volumeCreateTimeout"` + SDKTimeout string `json:"sdkTimeout"` + MaxCacheAge string `json:"maxCacheAge"` + CustomerEncryptionKeys map[string]string `json:"customerEncryptionKeys"` + CloudConfiguration *AzureCloudConfiguration `json:"cloudConfiguration,omitempty"` AzureNASStorageDriverPool Storage []AzureNASStorageDriverPool `json:"storage"` } +// AzureCloudConfiguration allows users to specify Azure cloud environment. +type AzureCloudConfiguration struct { + CloudName string `json:"cloudName,omitempty"` // AzurePublic, AzureChina, AzureGovernment + ADAuthorityHost string `json:"adAuthorityHost,omitempty"` // e.g., https://login.chinacloudapi.cn/ + Audience string `json:"audience,omitempty"` // e.g., https://management.core.chinacloudapi.cn + Endpoint string `json:"endpoint,omitempty"` // e.g., https://management.chinacloudapi.cn +} + // AzureNASStorageDriverPool is the virtual pool definition for the ANF driver. Note that 'Region' and 'Zone' // are internal specifiers, not related to Azure's 'Location' field. type AzureNASStorageDriverPool struct { diff --git a/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.json b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.json new file mode 100644 index 000000000..104ac8926 --- /dev/null +++ b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "storageDriverName": "azure-netapp-files", + "subscriptionID": "12abc678-4774-fake-a1b2-a7abcde39312", + "tenantID": "a7abcde3-edc1-fake-b111-a7abcde356cf", + "clientID": "abcde356-bf8e-fake-c111-abcde35613aa", + "clientSecret": "rR0rUmWXfNioN1KhtHisiSAnoTherboGuskey6pU", + "location": "eastus", + "serviceLevel": "Premium", + "cloudConfiguration": { + "adAuthorityHost": "https://login.microsoftonline.azurestack.contoso.com/", + "audience": "https://management.azurestack.contoso.com", + "endpoint": "https://management.azurestack.contoso.com" + } +} diff --git a/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.yaml b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.yaml new file mode 100644 index 000000000..d655d8dfa --- /dev/null +++ b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration-custom.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Secret +metadata: + name: backend-tbc-anf-cloud-custom-secret +type: Opaque +stringData: + clientID: abcde356-bf8e-fake-c111-abcde35613aa + clientSecret: rR0rUmWXfNioN1KhtHisiSAnoTherboGuskey6pU +--- +apiVersion: trident.netapp.io/v1 +kind: TridentBackendConfig +metadata: + name: backend-tbc-anf-cloud-custom +spec: + version: 1 + storageDriverName: azure-netapp-files + subscriptionID: 12abc678-4774-fake-a1b2-a7abcde39312 + tenantID: a7abcde3-edc1-fake-b111-a7abcde356cf + location: eastus + serviceLevel: Premium + cloudConfiguration: + adAuthorityHost: https://login.microsoftonline.azurestack.contoso.com/ + audience: https://management.azurestack.contoso.com + endpoint: https://management.azurestack.contoso.com + credentials: + name: backend-tbc-anf-cloud-custom-secret diff --git a/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.json b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.json new file mode 100644 index 000000000..82f11edb9 --- /dev/null +++ b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "storageDriverName": "azure-netapp-files", + "subscriptionID": "12abc678-4774-fake-a1b2-a7abcde39312", + "tenantID": "a7abcde3-edc1-fake-b111-a7abcde356cf", + "clientID": "abcde356-bf8e-fake-c111-abcde35613aa", + "clientSecret": "rR0rUmWXfNioN1KhtHisiSAnoTherboGuskey6pU", + "location": "usgovvirginia", + "serviceLevel": "Premium", + "cloudConfiguration": { + "cloudName": "AzureGovernment" + } +} diff --git a/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.yaml b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.yaml new file mode 100644 index 000000000..456941806 --- /dev/null +++ b/trident-installer/sample-input/backends-samples/azure-netapp-files/backend-anf-cloud-configuration.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Secret +metadata: + name: backend-tbc-anf-cloud-secret +type: Opaque +stringData: + clientID: abcde356-bf8e-fake-c111-abcde35613aa + clientSecret: rR0rUmWXfNioN1KhtHisiSAnoTherboGuskey6pU +--- +apiVersion: trident.netapp.io/v1 +kind: TridentBackendConfig +metadata: + name: backend-tbc-anf-cloud +spec: + version: 1 + storageDriverName: azure-netapp-files + subscriptionID: 12abc678-4774-fake-a1b2-a7abcde39312 + tenantID: a7abcde3-edc1-fake-b111-a7abcde356cf + location: usgovvirginia + serviceLevel: Premium + cloudConfiguration: + cloudName: AzureGovernment + credentials: + name: backend-tbc-anf-cloud-secret From 20119c1f962bc9a80fe5d74a8062b7dbd4552e88 Mon Sep 17 00:00:00 2001 From: Alloyd Savio Mendonca <167860552+alloydsa@users.noreply.github.com> Date: Tue, 21 Oct 2025 18:28:25 +0530 Subject: [PATCH 07/30] Updated 3rd-party dependencies for 25.10 --- config/config.go | 12 +- .../kubernetes/plugin_test.go | 9 + go.mod | 231 ++++---- go.sum | 511 ++++++++++-------- 4 files changed, 412 insertions(+), 351 deletions(-) diff --git a/config/config.go b/config/config.go index 01e08773a..e61298483 100644 --- a/config/config.go +++ b/config/config.go @@ -297,15 +297,15 @@ const ( // Minimum and maximum supported Kubernetes versions KubernetesVersionMin = "v1.27" - KubernetesVersionMax = "v1.33" + KubernetesVersionMax = "v1.34" // KubernetesCSISidecarRegistry is where the CSI sidecar images are hosted KubernetesCSISidecarRegistry = "registry.k8s.io/sig-storage" - CSISidecarProvisionerImageTag = "csi-provisioner:v5.2.0" - CSISidecarAttacherImageTag = "csi-attacher:v4.8.1" - CSISidecarResizerImageTag = "csi-resizer:v1.13.2" - CSISidecarSnapshotterImageTag = "csi-snapshotter:v8.2.1" - CSISidecarNodeDriverRegistrarImageTag = "csi-node-driver-registrar:v2.13.0" + CSISidecarProvisionerImageTag = "csi-provisioner:v5.3.0" + CSISidecarAttacherImageTag = "csi-attacher:v4.10.0" + CSISidecarResizerImageTag = "csi-resizer:v1.14.0" + CSISidecarSnapshotterImageTag = "csi-snapshotter:v8.3.0" + CSISidecarNodeDriverRegistrarImageTag = "csi-node-driver-registrar:v2.15.0" CSISidecarLivenessProbeImageTag = "livenessprobe:v2.15.0" DefaultK8sAPIQPS = 100.0 diff --git a/frontend/csi/controller_helpers/kubernetes/plugin_test.go b/frontend/csi/controller_helpers/kubernetes/plugin_test.go index 47fdfca13..cd9546d04 100644 --- a/frontend/csi/controller_helpers/kubernetes/plugin_test.go +++ b/frontend/csi/controller_helpers/kubernetes/plugin_test.go @@ -2284,3 +2284,12 @@ func (f *fakeController) SetTransform(handler cache.TransformFunc) error func (f *fakeController) IsStopped() bool { return false } func (f *fakeController) GetIndexer() cache.Indexer { return nil } func (f *fakeController) AddIndexers(indexers cache.Indexers) error { return nil } +func (f *fakeController) AddEventHandlerWithOptions(handler cache.ResourceEventHandler, + options cache.HandlerOptions, +) (cache.ResourceEventHandlerRegistration, error) { + return nil, nil +} +func (f *fakeController) RunWithContext(ctx context.Context) {} +func (f *fakeController) SetWatchErrorHandlerWithContext(handler cache.WatchErrorHandlerWithContext) error { + return nil +} diff --git a/go.mod b/go.mod index 7071fa25c..b15fd5ff3 100644 --- a/go.mod +++ b/go.mod @@ -1,111 +1,114 @@ module github.com/netapp/trident -go 1.24.0 +go 1.24.6 require ( - cloud.google.com/go/compute v1.38.0 - cloud.google.com/go/netapp v1.9.0 // https://pkg.go.dev/cloud.google.com/go/netapp - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7 v7.5.0 + cloud.google.com/go/compute v1.49.0 + cloud.google.com/go/netapp v1.10.1 // https://pkg.go.dev/cloud.google.com/go/netapp + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7 v7.7.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures v1.2.0 - github.com/RoaringBitmap/roaring/v2 v2.5.0 - github.com/aws/aws-sdk-go-v2 v1.36.1 - github.com/aws/aws-sdk-go-v2/config v1.29.2 - github.com/aws/aws-sdk-go-v2/credentials v1.17.55 - github.com/aws/aws-sdk-go-v2/service/fsx v1.52.0 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.14 - github.com/brunoga/deep v1.2.4 + github.com/RoaringBitmap/roaring/v2 v2.10.0 + github.com/aws/aws-sdk-go-v2 v1.39.2 + github.com/aws/aws-sdk-go-v2/config v1.31.12 + github.com/aws/aws-sdk-go-v2/credentials v1.18.16 + github.com/aws/aws-sdk-go-v2/service/fsx v1.62.0 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 + github.com/brunoga/deep v1.2.5 github.com/cenkalti/backoff/v4 v4.3.0 //v5.0.2 github.com/container-storage-interface/spec v1.11.0 github.com/distribution/reference v0.6.0 github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 github.com/dustin/go-humanize v1.0.2-0.20250512220336-b48bc01a0676 - github.com/elastic/go-sysinfo v1.15.3 + github.com/elastic/go-sysinfo v1.15.4 github.com/evanphx/json-patch/v5 v5.9.11 github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // 1/18/2022 - github.com/go-openapi/errors v0.22.1 - github.com/go-openapi/runtime v0.28.0 - github.com/go-openapi/strfmt v0.23.0 - github.com/go-openapi/swag v0.23.1 - github.com/go-openapi/validate v0.24.0 + github.com/go-openapi/errors v0.22.3 + github.com/go-openapi/runtime v0.29.0 + github.com/go-openapi/strfmt v0.24.0 + github.com/go-openapi/swag v0.25.1 + github.com/go-openapi/validate v0.25.0 github.com/golang/protobuf v1.5.4 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/hpe-storage/common-host-libs v4.7.1+incompatible - github.com/jarcoal/httpmock v1.4.0 + github.com/jarcoal/httpmock v1.4.1 github.com/kr/secureheader v0.2.0 - github.com/kubernetes-csi/csi-lib-utils v0.16.0 - github.com/kubernetes-csi/csi-proxy/client v1.2.1 + github.com/kubernetes-csi/csi-lib-utils v0.22.0 + github.com/kubernetes-csi/csi-proxy/client v1.3.0 github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0 - github.com/mattermost/xml-roundtrip-validator v0.1.1-0.20230502164821-3079e7b80fca + github.com/mattermost/xml-roundtrip-validator v0.1.0 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/olekukonko/tablewriter v0.0.5 - github.com/openshift/api v0.0.0-20250530162003-e041b5efb8e4 + github.com/openshift/api v0.0.0-20251013165757-fe48e8fd548b github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/client_golang v1.23.2 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/afero v1.14.0 - github.com/spf13/cobra v1.9.1 - github.com/spf13/pflag v1.0.7 - github.com/stretchr/testify v1.10.0 + github.com/spf13/afero v1.15.0 + github.com/spf13/cobra v1.10.1 + github.com/spf13/pflag v1.0.10 + github.com/stretchr/testify v1.11.1 github.com/vishvananda/netlink v1.3.1 github.com/zcalusic/sysinfo v1.1.3 - go.uber.org/mock v0.5.2 + go.uber.org/mock v0.6.0 go.uber.org/multierr v1.11.0 // github.com/uber-go/multierr - golang.org/x/crypto v0.40.0 // github.com/golang/crypto - golang.org/x/net v0.42.0 // github.com/golang/net - golang.org/x/oauth2 v0.30.0 // github.com/golang/oauth2 - golang.org/x/sync v0.16.0 - golang.org/x/sys v0.34.0 // github.com/golang/sys - golang.org/x/text v0.27.0 // github.com/golang/text - golang.org/x/time v0.11.0 // github.com/golang/time - google.golang.org/api v0.234.0 - google.golang.org/grpc v1.73.0 // github.com/grpc/grpc-go - google.golang.org/protobuf v1.36.6 // github.com/protocolbuffers/protobuf-go + golang.org/x/crypto v0.43.0 // github.com/golang/crypto + golang.org/x/net v0.46.0 // github.com/golang/net + golang.org/x/oauth2 v0.32.0 // github.com/golang/oauth2 + golang.org/x/sync v0.17.0 + golang.org/x/sys v0.37.0 // github.com/golang/sys + golang.org/x/text v0.30.0 // github.com/golang/text + golang.org/x/time v0.14.0 // github.com/golang/time + google.golang.org/api v0.252.0 + google.golang.org/grpc v1.76.0 // github.com/grpc/grpc-go + google.golang.org/protobuf v1.36.10 // github.com/protocolbuffers/protobuf-go gopkg.in/yaml.v2 v2.4.0 // github.com/go-yaml/yaml - k8s.io/api v0.32.1 // github.com/kubernetes/api - k8s.io/apiextensions-apiserver v0.32.1 // github.com/kubernetes/apiextensions-apiserver - k8s.io/apimachinery v0.32.1 // github.com/kubernetes/apimachinery - k8s.io/client-go v0.32.1 // github.com/kubernetes/client-go - k8s.io/mount-utils v0.32.1 // github.com/kubernetes/mount-utils - k8s.io/utils v0.0.0-20241210054802-24370beab758 // github.com/kubernetes/utils - sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.0.50 // github.com/kubernetes-sigs/cloud-provider-azure + k8s.io/api v0.34.1 // github.com/kubernetes/api + k8s.io/apiextensions-apiserver v0.34.1 // github.com/kubernetes/apiextensions-apiserver + k8s.io/apimachinery v0.34.1 // github.com/kubernetes/apimachinery + k8s.io/client-go v0.34.1 // github.com/kubernetes/client-go + k8s.io/mount-utils v0.34.1 // github.com/kubernetes/mount-utils + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // github.com/kubernetes/utils + sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.9.3 // github.com/kubernetes-sigs/cloud-provider-azure ) require ( - cloud.google.com/go v0.121.0 // indirect - cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go v0.121.6 // indirect + cloud.google.com/go/auth v0.17.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/longrunning v0.6.7 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.4.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4 v4.8.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.6.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.1.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage/v2 v2.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect + github.com/Azure/msi-dataplane v0.4.3 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.25 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.10 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.12 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.10 // indirect - github.com/aws/smithy-go v1.22.2 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect + github.com/aws/smithy-go v1.23.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -113,75 +116,83 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/elastic/go-windows v1.0.2 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/jsonpointer v0.21.1 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/loads v0.22.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/analysis v0.24.0 // indirect + github.com/go-openapi/jsonpointer v0.22.1 // indirect + github.com/go-openapi/jsonreference v0.21.2 // indirect + github.com/go-openapi/loads v0.23.1 // indirect + github.com/go-openapi/spec v0.22.0 // indirect + github.com/go-openapi/swag/cmdutils v0.25.1 // indirect + github.com/go-openapi/swag/conv v0.25.1 // indirect + github.com/go-openapi/swag/fileutils v0.25.1 // indirect + github.com/go-openapi/swag/jsonname v0.25.1 // indirect + github.com/go-openapi/swag/jsonutils v0.25.1 // indirect + github.com/go-openapi/swag/loading v0.25.1 // indirect + github.com/go-openapi/swag/mangling v0.25.1 // indirect + github.com/go-openapi/swag/netutils v0.25.1 // indirect + github.com/go-openapi/swag/stringutils v0.25.1 // indirect + github.com/go-openapi/swag/typeutils v0.25.1 // indirect + github.com/go-openapi/swag/yamlutils v0.25.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v5 v5.2.2 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.14.2 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/mailru/easyjson v0.9.0 // indirect - github.com/mattn/go-runewidth v0.0.10 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect - github.com/moby/sys/userns v0.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/rivo/uniseg v0.1.0 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver v1.17.4 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/tools v0.35.0 // indirect - google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/term v0.36.0 // indirect + golang.org/x/tools v0.37.0 // indirect + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 88ef6ca40..5019d0c3d 100644 --- a/go.sum +++ b/go.sum @@ -1,111 +1,121 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.121.0 h1:pgfwva8nGw7vivjZiRfrmglGWiCJBP+0OmDpenG/Fwg= -cloud.google.com/go v0.121.0/go.mod h1:rS7Kytwheu/y9buoDmu5EIpMMCI4Mb8ND4aeN4Vwj7Q= -cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= -cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= +cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= +cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= +cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute v1.38.0 h1:MilCLYQW2m7Dku8hRIIKo4r0oKastlD74sSu16riYKs= -cloud.google.com/go/compute v1.38.0/go.mod h1:oAFNIuXOmXbK/ssXm3z4nZB8ckPdjltJ7xhHCdbWFZM= -cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= -cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +cloud.google.com/go/compute v1.49.0 h1:gg+/OB49pK5cznJqR8UE7s7/4+GekkSs7wtYtt8m8Pg= +cloud.google.com/go/compute v1.49.0/go.mod h1:1uoZvP8Avyfhe3Y4he7sMOR16ZiAm2Q+Rc2P5rrJM28= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= -cloud.google.com/go/netapp v1.9.0 h1:alkHx3/mAVnlh1ZIZ8YMeQUQNIR+kOzJUjqmfgK6acg= -cloud.google.com/go/netapp v1.9.0/go.mod h1:+x5Fke/VewaIts8yw5EXeHcONezVbTHUIAhTmlPE7u0= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= +cloud.google.com/go/netapp v1.10.1 h1:Rh57E98p+vlQo9kXf55RalIl6BJTebsCNN1TqjRBtYY= +cloud.google.com/go/netapp v1.10.1/go.mod h1:SBvDfPaVjPH3hkPNWpJ+D3UsrDRR5C9Nio83sQZCCQc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 h1:wL5IEG5zb7BVv1Kv0Xm92orq+5hB5Nipn3B5tn4Rqfk= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0 h1:Bg8m3nq/X1DeePkAbCfb6ml6F3F0IunEhE8TMh+lY48= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 h1:Hp+EScFOu9HeCbeW8WU2yQPJd4gGwhMgKxWe+G6jNzw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0/go.mod h1:/pz8dyNQe+Ey3yBp/XuYz7oqX8YDNWVpPB0hH3XWfbc= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0/go.mod h1:QyiQdW4f4/BIfB8ZutZ2s+28RAgfa/pT+zS++ZHyM1I= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.4.0 h1:z7Mqz6l0EFH549GvHEqfjKvi+cRScxLWbaoeLm9wxVQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.4.0/go.mod h1:v6gbfH+7DG7xH2kUNs+ZJ9tF6O3iNnR85wMtmr+F54o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0 h1:DWlwvVV5r/Wy1561nZ3wrpI1/vDIBRY/Wd1HWaRBZWA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry v1.2.0/go.mod h1:E7ltexgRDmeJ0fJWv0D/HLwY2xbDdN+uv+X2uZtOx3w= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4 v4.8.0 h1:0nGmzwBv5ougvzfGPCO2ljFRHvun57KpNrVCMrlk0ns= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4 v4.8.0/go.mod h1:gYq8wyDgv6JLhGbAU6gg8amCPgQWRE+aCvrV2gyzdfs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0/go.mod h1:HcZY0PHPo/7d75p99lB6lK0qYOP4vLRJUBpiehYXtLQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.6.0 h1:xkWEcbsnJWid3rOf/S/LOHy1I55JA+4kw/f8Tnm+Onc= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.6.0/go.mod h1:OWKfCmX4X3Vp2w7GSx1LZn8566tOHJBA6K0IAUVNYx0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.0.0 h1:Kb8eVvjdP6kZqYnER5w/PiGCFp91yVgaxve3d7kCEpY= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.0.0/go.mod h1:lYq15QkJyEsNegz5EhI/0SXQ6spvGfgwBH/Qyzkoc/s= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0 h1:HlZMUZW8S4P9oob1nCHxCCKrytxyLc+24nUJGssoEto= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.4.0/go.mod h1:StGsLbuJh06Bd8IBfnAlIFV3fLb+gkczONWf15hpX2E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0 h1:nnQ9vXH039UrEFxi08pPuZBE7VfqSJt343uJLw0rhWI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault v1.5.0/go.mod h1:4YIVtzMFVsPwBvitCDX7J9sqthSj43QD1sP6fYc1egc= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7 v7.5.0 h1:EVUTdXtXYy2Fk4W9UukantrWmeb1Rwfqkd6X2k2HFo8= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7 v7.5.0/go.mod h1:JA//9UWkJI1Eh6yd+VisRlPMkiFXWrsqJB2Fk+uuxLE= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 h1:bXwSugBiSbgtz7rOtbfGf+woewp4f06orW9OP5BjHLA= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0/go.mod h1:Y/HgrePTmGy9HjdSGTqZNa+apUpTVIEVKXJyARP2lrk= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 h1:9Eih8XcEeQnFD0ntMlUDleKMzfeCeUfa+VbnDCI4AZs= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0/go.mod h1:wGPyTi+aURdqPAGMZDQqnNs9IrShADF8w2WZb6bKeq0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0 h1:z4YeiSXxnUI+PqB46Yj6MZA3nwb1CcJIkEMDrzUd8Cs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.2.0/go.mod h1:rko9SzMxcMk0NJsNAxALEGaTYyy79bNRwxgJfrH0Spw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7 v7.7.0 h1:/bInrH+UiKk21tXyPpX60XsD7edsggrlrI4S6RKqPQY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/netapp/armnetapp/v7 v7.7.0/go.mod h1:5qSzgQYMuIAXNNsnKJFlnaCCijSMkKRS9iamllg/cq0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0 h1:HYGD75g0bQ3VO/Omedm54v4LrD3B1cGImuRF3AJ5wLo= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0/go.mod h1:ulHyBFJOI0ONiRL4vcJTmS7rx18jQQlEPmAgo80cRdM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 h1:yzrctSl9GMIQ5lHu7jc8olOsGjWDCsBpJhWqfGa/YIM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0/go.mod h1:GE4m0rnnfwLGX0Y9A9A25Zx5N/90jneT5ABevqzhuFQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 h1:zLzoX5+W2l95UJoVwiyNS4dX8vHyQ6x2xRLoBBL9wMk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures v1.2.0 h1:wIDqH4WA5uJ6irRqjzodeSw6Pmp0tu3oIbwzBZEdMfQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures v1.2.0/go.mod h1:g8mnARUMaYRsg80mxm3PxjF7+oUotB/lneDbwYbGNxg= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.1.0 h1:h4Zxgmi9oyZL2l8jeg1iRTqPloHktywWcu0nlJmo1tA= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.1.0/go.mod h1:LgLGXawqSreJz135Elog0ywTJDsm0Hz2k+N+6ZK35u8= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage/v2 v2.0.0 h1:+vh02EiRx2UmL9NDoA36U18Bgwl9luxs6ia0GAI9Rzg= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage/v2 v2.0.0/go.mod h1:iKOtU3WyuNvNc4L1Z4IxHaoO0dGq5tg+uhLix/KRmzE= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 h1:/g8S6wk65vfC6m3FIxJ+i5QDyN9JWwXI8Hb0Img10hU= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0/go.mod h1:gpl+q95AzZlKVI3xSoseF9QPrypk0hQqBiJYeB/cR/I= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= +github.com/Azure/msi-dataplane v0.4.3 h1:dWPWzY4b54tLIR9T1Q014Xxd/1DxOsMIp6EjRFAJlQY= +github.com/Azure/msi-dataplane v0.4.3/go.mod h1:yAfxdJyvcnvSDfSyOFV9qm4fReEQDl+nZLGeH2ZWSmw= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/RoaringBitmap/roaring/v2 v2.5.0 h1:TJ45qCM7D7fIEBwKd9zhoR0/S1egfnSSIzLU1e1eYLY= -github.com/RoaringBitmap/roaring/v2 v2.5.0/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= +github.com/RoaringBitmap/roaring/v2 v2.10.0 h1:HbJ8Cs71lfCJyvmSptxeMX2PtvOC8yonlU0GQcy2Ak0= +github.com/RoaringBitmap/roaring/v2 v2.10.0/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/config v1.29.2 h1:JuIxOEPcSKpMB0J+khMjznG9LIhIBdmqNiEcPclnwqc= -github.com/aws/aws-sdk-go-v2/config v1.29.2/go.mod h1:HktTHregOZwNSM/e7WTfVSu9RCX+3eOv+6ij27PtaYs= -github.com/aws/aws-sdk-go-v2/credentials v1.17.55 h1:CDhKnDEaGkLA5ZszV/qw5uwN5M8rbv9Cl0JRN+PRsaM= -github.com/aws/aws-sdk-go-v2/credentials v1.17.55/go.mod h1:kPD/vj+RB5MREDUky376+zdnjZpR+WgdBBvwrmnlmKE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.25 h1:kU7tmXNaJ07LsyN3BUgGqAmVmQtq0w6duVIHAKfp0/w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.25/go.mod h1:OiC8+OiqrURb1wrwmr/UbOVLFSWEGxjinj5C299VQdo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/fsx v1.52.0 h1:dfCPvsrDuWivFMnhsAqKhOOIyTK+uKCLlz15PVV6SyM= -github.com/aws/aws-sdk-go-v2/service/fsx v1.52.0/go.mod h1:gnNrZVY5gL3FWp4lppI6lfKy+mVwycjYcn0bKev9uUc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.10 h1:hN4yJBGswmFTOVYqmbz1GBs9ZMtQe8SrYxPwrkrlRv8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.10/go.mod h1:TsxON4fEZXyrKY+D+3d2gSTyJkGORexIYab9PTf56DA= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.14 h1:rhT0h8cSV5ZNZWy67Eqe3OQTFGRu9xwgyFsuGeIXmGQ= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.14/go.mod h1:CLEjbx0xH3ptihCb1l0XlrqoGfWD9xU0na47/s7fR/s= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.12 h1:kznaW4f81mNMlREkU9w3jUuJvU5g/KsqDV43ab7Rp6s= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.12/go.mod h1:bZy9r8e0/s0P7BSDHgMLXK2KvdyRRBIQ2blKlvLt0IU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.11 h1:mUwIpAvILeKFnRx4h1dEgGEFGuV8KJ3pEScZWVFYuZA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.11/go.mod h1:JDJtD+b8HNVv71axz8+S5492KM8wTzHRFpMKQbPlYxw= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.10 h1:g9d+TOsu3ac7SgmY2dUf1qMgu/uJVTlQ4VCbH6hRxSw= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.10/go.mod h1:WZfNmntu92HO44MVZAubQaz3qCuIdeOdog2sADfU6hU= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= +github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= +github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/fsx v1.62.0 h1:by2Uy4YkY+kddlqUXziLUo+ORa5d5Zba7+9tDyB+nSc= +github.com/aws/aws-sdk-go-v2/service/fsx v1.62.0/go.mod h1:IYOHN0ZkhnOc76Wq3jA9p7EBmcyUrD7ovglUA7thwAA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 h1:9PWl450XOG+m5lKv+qg5BXso1eLxpsZLqq7VPug5km0= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6/go.mod h1:hwt7auGsDcaNQ8pzLgE2kCNyIWouYlAKSjuUu5Dqr7I= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/brunoga/deep v1.2.4 h1:Aj9E9oUbE+ccbyh35VC/NHlzzjfIVU69BXu2mt2LmL8= -github.com/brunoga/deep v1.2.4/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= +github.com/brunoga/deep v1.2.5 h1:bigq4eooqbeJXfvTfZBn3AH3B1iW+rtetxVeh0GiLrg= +github.com/brunoga/deep v1.2.5/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -113,6 +123,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/container-storage-interface/spec v1.11.0 h1:H/YKTOeUZwHtyPOr9raR+HgFmGluGCklulxDYxSdVNM= github.com/container-storage-interface/spec v1.11.0/go.mod h1:DtUvaQszPml1YJfIK7c00mlv6/g4wNMLanLgiUbKFRI= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= @@ -122,8 +134,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -132,57 +142,90 @@ github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 h1:IMfrF github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA= github.com/dustin/go-humanize v1.0.2-0.20250512220336-b48bc01a0676 h1:OxZLiOygu9PckrgV21IcpZEr4bqnGtnfbsrgs1Hu38s= github.com/dustin/go-humanize v1.0.2-0.20250512220336-b48bc01a0676/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/elastic/go-sysinfo v1.15.3 h1:W+RnmhKFkqPTCRoFq2VCTmsT4p/fwpo+3gKNQsn1XU0= -github.com/elastic/go-sysinfo v1.15.3/go.mod h1:K/cNrqYTDrSoMh2oDkYEMS2+a72GRxMvNP+GC+vRIlo= +github.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886eyiv1Q= +github.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU= github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/go-faker/faker/v4 v4.6.1 h1:xUyVpAjEtB04l6XFY0V/29oR332rOSPWV4lU8RwDt4k= +github.com/go-faker/faker/v4 v4.6.1/go.mod h1:arSdxNCSt7mOhdk8tEolvHeIJ7eX4OX80wXjKKvkKBY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= -github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= -github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= -github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= -github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= +github.com/go-openapi/analysis v0.24.0 h1:vE/VFFkICKyYuTWYnplQ+aVr45vlG6NcZKC7BdIXhsA= +github.com/go-openapi/analysis v0.24.0/go.mod h1:GLyoJA+bvmGGaHgpfeDh8ldpGo69fAJg7eeMDMRCIrw= +github.com/go-openapi/errors v0.22.3 h1:k6Hxa5Jg1TUyZnOwV2Lh81j8ayNw5VVYLvKrp4zFKFs= +github.com/go-openapi/errors v0.22.3/go.mod h1:+WvbaBBULWCOna//9B9TbLNGSFOfF8lY9dw4hGiEiKQ= +github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= +github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= +github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU= +github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ= +github.com/go-openapi/loads v0.23.1 h1:H8A0dX2KDHxDzc797h0+uiCZ5kwE2+VojaQVaTlXvS0= +github.com/go-openapi/loads v0.23.1/go.mod h1:hZSXkyACCWzWPQqizAv/Ye0yhi2zzHwMmoXQ6YQml44= +github.com/go-openapi/runtime v0.29.0 h1:Y7iDTFarS9XaFQ+fA+lBLngMwH6nYfqig1G+pHxMRO0= +github.com/go-openapi/runtime v0.29.0/go.mod h1:52HOkEmLL/fE4Pg3Kf9nxc9fYQn0UsIWyGjGIJE9dkg= +github.com/go-openapi/spec v0.22.0 h1:xT/EsX4frL3U09QviRIZXvkh80yibxQmtoEvyqug0Tw= +github.com/go-openapi/spec v0.22.0/go.mod h1:K0FhKxkez8YNS94XzF8YKEMULbFrRw4m15i2YUht4L0= +github.com/go-openapi/strfmt v0.24.0 h1:dDsopqbI3wrrlIzeXRbqMihRNnjzGC+ez4NQaAAJLuc= +github.com/go-openapi/strfmt v0.24.0/go.mod h1:Lnn1Bk9rZjXxU9VMADbEEOo7D7CDyKGLsSKekhFr7s4= +github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8= +github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo= +github.com/go-openapi/swag/cmdutils v0.25.1 h1:nDke3nAFDArAa631aitksFGj2omusks88GF1VwdYqPY= +github.com/go-openapi/swag/cmdutils v0.25.1/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= +github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= +github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU= +github.com/go-openapi/swag/fileutils v0.25.1/go.mod h1:+NXtt5xNZZqmpIpjqcujqojGFek9/w55b3ecmOdtg8M= +github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= +github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= +github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= +github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= +github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= +github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= +github.com/go-openapi/swag/mangling v0.25.1 h1:XzILnLzhZPZNtmxKaz/2xIGPQsBsvmCjrJOWGNz/ync= +github.com/go-openapi/swag/mangling v0.25.1/go.mod h1:CdiMQ6pnfAgyQGSOIYnZkXvqhnnwOn997uXZMAd/7mQ= +github.com/go-openapi/swag/netutils v0.25.1 h1:2wFLYahe40tDUHfKT1GRC4rfa5T1B4GWZ+msEFA4Fl4= +github.com/go-openapi/swag/netutils v0.25.1/go.mod h1:CAkkvqnUJX8NV96tNhEQvKz8SQo2KF0f7LleiJwIeRE= +github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= +github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= +github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= +github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= +github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= +github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/validate v0.25.0 h1:JD9eGX81hDTjoY3WOzh6WqxVBVl7xjsLnvDo1GL5WPU= +github.com/go-openapi/validate v0.25.0/go.mod h1:SUY7vKrN5FiwK6LyvSwKjDfLNirSfWwHNgxd2l29Mmw= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -197,21 +240,18 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -219,25 +259,23 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= -github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/hpe-storage/common-host-libs v4.7.1+incompatible h1:ndkULLQW8eul2xQZTKDAP3iGEBRhXkxj9Sc8/alXzVI= github.com/hpe-storage/common-host-libs v4.7.1+incompatible/go.mod h1:qQxvwt4l9C79p2V8bY1P13As1+ylyznKJVp3K2P5bz8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k= -github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= +github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A= +github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -249,38 +287,33 @@ github.com/kr/secureheader v0.2.0 h1:Fe/BS3McH8EGMSc+HzaZkkRnrCyx2gq9kSVgLbyBNrA github.com/kr/secureheader v0.2.0/go.mod h1:PfvbGMMfqBg6z+vxKGKbSJRcmASZc4klL5DiW9V5iLI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kubernetes-csi/csi-lib-utils v0.16.0 h1:LXCvkhXHtFOkl7LoDqFdho/MuebccZqWxLwhKiRGiBg= -github.com/kubernetes-csi/csi-lib-utils v0.16.0/go.mod h1:fp1Oik+45tP2o4X9SD/SBWXLTQYT9wtLxGasBE3+vBI= -github.com/kubernetes-csi/csi-proxy/client v1.2.1 h1:6ApXTKp5Rhb+2PrPfnk6C+TDG7XwnWJ9/xmmq7iN5cw= -github.com/kubernetes-csi/csi-proxy/client v1.2.1/go.mod h1:SfK4HVKQdMH5KrffivddAWgX5hl3P5KmnuOTBbDNboU= +github.com/kubernetes-csi/csi-lib-utils v0.22.0 h1:EUAs1+uHGps3OtVj4XVx16urhpI02eu+Z8Vps6plpHY= +github.com/kubernetes-csi/csi-lib-utils v0.22.0/go.mod h1:f+PalKyS4Ujsjb9+m6Rj0W6c28y3nfea3paQ/VqjI28= +github.com/kubernetes-csi/csi-proxy/client v1.3.0 h1:c8JAHmspuI5w+/IBuspdartuFIZ0LcKOvz6lbHyi7WQ= +github.com/kubernetes-csi/csi-proxy/client v1.3.0/go.mod h1:SfK4HVKQdMH5KrffivddAWgX5hl3P5KmnuOTBbDNboU= github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0 h1:Q3jQ1NkFqv5o+F8dMmHd8SfEmlcwNeo1immFApntEwE= github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0/go.mod h1:E3vdYxHj2C2q6qo8/Da4g7P+IcwqRZyy3gJBzYybV9Y= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/mattermost/xml-roundtrip-validator v0.1.1-0.20230502164821-3079e7b80fca h1:TsdNYsfVbY0KKLQPNWupAj/+8getyMQd/5X3haqHvt4= -github.com/mattermost/xml-roundtrip-validator v0.1.1-0.20230502164821-3079e7b80fca/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= +github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= +github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX48g9wI= github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -291,49 +324,47 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/openshift/api v0.0.0-20250530162003-e041b5efb8e4 h1:E/NGBIipQbG6+BUuABD+N4bGQawqhLFNx/yX75FwhuY= -github.com/openshift/api v0.0.0-20250530162003-e041b5efb8e4/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openshift/api v0.0.0-20251013165757-fe48e8fd548b h1:X18aj8dcvmGC9T7xiHHz3B9YRT4b5KiX/snG27cj9mc= +github.com/openshift/api v0.0.0-20251013165757-fe48e8fd548b/go.mod h1:SPLf21TYPipzCO67BURkCfK6dcIIxx0oNRVWaOyRcXM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= -github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= -github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -343,8 +374,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= @@ -357,33 +388,39 @@ github.com/zcalusic/sysinfo v1.1.3 h1:u/AVENkuoikKuIZ4sUEJ6iibpmQP6YpGD8SSMCrqAF github.com/zcalusic/sysinfo v1.1.3/go.mod h1:NX+qYnWGtJVPV0yWldff9uppNKU4h40hJIRPf/pGLv4= go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= @@ -392,8 +429,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -402,18 +439,18 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= +golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -424,16 +461,16 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -442,32 +479,34 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.234.0 h1:d3sAmYq3E9gdr2mpmiWGbm9pHsA/KJmyiLkwKfHBqU4= -google.golang.org/api v0.234.0/go.mod h1:QpeJkemzkFKe5VCE/PMv7GsUfn9ZF+u+q1Q7w6ckxTg= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI= +google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= -google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= -google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -477,8 +516,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -502,27 +541,29 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= -k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= -k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= -k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= -k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= -k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= -k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/mount-utils v0.32.1 h1:RJOD6xXzEJT/OOJoG1KstfVa8ZXJJPlHb+t2MoulPHM= -k8s.io/mount-utils v0.32.1/go.mod h1:Kun5c2svjAPx0nnvJKYQWhfeNW+O0EpzHgRhDcYoSY0= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.0.50 h1:l9igMANNptVwYmZrqGS51oW0zvfSxBGmlOaDPe407FI= -sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.0.50/go.mod h1:1M90A+akyTabHVnveSKlvIO/Kk9kEr1LjRx+08twKVU= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/mount-utils v0.34.1 h1:zMBEFav8Rxwm54S8srzy5FxAc4KQ3X4ZcjnqTCzHmZk= +k8s.io/mount-utils v0.34.1/go.mod h1:MIjjYlqJ0ziYQg0MO09kc9S96GIcMkhF/ay9MncF0GA= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.9.3 h1:wvnrIKlj7DwbgEniLI/gHdu8i8ovWO5F7bn+ziTQmAc= +sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.9.3/go.mod h1:CHkQgjSn8glz9dwobLjId1POadnXB0puBwklI2tJcyk= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From 3912ea7a53f96a1d0b6386ba871100f622caff67 Mon Sep 17 00:00:00 2001 From: Joe Webster <31218426+jwebster7@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:39:55 -0500 Subject: [PATCH 08/30] Fix dirty node publish enforcement This commit changes Trident to reject new CSI ControllerPublishVolume calls for CO nodes that have potentially unclean attachment states. --- storage_drivers/ontap/ontap_asa.go | 6 +- storage_drivers/ontap/ontap_asa_nvme.go | 4 + storage_drivers/ontap/ontap_common.go | 27 ++++++ storage_drivers/ontap/ontap_common_test.go | 100 +++++++++++++++++++++ storage_drivers/ontap/ontap_nas.go | 16 +--- storage_drivers/ontap/ontap_nas_qtree.go | 16 +--- storage_drivers/ontap/ontap_san.go | 4 + storage_drivers/ontap/ontap_san_economy.go | 4 + storage_drivers/ontap/ontap_san_nvme.go | 4 + 9 files changed, 150 insertions(+), 31 deletions(-) diff --git a/storage_drivers/ontap/ontap_asa.go b/storage_drivers/ontap/ontap_asa.go index 56302217e..485149ce8 100644 --- a/storage_drivers/ontap/ontap_asa.go +++ b/storage_drivers/ontap/ontap_asa.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package ontap @@ -1323,6 +1323,10 @@ func (d *ASAStorageDriver) CanEnablePublishEnforcement() bool { return true } +func (d *ASAStorageDriver) HealVolumePublishEnforcement(ctx context.Context, volume *storage.Volume) bool { + return HealSANPublishEnforcement(ctx, d, volume) +} + // CreateASALUNInternalID creates a string in the format /svm//lun/ func (d *ASAStorageDriver) CreateASALUNInternalID(svm, name string) string { return fmt.Sprintf("/svm/%s/lun/%s", svm, name) diff --git a/storage_drivers/ontap/ontap_asa_nvme.go b/storage_drivers/ontap/ontap_asa_nvme.go index 31b24fe41..abb570d84 100644 --- a/storage_drivers/ontap/ontap_asa_nvme.go +++ b/storage_drivers/ontap/ontap_asa_nvme.go @@ -1479,6 +1479,10 @@ func (d *ASANVMeStorageDriver) CanEnablePublishEnforcement() bool { return true } +func (d *ASANVMeStorageDriver) HealVolumePublishEnforcement(ctx context.Context, volume *storage.Volume) bool { + return HealSANPublishEnforcement(ctx, d, volume) +} + // CreateASANVMeNamespaceInternalID creates a string in the format /svm// func (d *ASANVMeStorageDriver) CreateASANVMeNamespaceInternalID(svm, name string) string { return fmt.Sprintf("/svm/%s/namespace/%s", svm, name) diff --git a/storage_drivers/ontap/ontap_common.go b/storage_drivers/ontap/ontap_common.go index 49989d1cd..c929b7dfd 100644 --- a/storage_drivers/ontap/ontap_common.go +++ b/storage_drivers/ontap/ontap_common.go @@ -4576,6 +4576,33 @@ func EnableSANPublishEnforcement( return nil } +// HealSANPublishEnforcement is a no-op for ONTAP-SAN volumes because ONTAP-SAN already properly sets +// the LUN mappings during publish/unpublish, +// operations. This function is implemented to satisfy interface assertions on the drivers. +func HealSANPublishEnforcement(_ context.Context, _ storage.Driver, _ *storage.Volume) bool { + return false +} + +// HealNASPublishEnforcement checks if publish enforcement should be enabled on the given NAS volume +// and updates the volume config accordingly. It returns true if the volume config was updated. +func HealNASPublishEnforcement(ctx context.Context, driver storage.Driver, volume *storage.Volume) bool { + var updated bool + // Check if publish enforcement is already set. + if volume.Config.AccessInfo.PublishEnforcement { + // If publish enforcement is already enabled on the volume, nothing to do. + return updated + } + + policy := volume.Config.ExportPolicy + driverConfig := driver.GetCommonConfig(ctx) + if policy == getEmptyExportPolicyName(*driverConfig.StoragePrefix) || + policy == volume.Config.InternalName { + volume.Config.AccessInfo.PublishEnforcement = true + updated = true + } + return updated +} + func ValidateStoragePrefixEconomy(storagePrefix string) error { // Ensure storage prefix is compatible with ONTAP matched, err := regexp.MatchString(`^$|^[a-zA-Z0-9_.-]*$`, storagePrefix) diff --git a/storage_drivers/ontap/ontap_common_test.go b/storage_drivers/ontap/ontap_common_test.go index 6934edccc..0fabf9611 100644 --- a/storage_drivers/ontap/ontap_common_test.go +++ b/storage_drivers/ontap/ontap_common_test.go @@ -29,6 +29,7 @@ import ( "github.com/netapp/trident/storage" sa "github.com/netapp/trident/storage_attribute" drivers "github.com/netapp/trident/storage_drivers" + fakeDriver "github.com/netapp/trident/storage_drivers/fake" "github.com/netapp/trident/storage_drivers/ontap/api" "github.com/netapp/trident/storage_drivers/ontap/api/azgo" "github.com/netapp/trident/storage_drivers/ontap/api/rest/client/networking" @@ -10370,3 +10371,102 @@ func TestCleanupFailedCloneFlexVol(t *testing.T) { }) } } + +func TestHealNASPublishEnforcement(t *testing.T) { + tt := map[string]struct { + makeDriver func() storage.Driver + volume *storage.Volume + assertBool assert.BoolAssertionFunc + }{ + "enables publish enforcement when policy is the same as internal name": { + makeDriver: func() storage.Driver { + config := drivers.FakeStorageDriverConfig{ + CommonStorageDriverConfig: &drivers.CommonStorageDriverConfig{ + StorageDriverName: "fakeDriver", + StoragePrefix: convert.ToPtr("fake_"), + }, + } + return fakeDriver.NewFakeStorageDriver(ctx, config) + }, + volume: &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "pvc-test-name", + ExportPolicy: "pvc-test-name", + AccessInfo: tridentmodels.VolumeAccessInfo{ + PublishEnforcement: false, + }, + }, + }, + assertBool: assert.True, + }, + "enables publish enforcement when policy is the empty export policy": { + makeDriver: func() storage.Driver { + config := drivers.FakeStorageDriverConfig{ + CommonStorageDriverConfig: &drivers.CommonStorageDriverConfig{ + StorageDriverName: "fakeDriver", + StoragePrefix: convert.ToPtr("fake_"), + }, + } + return fakeDriver.NewFakeStorageDriver(ctx, config) + }, + volume: &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "pvc-test-name", + ExportPolicy: getEmptyExportPolicyName("fake_"), + AccessInfo: tridentmodels.VolumeAccessInfo{ + PublishEnforcement: false, + }, + }, + }, + assertBool: assert.True, + }, + "does not enable publish enforcement when policy is not empty and policy is not the same as the volume": { + makeDriver: func() storage.Driver { + config := drivers.FakeStorageDriverConfig{ + CommonStorageDriverConfig: &drivers.CommonStorageDriverConfig{ + StorageDriverName: "fakeDriver", + StoragePrefix: convert.ToPtr("fake_"), + }, + } + return fakeDriver.NewFakeStorageDriver(ctx, config) + }, + volume: &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "pvc-test-name", + ExportPolicy: "trident-export-policy", + AccessInfo: tridentmodels.VolumeAccessInfo{ + PublishEnforcement: false, + }, + }, + }, + assertBool: assert.False, + }, + "does not update if publish enforcement is alread set": { + makeDriver: func() storage.Driver { + config := drivers.FakeStorageDriverConfig{ + CommonStorageDriverConfig: &drivers.CommonStorageDriverConfig{ + StorageDriverName: "fakeDriver", + StoragePrefix: convert.ToPtr("fake_"), + }, + } + return fakeDriver.NewFakeStorageDriver(ctx, config) + }, + volume: &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "pvc-test-name", + ExportPolicy: "trident-export-policy", + AccessInfo: tridentmodels.VolumeAccessInfo{ + PublishEnforcement: true, + }, + }, + }, + assertBool: assert.False, + }, + } + + for name, fixture := range tt { + t.Run(name, func(t *testing.T) { + fixture.assertBool(t, HealNASPublishEnforcement(ctx, fixture.makeDriver(), fixture.volume)) + }) + } +} diff --git a/storage_drivers/ontap/ontap_nas.go b/storage_drivers/ontap/ontap_nas.go index be7c9432b..d09921425 100644 --- a/storage_drivers/ontap/ontap_nas.go +++ b/storage_drivers/ontap/ontap_nas.go @@ -1991,19 +1991,5 @@ func (d *NASStorageDriver) CanEnablePublishEnforcement() bool { func (d *NASStorageDriver) HealVolumePublishEnforcement( ctx context.Context, vol *storage.Volume, ) bool { - var updated bool - // Check of publish enforcment is already set - if vol.Config.AccessInfo.PublishEnforcement { - // If publish enforcement is already enabled on the volume, nothing to do. - return updated - } - - policy := vol.Config.ExportPolicy - driverConfig := d.GetCommonConfig(ctx) - if policy == getEmptyExportPolicyName(*driverConfig.StoragePrefix) || - policy == vol.Config.InternalName { - vol.Config.AccessInfo.PublishEnforcement = true - updated = true - } - return updated + return HealNASPublishEnforcement(ctx, d, vol) } diff --git a/storage_drivers/ontap/ontap_nas_qtree.go b/storage_drivers/ontap/ontap_nas_qtree.go index b9fc9082c..d5d679c2b 100644 --- a/storage_drivers/ontap/ontap_nas_qtree.go +++ b/storage_drivers/ontap/ontap_nas_qtree.go @@ -2789,19 +2789,5 @@ func (d *NASQtreeStorageDriver) CanEnablePublishEnforcement() bool { func (d *NASQtreeStorageDriver) HealVolumePublishEnforcement( ctx context.Context, vol *storage.Volume, ) bool { - var updated bool - // Check of publish enforcment is already set - if vol.Config.AccessInfo.PublishEnforcement { - // If publish enforcement is already enabled on the volume, nothing to do. - return updated - } - - policy := vol.Config.ExportPolicy - driverConfig := d.GetCommonConfig(ctx) - if policy == getEmptyExportPolicyName(*driverConfig.StoragePrefix) || - policy == vol.Config.InternalName { - vol.Config.AccessInfo.PublishEnforcement = true - updated = true - } - return updated + return HealNASPublishEnforcement(ctx, d, vol) } diff --git a/storage_drivers/ontap/ontap_san.go b/storage_drivers/ontap/ontap_san.go index 801a496e3..335779fa1 100644 --- a/storage_drivers/ontap/ontap_san.go +++ b/storage_drivers/ontap/ontap_san.go @@ -1807,3 +1807,7 @@ func (d *SANStorageDriver) EnablePublishEnforcement(ctx context.Context, volume func (d *SANStorageDriver) CanEnablePublishEnforcement() bool { return true } + +func (d *SANStorageDriver) HealVolumePublishEnforcement(ctx context.Context, volume *storage.Volume) bool { + return HealSANPublishEnforcement(ctx, d, volume) +} diff --git a/storage_drivers/ontap/ontap_san_economy.go b/storage_drivers/ontap/ontap_san_economy.go index b8b71c9bd..c90622edb 100644 --- a/storage_drivers/ontap/ontap_san_economy.go +++ b/storage_drivers/ontap/ontap_san_economy.go @@ -2814,6 +2814,10 @@ func (d *SANEconomyStorageDriver) CanEnablePublishEnforcement() bool { return true } +func (d *SANEconomyStorageDriver) HealVolumePublishEnforcement(ctx context.Context, volume *storage.Volume) bool { + return HealSANPublishEnforcement(ctx, d, volume) +} + // ParseLunInternalID parses the passed string which is in the format /svm//flexvol//lun/ // and returns svm, flexvol and LUN name. func (d SANEconomyStorageDriver) ParseLunInternalID(internalId string) (svm, flexvol, lun string, err error) { diff --git a/storage_drivers/ontap/ontap_san_nvme.go b/storage_drivers/ontap/ontap_san_nvme.go index 7c1504072..0e45a14a4 100644 --- a/storage_drivers/ontap/ontap_san_nvme.go +++ b/storage_drivers/ontap/ontap_san_nvme.go @@ -1833,3 +1833,7 @@ func (d *NVMeStorageDriver) EnablePublishEnforcement(_ context.Context, volume * func (d *NVMeStorageDriver) CanEnablePublishEnforcement() bool { return true } + +func (d *NVMeStorageDriver) HealVolumePublishEnforcement(ctx context.Context, volume *storage.Volume) bool { + return HealSANPublishEnforcement(ctx, d, volume) +} From 76e99058584134582a8eac079d5b9e83f8393ddd Mon Sep 17 00:00:00 2001 From: emmahardison <106281452+emmahardison@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:52:42 -0600 Subject: [PATCH 09/30] Continue node remediation when volume not found --- frontend/crd/trident_node_remediation_utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/crd/trident_node_remediation_utils.go b/frontend/crd/trident_node_remediation_utils.go index 0ae491990..1542c5d58 100644 --- a/frontend/crd/trident_node_remediation_utils.go +++ b/frontend/crd/trident_node_remediation_utils.go @@ -113,7 +113,8 @@ func (n *nodeRemediationUtils) GetPvcToTvolMap( for _, volumeName := range tridentVolumesOnNode { tvol, err := n.orchestrator.GetVolume(ctx, volumeName) if err != nil { - return nil, fmt.Errorf("could not get volume %s: %v", volumeName, err) + Logc(ctx).WithError(err).Warnf("Could not get volume %s.", volumeName) + continue } pvcName := tvol.Config.RequestName if pvcName == "" { // Sanity check, should never be empty From 84a29e40ea99113a29fcd79e4771ba8aa39ea6ff Mon Sep 17 00:00:00 2001 From: jharrod Date: Thu, 23 Oct 2025 10:19:09 -0600 Subject: [PATCH 10/30] update backend status during reconcile Co-authored-by: Clinton Knight Handle backends going online/offline --- core/concurrent_core.go | 2 ++ core/orchestrator_core.go | 1 + 2 files changed, 3 insertions(+) diff --git a/core/concurrent_core.go b/core/concurrent_core.go index a4ee4df71..9b326bc23 100644 --- a/core/concurrent_core.go +++ b/core/concurrent_core.go @@ -5365,6 +5365,7 @@ func (o *ConcurrentTridentOrchestrator) reconcileBackendState(ctx context.Contex return dbErr } backend = results[0].Backend.Read + upserter := results[0].Backend.Upsert if backend == nil { return errors.NotFoundError("backend '%s' not found", backendUUID) @@ -5378,6 +5379,7 @@ func (o *ConcurrentTridentOrchestrator) reconcileBackendState(ctx context.Contex } backend.UpdateBackendState(ctx, reason) + upserter(backend) logFields := LogFields{ "backend": backend.Name(), diff --git a/core/orchestrator_core.go b/core/orchestrator_core.go index 1d6db2416..219ea8ac5 100644 --- a/core/orchestrator_core.go +++ b/core/orchestrator_core.go @@ -5834,6 +5834,7 @@ func (o *TridentOrchestrator) reconcileBackendState(ctx context.Context, b stora defer o.updateMetrics() reason, changeMap := b.GetBackendState(ctx) + b.UpdateBackendState(ctx, reason) if changeMap != nil { if changeMap.Contains(storage.BackendStateReasonChange) { From 0a51ea32e6f44afe8a3c504cf2631fd7ffcb45c9 Mon Sep 17 00:00:00 2001 From: shashank-netapp <108022276+shashank-netapp@users.noreply.github.com> Date: Sat, 25 Oct 2025 12:46:00 +0530 Subject: [PATCH 11/30] Fixes github issue 1070: Helm chart not applying resources.limits.memory to operator's deployment Co-authored-by: Etienne Divet --- helm/trident-operator/templates/_helpers.tpl | 35 +++++++++++++++++++ .../templates/deployment.yaml | 11 ++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/helm/trident-operator/templates/_helpers.tpl b/helm/trident-operator/templates/_helpers.tpl index ca69e776e..bbf3dc01f 100644 --- a/helm/trident-operator/templates/_helpers.tpl +++ b/helm/trident-operator/templates/_helpers.tpl @@ -379,3 +379,38 @@ Helper functions to check if resources are actually defined (not just empty stru {{- end -}} {{- if $hasResources -}}true{{- end -}} {{- end -}} + +{{/* +Helper function to check if operator resources are defined +*/}} +{{- define "trident-operator.hasResources" -}} + {{- $val := . -}} + {{- if or $val.requests.cpu $val.requests.memory $val.limits.cpu $val.limits.memory -}} + true + {{- end -}} +{{- end -}} + +{{/* +Helper function to render resource requests and limits for the operator +*/}} +{{- define "trident-operator.resources" }} +{{- $val := .}} +{{- if or $val.requests.cpu $val.requests.memory }} +requests: +{{- if $val.requests.cpu }} + cpu: {{ $val.requests.cpu }} +{{- end }} +{{- if $val.requests.memory }} + memory: {{ $val.requests.memory }} +{{- end }} +{{- end }} +{{- if or $val.limits.cpu $val.limits.memory }} +limits: +{{- if $val.limits.cpu }} + cpu: {{ $val.limits.cpu }} +{{- end }} +{{- if $val.limits.memory }} + memory: {{ $val.limits.memory }} +{{- end }} +{{- end }} +{{- end -}} diff --git a/helm/trident-operator/templates/deployment.yaml b/helm/trident-operator/templates/deployment.yaml index 4b6775015..c57bb5876 100644 --- a/helm/trident-operator/templates/deployment.yaml +++ b/helm/trident-operator/templates/deployment.yaml @@ -69,15 +69,10 @@ spec: image: {{ include "trident-operator.image" $ }} imagePullPolicy: {{ .Values.imagePullPolicy }} name: trident-operator + {{- if (include "trident-operator.hasResources" .Values.resources.operator) }} resources: - requests: - cpu: {{ .Values.resources.operator.requests.cpu }} - memory: {{ .Values.resources.operator.requests.memory }} - {{- if .Values.resources.operator.limits.cpu }} - limits: - cpu: {{ .Values.resources.operator.limits.cpu }} - memory: {{ .Values.resources.operator.limits.memory }} - {{- end }} + {{- include "trident-operator.resources" .Values.resources.operator | indent 10 }} + {{- end }} {{- if and (eq .Values.cloudProvider "Azure") (eq .Values.cloudIdentity "") }} volumes: - name: azure-cred From fba6523ef4fdcbc792e3372acf7934ce78a70d2b Mon Sep 17 00:00:00 2001 From: Clinton Knight Date: Mon, 27 Oct 2025 10:13:15 -0400 Subject: [PATCH 12/30] Reverted sync qtree APIs --- storage_drivers/ontap/api/ontap_rest.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/storage_drivers/ontap/api/ontap_rest.go b/storage_drivers/ontap/api/ontap_rest.go index ec9de1269..37f8bc1a2 100644 --- a/storage_drivers/ontap/api/ontap_rest.go +++ b/storage_drivers/ontap/api/ontap_rest.go @@ -718,7 +718,7 @@ func (c *RestClient) setVolumeSizeByNameAndStyle(ctx context.Context, volumeName params.Context = ctx params.HTTPClient = c.httpClient params.UUID = uuid - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) sizeBytesStr, _ := capacity.ToBytes(newSize) sizeBytes, err := convert.ToPositiveInt64(sizeBytesStr) @@ -4740,7 +4740,7 @@ func (c *RestClient) QtreeCreate( params := storage.NewQtreeCreateParamsWithTimeout(c.httpClient.Timeout) params.SetContext(ctx) params.SetHTTPClient(c.httpClient) - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) qtreeInfo := &models.Qtree{ Name: convert.ToPtr(name), @@ -4842,7 +4842,7 @@ func (c *RestClient) QtreeRename(ctx context.Context, path, newPath string) erro params.SetHTTPClient(c.httpClient) params.SetID(strconv.FormatInt(*qtree.ID, 10)) params.SetVolumeUUID(*qtree.Volume.UUID) - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) qtreeInfo := &models.Qtree{ Name: convert.ToPtr(strings.TrimPrefix(newPath, "/"+*qtree.Volume.Name+"/")), @@ -4887,7 +4887,7 @@ func (c *RestClient) QtreeDestroyAsync(ctx context.Context, path string, force b params.SetHTTPClient(c.httpClient) params.SetID(strconv.FormatInt(*qtree.ID, 10)) params.SetVolumeUUID(*qtree.Volume.UUID) - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) deleteOK, deleteAccepted, err := c.api.Storage.QtreeDelete(params, c.authInfo) if err != nil { @@ -5302,7 +5302,7 @@ func (c *RestClient) QtreeModifyExportPolicy(ctx context.Context, name, volumeNa params.SetHTTPClient(c.httpClient) params.SetID(strconv.FormatInt(*qtree.ID, 10)) params.SetVolumeUUID(*qtree.Volume.UUID) - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) qtreeInfo := &models.Qtree{ ExportPolicy: &models.QtreeInlineExportPolicy{ @@ -5367,7 +5367,7 @@ func (c *RestClient) quotaModify(ctx context.Context, volumeName string, quotaEn params.SetContext(ctx) params.SetHTTPClient(c.httpClient) params.SetUUID(*volume.UUID) - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) volumeInfo := &models.Volume{ Quota: &models.VolumeInlineQuota{ @@ -5413,7 +5413,7 @@ func (c *RestClient) QuotaSetEntry(ctx context.Context, qtreeName, volumeName, q params.SetContext(ctx) params.SetHTTPClient(c.httpClient) params.SetUUID(*quotaRule.UUID) - params.SetReturnTimeout(returnTimeout) + // params.SetReturnTimeout(returnTimeout) // determine the new hard disk limit value if diskLimit == "" { @@ -5450,7 +5450,7 @@ func (c *RestClient) QuotaAddEntry(ctx context.Context, volumeName, qtreeName, q params := storage.NewQuotaRuleCreateParamsWithTimeout(c.httpClient.Timeout) params.SetContext(ctx) params.SetHTTPClient(c.httpClient) - params.SetReturnTimeout(convert.ToPtr(int64(3))) + // params.SetReturnTimeout(convert.ToPtr(int64(3))) quotaRuleInfo := &models.QuotaRule{ Qtree: &models.QuotaRuleInlineQtree{ From c3bded628ae7b2b5d6d553733959e90bb95fa91a Mon Sep 17 00:00:00 2001 From: shashank-netapp <108022276+shashank-netapp@users.noreply.github.com> Date: Mon, 27 Oct 2025 22:02:51 +0530 Subject: [PATCH 13/30] added system-cluster-critical to the resourcequota's scope --- cli/k8s_client/yaml_factory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/k8s_client/yaml_factory.go b/cli/k8s_client/yaml_factory.go index fea76a4e8..174ffce27 100644 --- a/cli/k8s_client/yaml_factory.go +++ b/cli/k8s_client/yaml_factory.go @@ -390,7 +390,7 @@ spec: matchExpressions: - operator : In scopeName: PriorityClass - values: ["system-node-critical"] + values: ["system-node-critical", "system-cluster-critical"] ` const deploymentAutosupportYAMLTemplate = ` From 5a6761d10093de6ed7efb7f4b078ffe61bf6dda7 Mon Sep 17 00:00:00 2001 From: Alloyd Savio Mendonca <167860552+alloydsa@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:35:38 +0530 Subject: [PATCH 14/30] Bump ASUP version to 25.10 --- config/config.go | 2 +- deploy/crds/tridentorchestrator_cr_autosupport.yaml | 2 +- helm/trident-operator/values.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index e61298483..de50a4db8 100644 --- a/config/config.go +++ b/config/config.go @@ -415,7 +415,7 @@ var ( DefaultAutosupportName = "trident-autosupport" // DefaultAutosupportImage default image used by tridentctl and operator for asup sidecar - DefaultAutosupportImage = fmt.Sprintf("docker.io/netapp/%s:25.06", DefaultAutosupportName) + DefaultAutosupportImage = fmt.Sprintf("docker.io/netapp/%s:25.10", DefaultAutosupportName) // DefaultACPImage default image used by tridentctl and operator for acp sidecar DefaultACPImage = "cr.astra.netapp.io/astra/trident-acp:24.10.0" diff --git a/deploy/crds/tridentorchestrator_cr_autosupport.yaml b/deploy/crds/tridentorchestrator_cr_autosupport.yaml index 65b496ebe..dd361eb2a 100644 --- a/deploy/crds/tridentorchestrator_cr_autosupport.yaml +++ b/deploy/crds/tridentorchestrator_cr_autosupport.yaml @@ -6,5 +6,5 @@ spec: debug: true namespace: trident silenceAutosupport: false - autosupportImage: "netapp/trident-autosupport:25.06" + autosupportImage: "netapp/trident-autosupport:25.10" autosupportProxy: "http://proxy.example.com:8888" diff --git a/helm/trident-operator/values.yaml b/helm/trident-operator/values.yaml index 645e9e6d3..f4addea37 100644 --- a/helm/trident-operator/values.yaml +++ b/helm/trident-operator/values.yaml @@ -95,7 +95,7 @@ tridentExcludeAutosupport: false tridentAutosupportImage: "" # tridentAutosupportImageTag allows overriding the tag of the image for Trident's Autosupport container. -tridentAutosupportImageTag: "25.06" +tridentAutosupportImageTag: "25.10" # tridentAutosupportProxy allows Trident's autosupport container to phone home via an HTTP proxy. tridentAutosupportProxy: "" From 75cda477b539d14093c290f2c648b31d4c38e9a9 Mon Sep 17 00:00:00 2001 From: reederc42 Date: Fri, 31 Oct 2025 08:35:16 -0700 Subject: [PATCH 15/30] Updates CHANGELOG and NOTICEs for 25.10.0 Co-authored-by: Jeremy Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 58 +- NOTICE.txt | 4720 ++++++++++++++++++++++++---------------- NOTICE_ASUP_module.txt | 3380 +++++++++------------------- 3 files changed, 3902 insertions(+), 4256 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6323151..3ec56c10e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,61 @@ [Releases](https://github.com/NetApp/trident/releases) -## Changes since v25.02.0 +## Changes since v25.06.0 + +### Trident + +**Fixes:** + +- **Kubernetes:** Fixed an issue where multiple attempts to close a LUKS device resulted in failures to detach volumes. +- **Kubernetes:** Fixed CSI node-driver-registrar container name inconsistency by standardizing Linux DaemonSet to `node-driver-registrar` to match Windows DaemonSet and container image naming. +- **Openshift:** Fixed Trident node pod not starting on Windows nodes in Openshift due to SCC having `allowHostDirVolumePlugin` set to false (Issue [#950](https://github.com/NetApp/trident/issues/950)) +- **Kubernetes:** Fixed an issue where export policies for legacy qtrees were not properly upgraded. +- **Kubernetes:** Fixed critical issue where incorrect iSCSI devices were discovered when detaching volumes from Kubernetes nodes. +- **Kubernetes:** Fixed an issue where NQNs were not checked before they are unmapped from Subsystems. +- **Openshift:** Fixed an issue where iSCSI node prep failed with OCP 4.19. +- **Kubernetes:** Block cloning of volume across different storage classes. +- Increased timeout when cloning a volume using SolidFire backends (Issue [#1008](https://github.com/NetApp/trident/issues/1008)). +- Fixed Kubernetes API QPS not being set via Helm (Issue [#975](https://github.com/NetApp/trident/issues/975)). +- Fixed inability to mount a Persistent Volume Claim (PVC) based on a snapshot of an NVMe based XFS filesystem PVC on the same Kubernetes node. +- Fixed UUID change issue after host/Docker restart in NDVP mode by adding unique/shared subsystem names per backend (e.g., `netappdvp_subsystem`). +- Fixed mount errors for iSCSI volumes during Trident upgrade from versions prior to 23.10 to 24.10 and above, resolving "invalid SANType" issue. +- Fixed issue where Trident backend state was not transitioning to online/offline without restarting the Trident controller. +- Fixed snapshots not being cleaned up on volume clone failures. +- Fixed failure to unstage volume when its device path was changed by the kernel. +- Fixed failure to unstage volume due to LUKS device already closed. +- Fixed issue where slow storage operations were leading to ContextDeadline errors. +- Trident Operator will wait for configurable `k8s-timeout` to check Trident version. + +**Enhancements:** + +- **Kubernetes:** Added support for CSI Volume Group Snapshots with v1beta1 Volume Group Snapshot Kubernetes APIs for ONTAP-NAS NFS and ONTAP-SAN-Economy drivers, in addition to ONTAP-SAN (iSCSI and FC). +- Added option for Trident controller to use host networking via helm, operator and tridentctl (Issue [#858](https://github.com/NetApp/trident/issues/858)). +- **Kubernetes:** Added support for automated workload failover with force volume detach for the ONTAP-NAS and ONTAP-NAS-Economy (excluding SMB in both NAS drivers), and the ONTAP-SAN and ONTAP-SAN-Economy drivers. +- **Kubernetes:** Enhanced Trident node concurrency for higher scalability on node operations for FCP volumes. +- **Kubernetes:** Added ONTAP AFX support for ONTAP NAS NFS driver. +- **Kubernetes:** Added support for configuring CPU and memory resource requests and limits for Trident containers via TridentOrchestrator CR and Helm chart values. (Issues [#1000](https://github.com/NetApp/trident/issues/1000), [#927](https://github.com/NetApp/trident/issues/927), [#853](https://github.com/NetApp/trident/issues/853), [#592](https://github.com/NetApp/trident/issues/592), [#110](https://github.com/NetApp/trident/issues/110)). +- **Kubernetes:** Added FC support for ASAr2 personality. +- **Kubernetes:** Added option to serve Prometheus metrics with HTTPS, instead of HTTP. +- **Kubernetes:** Added an option `--no-rename` when importing a volume to keep the original name but let Trident manage its lifecycle. +- **Kubernetes:** Trident deployment now runs at system-cluster-critical priority class. +- Added manual QoS support to the ANF driver, making it production-ready in 25.10; this experimental enhancement was introduced in 25.06. + +**Experimental Enhancements:** + +**NOTE:** Not for use in production environments. + +- [Tech Preview] Added support for concurrency for ONTAP-NAS (NFS only) and ONTAP-SAN (NVMe for unified ONTAP 9), in addition to the existing Tech Preview for the ONTAP-SAN driver (iSCSI and FCP protocols in unified ONTAP 9). + +### Trident Protect + +**Enhancements:** + +- Added annotations to Schedule and Backup CR's to control various Snapshot CR timeouts: `protect.trident.netapp.io/snapshot-completion-timeout`, `protect.trident.netapp.io/volume-snapshots-ready-to-use-timeout`, `protect.trident.netapp.io/volume-snapshots-created-timeout` +- Added annotation to Schedule CR to configure PVC bind timeout, which will be used by Backup CR: `protect.trident.netapp.io/pvc-bind-timeout-sec` +- Improving tridentctl-protect backup and snapshot listings to add a new field to indicate execution hook failures + +## v25.06.0 ### Trident @@ -34,7 +88,7 @@ **Experimental Enhancements:** -**NOTE**: Not for use in production environments. +**NOTE:** Not for use in production environments. - [Tech Preview] Enabled concurrent Trident controller operations via the `--enable-concurrency` feature flag. This allows controller operations to run in parallel, improving performance for busy or large environments. **NOTE:** This feature is experimental and currently supports limited parallel workflows with the ONTAP-SAN driver (iSCSI and FCP protocols). diff --git a/NOTICE.txt b/NOTICE.txt index a024184ad..d4bb9aedc 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -3,12 +3,12 @@ NetApp Notice Report Copyright 2025 About this document -The following copyright statements and licenses apply to the software components that are distributed with the Trident version 25.06.0 product. This product does not necessarily use all the software components referred to below. +The following copyright statements and licenses apply to the software components that are distributed with the Trident version 25.10.0 product. This product does not necessarily use all the software components referred to below. Where required, source code is published at the following location. https://opensource.netapp.com/ -You may also request a copy of the open source code by submitting a written request to ng-opensource-request@netapp.com or by writing to: +You may also request a copy of the open source code by submitting a written request to ng-opensource-request@netapp.com or by writing to: NetApp Inc. Attention: IP Legal Department (Open Source Request) @@ -25,1931 +25,2633 @@ Your request must include: 6.Your return mailing address and email. This offer is valid for three years from the date you acquired the Trident products or for as long asthe applicable license requires this offer to be valid. We may charge you a fee to cover the cost of physical media and processing. Notwithstanding any other agreement or provision, NetApp disclaims all liability and warranties with respect to any source code made available by any method provided above. -Components: +Components: -alecthomas-kingpin v2.4.0 : MIT License +alecthomas-kingpin v2.4.0 : MIT License -alecthomas-units 20211218-snapshot-b94a6e3c : MIT License +alecthomas-units 20211218-snapshot-b94a6e3c : MIT License -armon/go-socks5 20160902-snapshot-e7533296 : MIT License +amazon-ecs-agent v1.87.0 : Apache License 2.0 -aws/aws-sdk-go-v2 config/v1.29.2 : Apache License 2.0 +armon/go-socks5 20160902-snapshot-e7533296 : MIT License -aws/aws-sdk-go-v2 credentials/v1.17.55 : Apache License 2.0 +AstroProfundis/sysinfo 20211201-snapshot-9f959380 : MIT License -aws/aws-sdk-go-v2 feature/ec2/imds/v1.16.25 : Apache License 2.0 +aws/aws-sdk-go-v2 config/v1.31.12 : Apache License 2.0 -aws/aws-sdk-go-v2 internal/configsources/v1.3.32 : Apache License 2.0 +aws/aws-sdk-go-v2 credentials/v1.18.16 : Apache License 2.0 -aws/aws-sdk-go-v2 internal/endpoints/v2.6.32 : Apache License 2.0 +aws/aws-sdk-go-v2 feature/ec2/imds/v1.18.9 : Apache License 2.0 -aws/aws-sdk-go-v2 internal/ini/v1.8.2 : Apache License 2.0 +aws/aws-sdk-go-v2 internal/configsources/v1.4.9 : Apache License 2.0 -aws/aws-sdk-go-v2 service/fsx/v1.52.0 : Apache License 2.0 +aws/aws-sdk-go-v2 internal/endpoints/v2.7.9 : Apache License 2.0 -aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.12.2 : Apache License 2.0 +aws/aws-sdk-go-v2 internal/ini/v1.8.3 : Apache License 2.0 -aws/aws-sdk-go-v2 service/internal/presigned-url/v1.12.10 : Apache License 2.0 +aws/aws-sdk-go-v2 service/fsx/v1.62.0 : Apache License 2.0 -aws/aws-sdk-go-v2 service/secretsmanager/v1.34.14 : Apache License 2.0 +aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.13.1 : Apache License 2.0 -aws/aws-sdk-go-v2 service/ssooidc/v1.28.11 : Apache License 2.0 +aws/aws-sdk-go-v2 service/internal/presigned-url/v1.13.9 : Apache License 2.0 -aws/aws-sdk-go-v2 service/sso/v1.24.12 : Apache License 2.0 +aws/aws-sdk-go-v2 service/secretsmanager/v1.39.6 : Apache License 2.0 -aws/aws-sdk-go-v2 service/sts/v1.33.10 : Apache License 2.0 +aws/aws-sdk-go-v2 service/ssooidc/v1.35.1 : Apache License 2.0 -aws/aws-sdk-go-v2 v1.36.1 : Apache License 2.0 +aws/aws-sdk-go-v2 service/sso/v1.29.6 : Apache License 2.0 -AzureAD/microsoft-authentication-library-for-go 20250410-snapshot : MIT License +aws/aws-sdk-go-v2 service/sts/v1.38.6 : Apache License 2.0 -AzureAD/microsoft-authentication-library-for-go v1.4.2 : MIT License +aws/aws-sdk-go-v2 v1.39.2 : Apache License 2.0 -Azure/azure-sdk-for-go 20240522-snapshot : MIT License +@aws-sdk/client-secrets-manager 3.181.0 : Apache License 2.0 -Azure/azure-sdk-for-go 20250605-snapshot : MIT License +AWS SDK for Java 1.12.549 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/azcore/v1.18.0 : MIT License +AWS SDK for Node.js 2.1040.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/azidentity/v1.8.2 : MIT License +AWS SDK for Node.js 2.1083.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/internal/v1.11.0 : MIT License +AWS SDK for Node.js 2.1114.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0 : MIT License +AWS SDK for Node.js 2.1119.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/compute/armcompute/v5.7.0 : MIT License +AWS SDK for Node.js 2.1143.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0 : MIT License +AWS SDK for Node.js 2.1226.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/containerservice/armcontainerservice/v4.8.0 : MIT License +AWS SDK for Node.js 2.1263.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.4.0 : MIT License +AWS SDK for Node.js 2.1643.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0 : MIT License +AWS SDK for Node.js 2.365.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.2.0 : MIT License +AWS SDK for Ruby 1.69.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0 : MIT License +AWS SDK for Ruby 1.91.0 : Apache License 2.0 -Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0 : MIT License +AzureAD/microsoft-authentication-library-for-go v1.5.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0 : MIT License +Azure/azure-sdk-for-go 20240704-snapshot : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.6.0 : MIT License +Azure/azure-sdk-for-go 20250604-snapshot : MIT License -Azure/azure-sdk-for-go v2.0.0-beta : Apache License 2.0 +Azure/azure-sdk-for-go 20250706-snapshot : MIT License -Azure/azure-sdk-for-go v3.0.0-beta : Apache License 2.0 +Azure/azure-sdk-for-go 20250804-snapshot : MIT License -beorn7-perks v1.0.1 : MIT License +Azure/azure-sdk-for-go 20250922-snapshot : MIT License -bits-and-blooms/bitset 20241228-snapshot : BSD 3-clause "New" or "Revised" License +Azure/azure-sdk-for-go sdk/azcore/v1.19.1 : MIT License -bits-and-blooms/bitset v1.20.0 : BSD 3-clause "New" or "Revised" License +Azure/azure-sdk-for-go sdk/azidentity/v1.12.0 : MIT License -blackfriday v2.1.0 : BSD 2-clause "Simplified" License +Azure/azure-sdk-for-go sdk/internal/v1.11.2 : MIT License -blang-semver v4.0.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0 : MIT License -brunoga/deep v1.2.4 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/billing/armbilling/v0.7.0 : MIT License -btree v1.0.1 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/blockchain/armblockchain/v0.6.0 : MIT License -BurntSushi/toml v0.3.1 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/connectedvmware/armconnectedvmware/v0.1.0 : MIT License -cenkalti/backoff v4.3.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/connectedvmware/armconnectedvmware/v1.0.0 : MIT License -census-instrumentation/opencensus-go v0.24.0 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0 : MIT License -cespare/xxhash v2.3.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/edgeorder/armedgeorder/v0.3.0 : MIT License -cli/cli v2.74.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/edgeorder/armedgeorder/v1.2.0 : MIT License -client9/misspell v0.3.4 : (MIT License AND BSD 3-clause "New" or "Revised" License) +Azure/azure-sdk-for-go sdk/resourcemanager/elastic/armelastic/v0.5.0 : MIT License -client-go v0.32.1 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.5.0 : MIT License -client_golang 20250409-snapshot : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0 : MIT License -client_golang v1.22.0 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/msi/armmsi/v1.2.0 : MIT License -cncf/udpa 20201120-snapshot-5459f2c9 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.3.0 : MIT License -containerd/containerd v1.7.23 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0 : MIT License -containerd/containerd v2.0.5 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) +Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0 : MIT License -containerd/containerd v2.1.0 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) +Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0 : MIT License -container-storage-interface/spec v1.11.0 : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.8.1 : MIT License -CoreOS v0.3.1 : Apache License 2.0 +Azure/azure-sdk-for-go v2.0.0-beta : Apache License 2.0 -cpuguy83-go-md2man v2.0.6 : MIT License +Azure/azure-sdk-for-go v3.1.0-beta : Apache License 2.0 -dgryski/go-rendezvous 20200823-snapshot-9f7001d1 : MIT License +Azure/azure-sdk-for-go v5.0.0-beta : Apache License 2.0 -diskv v2.0.1 : MIT License +beorn7-perks v1.0.1 : MIT License -dnaeon/go-vcr v1.2.0 : BSD 2-clause "Simplified" License +bingoohuang/golog 20230128-snapshot-a8993f58 : MIT License -dnaeon/go-vcr v3.2.0 : BSD 2-clause "Simplified" License +bits-and-blooms/bitset 20250923-snapshot : BSD 3-clause "New" or "Revised" License -docker/buildx v0.20.1 : Apache License 2.0 +bits-and-blooms/bitset v1.20.0 : BSD 3-clause "New" or "Revised" License -docker-compose v2.33.0 : Apache License 2.0 +blackfriday v2.1.0 : BSD 2-clause "Simplified" License -docker-go-plugins-helpers 20240701-snapshot-45e24314 : Apache License 2.0 +blang-semver v4.0.0 : MIT License -docker-go-plugins-helpers 20241106-snapshot : Apache License 2.0 +brunoga/deep 20250815-snapshot : Apache License 2.0 -docker-go-units v0.5.0 : Apache License 2.0 +brunoga/deep v1.2.5 : Apache License 2.0 -dominikh/go-tools 20190523-snapshot-ea95bdfd : MIT License +btree v1.1.3 : Apache License 2.0 -elastic/go-sysinfo 184688adcb6ddaa744fe787e6e6a47a95f8b5e44 : Apache License 2.0 +BurntSushi/toml v0.3.1 : MIT License -elastic/go-sysinfo 20250425-snapshot : Apache License 2.0 +cenkalti/backoff v4.3.0 : MIT License -elastic/go-windows v1.0.2 : Apache License 2.0 +census-instrumentation/opencensus-go v0.24.0 : Apache License 2.0 -envoyproxy/go-control-plane envoy/v1.32.4 : Apache License 2.0 +cespare/xxhash v2.3.0 : MIT License -envoyproxy/go-control-plane ratelimit/v0.1.0 : Apache License 2.0 +client9/misspell v0.3.4 : (MIT License AND BSD 3-clause "New" or "Revised" License) -envoyproxy/go-control-plane v0.13.4 : Apache License 2.0 +client-go 20250901-snapshot : Apache License 2.0 -errcheck v1.5.0-alpha : MIT License +client-go v0.34.1 : Apache License 2.0 -evanphx/json-patch v4.12.0 : BSD 3-clause "New" or "Revised" License +client_golang v1.23.2 : Apache License 2.0 -evanphx/json-patch v5.6.0 : BSD 3-clause "New" or "Revised" License +cncf/udpa 20201120-snapshot-5459f2c9 : Apache License 2.0 -evanphx/json-patch v5.9.11 : BSD 3-clause "New" or "Revised" License +@compose-generator/cli 1.0.0 : Apache License 2.0 -exp 20240711-snapshot-8a7402ab : BSD 3-clause "New" or "Revised" License +containerd/containerd api/v1.8.0-rc.4 : Apache License 2.0 -felixge/httpsnoop v1.0.4 : MIT License +containerd/containerd v2.0.5 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) -fsnotify-fsnotify v1.7.0 : BSD 3-clause "New" or "Revised" License +containerd/containerd v2.1.3 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) -fxamacker/cbor 2.7.0 : Expat License +container-storage-interface/spec v1.11.0 : Apache License 2.0 -gengo 20240911-snapshot-2b36238f : Apache License 2.0 +CoreOS v0.3.1 : Apache License 2.0 -github.com/antlr4-go/antlr v4.13.0 : BSD 3-clause "New" or "Revised" License +coreos/ignition 2.22.0 : Apache License 2.0 -github.com/aws/smithy-go 20250514-snapshot : Apache License 2.0 +cpuguy83-go-md2man v2.0.6 : MIT License -github.com/aws/smithy-go v1.22.2 : Apache License 2.0 +csi-provisioner v3.0.0 : Apache License 2.0 -github.com/cncf/xds 20250326-snapshot-ae57f3c0 : Apache License 2.0 +diskv v2.0.1 : MIT License -github.com/distribution/reference v0.6.0 : Apache License 2.0 +dnaeon/go-vcr v1.2.0 : BSD 2-clause "Simplified" License -github.com/google/cel-spec v0.23.0 : Apache License 2.0 +dnaeon/go-vcr v3.2.0 : BSD 2-clause "Simplified" License -github.com/konsorten/go-windows-terminal-sequences v1.0.1 : MIT License +docker/buildx v0.18.0 : Apache License 2.0 -github.com/kubernetes-csi/csi-lib-utils v0.16.0 : Apache License 2.0 +docker-go-plugins-helpers 20240701-snapshot-45e24314 : Apache License 2.0 -github.com/mattermost/xml-roundtrip-validator 20230502-snapshot-3079e7b8 : Apache License 2.0 +docker-go-units v0.5.0 : Apache License 2.0 -github.com/moby/spdystream v0.5.0 : Apache License 2.0 +Docker Moby v22.06.0-beta.0 : Apache License 2.0 -github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61 : BSD 3-clause "New" or "Revised" License +Docker Moby v25.0.0-beta.1 : Apache License 2.0 -github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b : BSD 3-clause "New" or "Revised" License +docker-org 0.9.0 : Apache License 2.0 -github.com/redis/go-redis v9.7.0 : BSD 2-clause "Simplified" License +docker-org v0.1 : (Educational Community License v2.0 AND Apache License 2.0) -github.com/rivo/uniseg v0.1.0 : MIT License +docker-org v0.1.0 : MIT License -github.com/xdg-go/pbkdf2 1.0.0 : Apache License 2.0 +docker-org v0.2.0 : MIT License -go-check-check 20201130-snapshot-10cb9826 : BSD 2-clause "Simplified" License +docker-org v0.3.0 : MIT License -godebug v1.1.0 : Apache License 2.0 +docker-org v0.5.0 : MIT License -GoDoc Text v0.2.0 : MIT License +docker-org v1.0.2 : Apache License 2.0 -go-etcd api/v3.5.16 : Apache License 2.0 +dominikh/go-tools 20190523-snapshot-ea95bdfd : MIT License -go-etcd client/pkg/v3.5.16 : Apache License 2.0 +elastic/go-sysinfo 184688adcb6ddaa744fe787e6e6a47a95f8b5e44 : Apache License 2.0 -go-etcd client/v2.305.16 : Apache License 2.0 +elastic/go-sysinfo 20250922-snapshot : Apache License 2.0 -go-etcd client/v3.5.16 : Apache License 2.0 +elastic/go-windows v1.0.2 : Apache License 2.0 -go-etcd pkg/v3.5.16 : Apache License 2.0 +envoyproxy/go-control-plane envoy/v1.32.4 : Apache License 2.0 -go-etcd raft/v3.5.16 : Apache License 2.0 +envoyproxy/go-control-plane ratelimit/v0.1.0 : Apache License 2.0 -go-etcd server/v3.5.16 : Apache License 2.0 +envoyproxy/go-control-plane v0.13.4 : Apache License 2.0 -go.etcd.io/bbolt v1.3.11 : MIT License +envoyproxy/protoc-gen-validate 1.2.1 : Apache License 2.0 -go-flags v1.6.1 : BSD 3-clause "New" or "Revised" License +errcheck v1.5.0-alpha : MIT License -go-flowrate 20140419-snapshot-cca7078d : BSD 3-clause "New" or "Revised" License +etcd-io/raft v3.6.0 : Apache License 2.0 -gogo/protobuf v1.3.2 : BSD 3-clause "New" or "Revised" License +evanphx/json-patch v4.12.0 : BSD 3-clause "New" or "Revised" License -go humanize 20250512-snapshot-b48bc01a : MIT License +evanphx/json-patch v5.6.0 : BSD 3-clause "New" or "Revised" License -go-inf-inf v0.9.1 : BSD 3-clause "New" or "Revised" License +evanphx/json-patch v5.9.11 : BSD 3-clause "New" or "Revised" License -go-jose 4.0.5 : Apache License 2.0 +exp 20250718-snapshot-645b1fa8 : BSD 3-clause "New" or "Revised" License -golang/appengine v1.6.8 : Apache License 2.0 +felixge/httpsnoop v1.0.4 : MIT License -golang-github-docker-go-connections-dev 0.4.0 : Apache License 2.0 +fsnotify-fsnotify v1.9.0 : BSD 3-clause "New" or "Revised" License -golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd : MIT License +fxamacker/cbor v2.9.0 : MIT License -golang-github-ghodss-yaml-dev 20240620-snapshot : MIT License +gengo 20250604-snapshot-85fd79db : Apache License 2.0 -golang-github-googleapis-gax-go-dev 2.13.0 : BSD 3-clause "New" or "Revised" License +Gitea 1.21.3 : MIT License -golang-github-spf13-pflag-dev v1.0.6 : BSD 3-clause "New" or "Revised" License +github.com/antlr4-go/antlr v4.13.1 : BSD 3-clause "New" or "Revised" License -golang/glog v1.2.4 : Apache License 2.0 +github.com/aws/smithy-go 20250905-snapshot : Apache License 2.0 -golang-jwt/jwt v4.5.0 : MIT License +github.com/aws/smithy-go metrics/smithyotelmetrics/v1.0.1 : Apache License 2.0 -golang-jwt/jwt v5.2.2 : MIT License +github.com/aws/smithy-go v1.23.0 : Apache License 2.0 -golang-mock v1.1.1 : Apache License 2.0 +github.com/cncf/xds 20250501-snapshot-2ac532fd : Apache License 2.0 -golang.org/x/crypto v0.39.0 : BSD 3-clause "New" or "Revised" License +github.com/distribution/reference v0.6.0 : Apache License 2.0 -golang.org/x/lint 20190308-snapshot-d0100b6b : BSD 3-clause "New" or "Revised" License +github.com/expr-lang/expr v1.16.6 : MIT License -golang.org/x/mod v0.25.0 : BSD 3-clause "New" or "Revised" License +github.com/google/cel-spec v0.24.0 : Apache License 2.0 -golang.org/x/net 20250404-snapshot : BSD 3-clause "New" or "Revised" License +github.com/go-viper/mapstructure v2.4.0 : MIT License -golang.org/x/net 20250607-snapshot : BSD 3-clause "New" or "Revised" License +github.com/konsorten/go-windows-terminal-sequences v1.0.1 : MIT License -golang.org/x/net v0.41.0 : BSD 3-clause "New" or "Revised" License +github.com/kubernetes-csi/csi-lib-utils v0.22.0 : Apache License 2.0 -golang.org/x/oauth2 20250510-snapshot : BSD 3-clause "New" or "Revised" License +github.com/mattermost/xml-roundtrip-validator v0.1.0 : Apache License 2.0 -golang.org/x/oauth2 v0.30.0 : BSD 3-clause "New" or "Revised" License +github.com/moby/spdystream v0.5.0 : Apache License 2.0 -golang.org/x/sys 20250608-snapshot : BSD 3-clause "New" or "Revised" License +github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61 : BSD 3-clause "New" or "Revised" License -golang.org/x/sys v0.33.0 : BSD 3-clause "New" or "Revised" License +github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b : BSD 3-clause "New" or "Revised" License -golang.org/x/term v0.32.0 : BSD 3-clause "New" or "Revised" License +github.com/rivo/uniseg v0.2.0 : MIT License -golang.org/x/time v0.11.0 : BSD 3-clause "New" or "Revised" License +github.com/xdg-go/pbkdf2 1.0.0 : Apache License 2.0 -golang.org/x/tools 20250611-snapshot : BSD 3-clause "New" or "Revised" License +go-check-check 20201130-snapshot-10cb9826 : BSD 2-clause "Simplified" License -golang.org/x/tools v0.33.0 : BSD 3-clause "New" or "Revised" License +godebug v1.1.0 : Apache License 2.0 -golang.org/x/xerrors 20200804-snapshot-5ec99f83 : BSD 3-clause "New" or "Revised" License +GoDoc Text v0.2.0 : MIT License -Golang Protobuf v1.36.6 : BSD 3-clause "New" or "Revised" License +go-etcd api/v3.6.4 : Apache License 2.0 -Golang Protobuf v1.5.4 : BSD 3-clause "New" or "Revised" License +go-etcd client/pkg/v3.6.4 : Apache License 2.0 -golang-snappy-go-dev v0.0.4 : BSD 3-clause "New" or "Revised" License +go-etcd client/v3.6.4 : Apache License 2.0 -golang-stats v0.7.0 : MIT License +go-etcd pkg/v3.6.4 : Apache License 2.0 -golang/sync 20250607-snapshot : BSD 3-clause "New" or "Revised" License +go-etcd server/v3.6.4 : Apache License 2.0 -golang/sync v0.15.0 : BSD 3-clause "New" or "Revised" License +go-faker/faker v4.6.1 : MIT License -golang/telemetry 20240517-snapshot-bda55230 : BSD 3-clause "New" or "Revised" License +go-flags v1.6.1 : BSD 3-clause "New" or "Revised" License -golang/text 20240806-snapshot : BSD 3-clause "New" or "Revised" License +go-flowrate 20140419-snapshot-cca7078d : BSD 3-clause "New" or "Revised" License -golang/text v0.26.0 : BSD 3-clause "New" or "Revised" License +gogo/protobuf v1.3.2 : BSD 3-clause "New" or "Revised" License -golang/tools v0.33.0 : BSD 3-clause "New" or "Revised" License +go humanize 20250512-snapshot-b48bc01a : MIT License -go-logr/logr v1.4.2 : Apache License 2.0 +go-inf-inf v0.9.1 : BSD 3-clause "New" or "Revised" License -go-logr/stdr v1.2.2 : Apache License 2.0 +go-jose v4.1.2 : Apache License 2.0 -gomega v1.35.1 : MIT License +golang/appengine v1.6.8 : Apache License 2.0 -googleapis/enterprise-certificate-proxy v0.3.6 : Apache License 2.0 +golang-github-docker-go-connections-dev 0.4.0 : Apache License 2.0 -googleapis/gax-go 20250602-snapshot : BSD 3-clause "New" or "Revised" License +golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd : MIT License -googleapis/gax-go v2.14.2 : BSD 3-clause "New" or "Revised" License +golang-github-ghodss-yaml-dev 20240620-snapshot : MIT License -googleapis/go-genproto 20250505-snapshot-f936aa4a : Apache License 2.0 +golang-github-googleapis-gax-go-dev 2.13.0 : BSD 3-clause "New" or "Revised" License -googleapis/go-genproto 20250512-snapshot-5a2f75b7 : Apache License 2.0 +golang-github-spf13-pflag-dev 20250906-snapshot : BSD 3-clause "New" or "Revised" License -googleapis/go-genproto 20250603-snapshot-513f2392 : Apache License 2.0 +golang-github-spf13-pflag-dev v1.0.10 : BSD 3-clause "New" or "Revised" License -googleapis/go-genproto 20250604-snapshot : Apache License 2.0 +golang/glog v1.2.5 : Apache License 2.0 -googleapis/google-api-go-client 20250505-snapshot : BSD 3-clause "New" or "Revised" License +golang-jwt/jwt v5.3.0 : MIT License -googleapis/google-api-go-client 20250603-snapshot : BSD 3-clause "New" or "Revised" License +golang-mock v1.1.1 : Apache License 2.0 -googleapis/google-api-go-client v0.234.0 : BSD 3-clause "New" or "Revised" License +golang.org/x/crypto v0.43.0 : BSD 3-clause "New" or "Revised" License -google/cel-go v0.22.0 : Apache License 2.0 +golang.org/x/lint 20190308-snapshot-d0100b6b : BSD 3-clause "New" or "Revised" License -google-cloud-go 20250529-snapshot : Apache License 2.0 +golang.org/x/mod v0.28.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go 20250610-snapshot : Apache License 2.0 +golang.org/x/net v0.46.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go accessapproval/v1.8.6 : Apache License 2.0 +golang.org/x/oauth2 v0.32.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go accesscontextmanager/v1.9.6 : Apache License 2.0 +golang.org/x/sys 20250923-snapshot : BSD 3-clause "New" or "Revised" License -google-cloud-go aiplatform/v1.85.0 : Apache License 2.0 +golang.org/x/sys v0.37.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go analytics/v0.28.0 : Apache License 2.0 +golang.org/x/term v0.36.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go apigateway/v1.7.6 : Apache License 2.0 +golang.org/x/time v0.14.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go apigeeconnect/v1.7.6 : Apache License 2.0 +golang.org/x/tools 20250916-snapshot : BSD 3-clause "New" or "Revised" License -google-cloud-go apigeeregistry/v0.9.6 : Apache License 2.0 +golang.org/x/tools v0.37.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go appengine/v1.9.6 : Apache License 2.0 +golang.org/x/xerrors 20200804-snapshot-5ec99f83 : BSD 3-clause "New" or "Revised" License -google-cloud-go area120/v0.9.6 : Apache License 2.0 +Golang Protobuf 20250505-snapshot : BSD 3-clause "New" or "Revised" License -google-cloud-go artifactregistry/v1.17.1 : Apache License 2.0 +Golang Protobuf v1.36.10 : BSD 3-clause "New" or "Revised" License -google-cloud-go asset/v1.21.0 : Apache License 2.0 +Golang Protobuf v1.5.4 : BSD 3-clause "New" or "Revised" License -google-cloud-go assuredworkloads/v1.12.6 : Apache License 2.0 +golang-snappy-go-dev v0.0.4 : BSD 3-clause "New" or "Revised" License -google-cloud-go auth/oauth2adapt/v0.2.8 : Apache License 2.0 +golang-stats v0.7.1 : MIT License -google-cloud-go auth/v0.16.1 : Apache License 2.0 +golang/sync v0.17.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go automl/v1.14.7 : Apache License 2.0 +golang/text 20240806-snapshot : BSD 3-clause "New" or "Revised" License -google-cloud-go baremetalsolution/v1.3.6 : Apache License 2.0 +golang/text v0.30.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go batch/v1.12.2 : Apache License 2.0 +golang/text v0.5.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go beyondcorp/v1.1.6 : Apache License 2.0 +go-logr/logr v1.4.3 : Apache License 2.0 -google-cloud-go bigquery/v1.67.0 : Apache License 2.0 +go-logr/stdr v1.2.2 : Apache License 2.0 -google-cloud-go bigtable/v1.37.0 : Apache License 2.0 +Go Logrus v1.9.3 : MIT License -google-cloud-go billing/v1.20.4 : Apache License 2.0 +gomega v1.37.0 : MIT License -google-cloud-go binaryauthorization/v1.9.5 : Apache License 2.0 +Gonum numerical packages v0.16.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go certificatemanager/v1.9.5 : Apache License 2.0 +googleapis/enterprise-certificate-proxy v0.3.6 : Apache License 2.0 -google-cloud-go channel/v1.19.5 : Apache License 2.0 +googleapis/gax-go v2.15.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go cloudbuild/v1.22.2 : Apache License 2.0 +googleapis/go-genproto 20250603-snapshot-513f2392 : Apache License 2.0 -google-cloud-go clouddms/v1.8.7 : Apache License 2.0 +googleapis/go-genproto 20250818-snapshot-3122310a : Apache License 2.0 -google-cloud-go cloudtasks/v1.13.6 : Apache License 2.0 +googleapis/go-genproto 20251002-snapshot-7c0ddcbb : Apache License 2.0 -google-cloud-go compute/metadata/v0.7.0 : Apache License 2.0 +googleapis/google-api-go-client 20250917-snapshot : BSD 3-clause "New" or "Revised" License -google-cloud-go compute/v1.38.0 : Apache License 2.0 +googleapis/google-api-go-client v0.252.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go contactcenterinsights/v1.17.3 : Apache License 2.0 +google/cel-go v0.26.0 : Apache License 2.0 -google-cloud-go containeranalysis/v0.14.1 : Apache License 2.0 +google-cloud-go accessapproval/v1.8.6 : Apache License 2.0 -google-cloud-go container/v1.42.4 : Apache License 2.0 +google-cloud-go accesscontextmanager/v1.9.6 : Apache License 2.0 -google-cloud-go datacatalog/v1.26.0 : Apache License 2.0 +google-cloud-go advisorynotifications/v1.2.0 : Apache License 2.0 -google-cloud-go dataflow/v0.10.6 : Apache License 2.0 +google-cloud-go aiplatform/v1.48.0 : Apache License 2.0 -google-cloud-go dataform/v0.11.2 : Apache License 2.0 +google-cloud-go aiplatform/v1.89.0 : Apache License 2.0 -google-cloud-go datafusion/v1.8.6 : Apache License 2.0 +google-cloud-go analytics/v0.28.1 : Apache License 2.0 -google-cloud-go datalabeling/v0.9.6 : Apache License 2.0 +google-cloud-go apigateway/v1.7.6 : Apache License 2.0 -google-cloud-go dataplex/v1.25.2 : Apache License 2.0 +google-cloud-go apigeeconnect/v1.7.6 : Apache License 2.0 -google-cloud-go dataproc/v2.11.2 : Apache License 2.0 +google-cloud-go apigeeregistry/v0.9.6 : Apache License 2.0 -google-cloud-go dataqna/v0.9.6 : Apache License 2.0 +google-cloud-go appengine/v1.9.6 : Apache License 2.0 -google-cloud-go datastore/v1.20.0 : Apache License 2.0 +google-cloud-go area120/v0.9.6 : Apache License 2.0 -google-cloud-go datastream/v1.14.1 : Apache License 2.0 +google-cloud-go artifactregistry/v1.17.1 : Apache License 2.0 -google-cloud-go deploy/v1.27.1 : Apache License 2.0 +google-cloud-go asset/v1.21.1 : Apache License 2.0 -google-cloud-go dialogflow/v1.68.2 : Apache License 2.0 +google-cloud-go assuredworkloads/v1.12.6 : Apache License 2.0 -google-cloud-go dlp/v1.22.1 : Apache License 2.0 +google-cloud-go auth/oauth2adapt/v0.2.8 : Apache License 2.0 -google-cloud-go documentai/v1.37.0 : Apache License 2.0 +google-cloud-go auth/v0.17.0 : Apache License 2.0 -google-cloud-go domains/v0.10.6 : Apache License 2.0 +google-cloud-go automl/v1.14.7 : Apache License 2.0 -google-cloud-go edgecontainer/v1.4.3 : Apache License 2.0 +google-cloud-go baremetalsolution/v1.3.6 : Apache License 2.0 -google-cloud-go errorreporting/v0.3.2 : Apache License 2.0 +google-cloud-go batch/v1.12.2 : Apache License 2.0 -google-cloud-go essentialcontacts/v1.7.6 : Apache License 2.0 +google-cloud-go beyondcorp/v1.1.6 : Apache License 2.0 -google-cloud-go eventarc/v1.15.5 : Apache License 2.0 +google-cloud-go bigquery/v1.69.0 : Apache License 2.0 -google-cloud-go filestore/v1.10.2 : Apache License 2.0 +google-cloud-go bigtable/v1.37.0 : Apache License 2.0 -google-cloud-go firestore/v1.18.0 : Apache License 2.0 +google-cloud-go billing/v1.20.4 : Apache License 2.0 -google-cloud-go functions/v1.19.6 : Apache License 2.0 +google-cloud-go binaryauthorization/v1.9.5 : Apache License 2.0 -google-cloud-go gkebackup/v1.7.0 : Apache License 2.0 +google-cloud-go certificatemanager/v1.9.5 : Apache License 2.0 -google-cloud-go gkeconnect/v0.12.4 : Apache License 2.0 +google-cloud-go channel/v1.19.5 : Apache License 2.0 -google-cloud-go gkehub/v0.15.6 : Apache License 2.0 +google-cloud-go cloudbuild/v1.22.2 : Apache License 2.0 -google-cloud-go gkemulticloud/v1.5.3 : Apache License 2.0 +google-cloud-go clouddms/v1.8.7 : Apache License 2.0 -google-cloud-go gsuiteaddons/v1.7.7 : Apache License 2.0 +google-cloud-go cloudtasks/v1.13.6 : Apache License 2.0 -google-cloud-go iam/v1.5.2 : Apache License 2.0 +google-cloud-go compute/metadata/v0.9.0 : Apache License 2.0 -google-cloud-go iap/v1.11.1 : Apache License 2.0 +google-cloud-go compute/v1.49.0 : Apache License 2.0 -google-cloud-go ids/v1.5.6 : Apache License 2.0 +google-cloud-go contactcenterinsights/v1.17.3 : Apache License 2.0 -google-cloud-go iot/v1.8.6 : Apache License 2.0 +google-cloud-go containeranalysis/v0.14.1 : Apache License 2.0 -google-cloud-go kms/v1.21.2 : Apache License 2.0 +google-cloud-go container/v1.43.0 : Apache License 2.0 -google-cloud-go language/v1.14.5 : Apache License 2.0 +google-cloud-go datacatalog/v1.26.0 : Apache License 2.0 -google-cloud-go lifesciences/v0.10.6 : Apache License 2.0 +google-cloud-go dataflow/v0.11.0 : Apache License 2.0 -google-cloud-go logging/v1.13.0 : Apache License 2.0 +google-cloud-go dataform/v0.12.0 : Apache License 2.0 -google-cloud-go longrunning/v0.6.7 : Apache License 2.0 +google-cloud-go datafusion/v1.8.6 : Apache License 2.0 -google-cloud-go managedidentities/v1.7.6 : Apache License 2.0 +google-cloud-go datalabeling/v0.9.6 : Apache License 2.0 -google-cloud-go maps/v1.20.4 : Apache License 2.0 +google-cloud-go dataplex/v1.25.3 : Apache License 2.0 -google-cloud-go mediatranslation/v0.9.6 : Apache License 2.0 +google-cloud-go dataproc/v2.11.2 : Apache License 2.0 -google-cloud-go memcache/v1.11.6 : Apache License 2.0 +google-cloud-go dataqna/v0.9.7 : Apache License 2.0 -google-cloud-go metastore/v1.14.6 : Apache License 2.0 +google-cloud-go datastore/v1.20.0 : Apache License 2.0 -google-cloud-go monitoring/v1.24.2 : Apache License 2.0 +google-cloud-go datastream/v1.14.1 : Apache License 2.0 -google-cloud-go netapp/v1.9.0 : Apache License 2.0 +google-cloud-go deploy/v1.27.2 : Apache License 2.0 -google-cloud-go networkconnectivity/v1.17.1 : Apache License 2.0 +google-cloud-go dialogflow/v1.68.2 : Apache License 2.0 -google-cloud-go networkmanagement/v1.19.1 : Apache License 2.0 +google-cloud-go dlp/v1.23.0 : Apache License 2.0 -google-cloud-go networksecurity/v0.10.6 : Apache License 2.0 +google-cloud-go documentai/v1.37.0 : Apache License 2.0 -google-cloud-go notebooks/v1.12.6 : Apache License 2.0 +google-cloud-go domains/v0.10.6 : Apache License 2.0 -google-cloud-go optimization/v1.7.6 : Apache License 2.0 +google-cloud-go edgecontainer/v1.4.3 : Apache License 2.0 -google-cloud-go orchestration/v1.11.9 : Apache License 2.0 +google-cloud-go errorreporting/v0.3.2 : Apache License 2.0 -google-cloud-go orgpolicy/v1.15.0 : Apache License 2.0 +google-cloud-go essentialcontacts/v1.7.6 : Apache License 2.0 -google-cloud-go osconfig/v1.14.5 : Apache License 2.0 +google-cloud-go eventarc/v1.15.5 : Apache License 2.0 -google-cloud-go oslogin/v1.14.6 : Apache License 2.0 +google-cloud-go filestore/v1.10.2 : Apache License 2.0 -google-cloud-go phishingprotection/v0.9.6 : Apache License 2.0 +google-cloud-go firestore/v1.18.0 : Apache License 2.0 -google-cloud-go policytroubleshooter/v1.11.6 : Apache License 2.0 +google-cloud-go functions/v1.19.6 : Apache License 2.0 -google-cloud-go privatecatalog/v0.10.7 : Apache License 2.0 +google-cloud-go gkebackup/v1.8.0 : Apache License 2.0 -google-cloud-go pubsublite/v1.8.2 : Apache License 2.0 +google-cloud-go gkeconnect/v0.12.4 : Apache License 2.0 -google-cloud-go pubsub/v1.49.0 : Apache License 2.0 +google-cloud-go gkehub/v0.15.6 : Apache License 2.0 -google-cloud-go recaptchaenterprise/v2.20.4 : Apache License 2.0 +google-cloud-go gkemulticloud/v1.5.3 : Apache License 2.0 -google-cloud-go recommendationengine/v0.9.6 : Apache License 2.0 +google-cloud-go gsuiteaddons/v1.7.7 : Apache License 2.0 -google-cloud-go recommender/v1.13.5 : Apache License 2.0 +google-cloud-go iam/v1.5.2 : Apache License 2.0 -google-cloud-go redis/v1.18.2 : Apache License 2.0 +google-cloud-go iap/v1.11.2 : Apache License 2.0 -google-cloud-go resourcemanager/v1.10.6 : Apache License 2.0 +google-cloud-go ids/v1.5.6 : Apache License 2.0 -google-cloud-go resourcesettings/v1.8.3 : Apache License 2.0 +google-cloud-go iot/v1.8.6 : Apache License 2.0 -google-cloud-go retail/v1.20.0 : Apache License 2.0 +google-cloud-go kms/v1.22.0 : Apache License 2.0 -google-cloud-go run/v1.9.3 : Apache License 2.0 +google-cloud-go language/v1.14.5 : Apache License 2.0 -google-cloud-go scheduler/v1.11.7 : Apache License 2.0 +google-cloud-go lifesciences/v0.10.6 : Apache License 2.0 -google-cloud-go secretmanager/v1.14.7 : Apache License 2.0 +google-cloud-go logging/v1.13.0 : Apache License 2.0 -google-cloud-go securitycenter/v1.36.2 : Apache License 2.0 +google-cloud-go longrunning/v0.6.7 : Apache License 2.0 -google-cloud-go security/v1.18.5 : Apache License 2.0 +google-cloud-go managedidentities/v1.7.6 : Apache License 2.0 -google-cloud-go servicedirectory/v1.12.6 : Apache License 2.0 +google-cloud-go maps/v1.21.0 : Apache License 2.0 -google-cloud-go shell/v1.8.6 : Apache License 2.0 +google-cloud-go mediatranslation/v0.9.6 : Apache License 2.0 -google-cloud-go spanner/v1.80.0 : Apache License 2.0 +google-cloud-go memcache/v1.11.6 : Apache License 2.0 -google-cloud-go speech/v1.27.1 : Apache License 2.0 +google-cloud-go metastore/v1.14.7 : Apache License 2.0 -google-cloud-go storagetransfer/v1.12.4 : Apache License 2.0 +google-cloud-go monitoring/v1.24.2 : Apache License 2.0 -google-cloud-go storage/v1.52.0 : Apache License 2.0 +google-cloud-go netapp/v1.10.1 : Apache License 2.0 -google-cloud-go talent/v1.8.3 : Apache License 2.0 +google-cloud-go networkconnectivity/v1.17.1 : Apache License 2.0 -google-cloud-go texttospeech/v1.12.1 : Apache License 2.0 +google-cloud-go networkmanagement/v1.19.1 : Apache License 2.0 -google-cloud-go tpu/v1.8.3 : Apache License 2.0 +google-cloud-go networksecurity/v0.10.6 : Apache License 2.0 -google-cloud-go trace/v1.11.6 : Apache License 2.0 +google-cloud-go notebooks/v1.12.6 : Apache License 2.0 -google-cloud-go translate/v1.12.5 : Apache License 2.0 +google-cloud-go optimization/v1.7.6 : Apache License 2.0 -google-cloud-go v0.121.0 : Apache License 2.0 +google-cloud-go orchestration/v1.11.9 : Apache License 2.0 -google-cloud-go videointelligence/v1.12.6 : Apache License 2.0 +google-cloud-go orgpolicy/v1.15.0 : Apache License 2.0 -google-cloud-go video/v1.23.5 : Apache License 2.0 +google-cloud-go osconfig/v1.14.6 : Apache License 2.0 -google-cloud-go vision/v2.9.5 : Apache License 2.0 +google-cloud-go oslogin/v1.14.6 : Apache License 2.0 -google-cloud-go vmmigration/v1.8.6 : Apache License 2.0 +google-cloud-go phishingprotection/v0.9.6 : Apache License 2.0 -google-cloud-go vmwareengine/v1.3.5 : Apache License 2.0 +google-cloud-go policytroubleshooter/v1.11.6 : Apache License 2.0 -google-cloud-go vpcaccess/v1.8.6 : Apache License 2.0 +google-cloud-go privatecatalog/v0.10.7 : Apache License 2.0 -google-cloud-go webrisk/v1.11.1 : Apache License 2.0 +google-cloud-go pubsublite/v1.8.2 : Apache License 2.0 -google-cloud-go websecurityscanner/v1.7.6 : Apache License 2.0 +google-cloud-go pubsub/v1.49.0 : Apache License 2.0 -google-cloud-go workflows/v1.14.2 : Apache License 2.0 +google-cloud-go recaptchaenterprise/v2.20.4 : Apache License 2.0 -GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.27.0 : Apache License 2.0 +google-cloud-go recommendationengine/v0.9.6 : Apache License 2.0 -GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.51.0 : Apache License 2.0 +google-cloud-go recommender/v1.13.5 : Apache License 2.0 -GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.51.0 : Apache License 2.0 +google-cloud-go redis/v1.18.2 : Apache License 2.0 -GoogleCloudPlatform/osconfig 20241004.00 : Apache License 2.0 +google-cloud-go resourcemanager/v1.10.6 : Apache License 2.0 -google/gnostic-models v0.6.8 : Apache License 2.0 +google-cloud-go resourcesettings/v1.8.3 : Apache License 2.0 -google/go-cmp v0.7.0 : BSD 3-clause "New" or "Revised" License +google-cloud-go retail/v1.21.0 : Apache License 2.0 -google-gofuzz v1.2.0 : Apache License 2.0 +google-cloud-go run/v1.10.0 : Apache License 2.0 -google/go-pkcs11 v0.3.0 : Apache License 2.0 +google-cloud-go scheduler/v1.11.7 : Apache License 2.0 -google/pprof 20241029-snapshot-d1b30feb : Apache License 2.0 +google-cloud-go secretmanager/v1.14.7 : Apache License 2.0 -google/s2a-go v0.1.9 : Apache License 2.0 +google-cloud-go securitycenter/v1.36.2 : Apache License 2.0 -Googleuuid v1.6.0 : BSD 3-clause "New" or "Revised" License +google-cloud-go security/v1.18.5 : Apache License 2.0 -go-openapi/analysis v0.23.0 : Apache License 2.0 +google-cloud-go servicedirectory/v1.12.6 : Apache License 2.0 -go-openapi/errors 20250603-snapshot : Apache License 2.0 +google-cloud-go shell/v1.8.6 : Apache License 2.0 -go-openapi/errors v0.22.1 : Apache License 2.0 +google-cloud-go spanner/v1.82.0 : Apache License 2.0 -go-openapi/jsonpointer v0.21.0 : Apache License 2.0 +google-cloud-go speech/v1.27.1 : Apache License 2.0 -go-openapi/loads v0.22.0 : Apache License 2.0 +google-cloud-go storagetransfer/v1.13.0 : Apache License 2.0 -go-openapi/runtime v0.28.0 : Apache License 2.0 +google-cloud-go storage/v1.56.0 : Apache License 2.0 -go-openapi/spec v0.21.0 : Apache License 2.0 +google-cloud-go talent/v1.8.3 : Apache License 2.0 -go-openapi/validate v0.24.0 : Apache License 2.0 +google-cloud-go texttospeech/v1.13.0 : Apache License 2.0 -go.opentelemetry.io/proto otlp/v1.3.1 : Apache License 2.0 +google-cloud-go tpu/v1.8.3 : Apache License 2.0 -go-plist v1.0.1 : (BSD 3-clause "New" or "Revised" License OR BSD 2-Clause with views sentence) +google-cloud-go trace/v1.11.6 : Apache License 2.0 -go-restful v3.11.0 : MIT License +google-cloud-go translate/v1.12.5 : Apache License 2.0 -gorilla/mux v1.8.1 : BSD 3-clause "New" or "Revised" License +google-cloud-go v0.121.6 : Apache License 2.0 -gorilla/websocket v1.5.0 : BSD 2-clause "Simplified" License +google-cloud-go videointelligence/v1.12.6 : Apache License 2.0 -go-spew 20180930-snapshot-d8f796af : ISC License +google-cloud-go video/v1.24.0 : Apache License 2.0 -go-systemd 20191104-snapshot-d3cd4ed1 : Apache License 2.0 +google-cloud-go vision/v2.9.5 : Apache License 2.0 -go-systemd v22.5.0 : Apache License 2.0 +google-cloud-go vmmigration/v1.8.6 : Apache License 2.0 -go-task/slim-sprig v3.0.0 : MIT License +google-cloud-go vmwareengine/v1.3.5 : Apache License 2.0 -Go Testify v1.10.0 : MIT License +google-cloud-go vpcaccess/v1.8.6 : Apache License 2.0 -go.uber.org/goleak v1.3.0 : MIT License +google-cloud-go webrisk/v1.11.1 : Apache License 2.0 -go.uber.org/multierr v1.11.0 : MIT License +google-cloud-go websecurityscanner/v1.7.6 : Apache License 2.0 -govalidator 20230301-snapshot-a9d515a0 : MIT License +google-cloud-go workflows/v1.14.2 : Apache License 2.0 -go-zap v1.27.0 : MIT License +GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.29.0 : Apache License 2.0 -Grafana 10.2.6 : GNU Affero General Public License v3.0 +GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.53.0 : Apache License 2.0 -gregjones/httpcache 20190611-snapshot-901d9072 : MIT License +GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.53.0 : Apache License 2.0 -groupcache 20210331-snapshot-41bb18bf : Apache License 2.0 +google/gnostic-models v0.7.0 : Apache License 2.0 -grpc-ecosystem/go-grpc-middleware v1.3.0 : Apache License 2.0 +google/go-cmp v0.7.0 : BSD 3-clause "New" or "Revised" License -grpc-ecosystem/go-grpc-prometheus v1.2.0 : Apache License 2.0 +google-gofuzz v1.2.0 : Apache License 2.0 -grpc-gateway v1.16.0 : BSD 3-clause "New" or "Revised" License +google/go-pkcs11 v0.3.0 : Apache License 2.0 -grpc-gateway v2.20.0 : BSD 3-clause "New" or "Revised" License +google/pprof 20250403-snapshot-27863c87 : Apache License 2.0 -grpc-go 20250607-snapshot : Apache License 2.0 +google/s2a-go v0.1.9 : Apache License 2.0 -grpc-go 20250610-snapshot : Apache License 2.0 +Googleuuid v1.6.0 : BSD 3-clause "New" or "Revised" License -grpc-go v1.73.0 : Apache License 2.0 +go-openapi/analysis v0.24.0 : Apache License 2.0 -inconshreveable/mousetrap v1.1.0 : Apache License 2.0 +go-openapi/errors v0.22.3 : Apache License 2.0 -inspektor-gadget/inspektor-gadget v0.39.0 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND GNU General Public License v2.0 with Linux Syscall Note AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License AND Mozilla Public License 2.0) +go-openapi/jsonpointer v0.22.1 : Apache License 2.0 -jarcoal/httpmock v1.4.0 : MIT License +go-openapi/loads v0.23.1 : Apache License 2.0 -jonboulle-clockwork v0.4.0 : Apache License 2.0 +go-openapi/runtime v0.29.0 : Apache License 2.0 -josharian/intern v1.0.0 : MIT License +go-openapi/spec v0.22.0 : Apache License 2.0 -jpillora-backoff 1.0.0 : MIT License +go-openapi/validate v0.25.0 : Apache License 2.0 -jsoniter-go v1.1.12 : MIT License +go.opentelemetry.io/proto otlp/v1.5.0 : Apache License 2.0 -jsonreference v0.21.0 : Apache License 2.0 +go-plist v1.0.1 : (BSD 3-clause "New" or "Revised" License OR BSD 2-Clause with views sentence) -julienschmidt/httprouter v1.3.0 : BSD 3-clause "New" or "Revised" License +Go programming language 0 : BSD 3-clause "New" or "Revised" License -k8s.io/code-generator kubernetes-1.18.2-beta.0 : Apache License 2.0 +Go programming language 1.23rc1 : BSD 3-clause "New" or "Revised" License -k8s.io/code-generator v0.32.1 : Apache License 2.0 +Go programming language 20160322-snapshot : Go BSD License with Patent Provision -k8s.io/klog 2.130.1 : Apache License 2.0 +Go programming language 20170806-snapshot : BSD 3-clause "New" or "Revised" License -k8s.io/kube-openapi 20241105-snapshot-32ad38e4 : Apache License 2.0 +Go programming language go1.20rc1 : BSD 3-clause "New" or "Revised" License -k8s.io/utils 20241210-snapshot-24370bea : Apache License 2.0 +go-restful v3.12.2 : MIT License -keybase/go-keychain 20231219-snapshot-57a3676c : MIT License +gorilla/mux v1.8.1 : BSD 3-clause "New" or "Revised" License -kisielk-gotool v1.0.0 : (MIT License AND BSD 3-clause "New" or "Revised" License) +gorilla/websocket 20250226-snapshot-e064f32e : BSD 2-clause "Simplified" License -klauspost-compress v1.18.0 : BSD 3-clause "New" or "Revised" License +go-spew 20180930-snapshot-d8f796af : ISC License -kr/pretty v0.3.1 : MIT License +go-systemd 20191104-snapshot-d3cd4ed1 : Apache License 2.0 -kubernetes/api 20250213-snapshot : Apache License 2.0 +go-systemd v22.5.0 : Apache License 2.0 -kubernetes/api v0.32.1 : Apache License 2.0 +go-task/slim-sprig v3.0.0 : MIT License -kubernetes/apiextensions-apiserver 20241206-snapshot : Apache License 2.0 +Go Testify v1.11.1 : MIT License -kubernetes/apiextensions-apiserver v0.32.1 : Apache License 2.0 +Go Testify v1.9.0 : MIT License -kubernetes/apimachinery 20250211-snapshot : Apache License 2.0 +go.uber.org/automaxprocs v1.6.0 : MIT License -kubernetes/apimachinery v0.32.1 : Apache License 2.0 +go.uber.org/goleak v1.3.0 : MIT License -kubernetes/apiserver v0.32.1 : Apache License 2.0 +go.uber.org/mock 20241018-snapshot : Apache License 2.0 -kubernetes/component-base v0.32.1 : Apache License 2.0 +go.uber.org/multierr v1.11.0 : MIT License -kubernetes-csi/csi-proxy client/v1.2.1 : Apache License 2.0 +govalidator 20230301-snapshot-a9d515a0 : MIT License -kubernetes-csi/external-snapshotter 20250117-snapshot : Apache License 2.0 +govalidator v11.0.1 : MIT License -kubernetes-csi/external-snapshotter client/v8.2.0 : Apache License 2.0 +go.yaml.in/yaml/v2 v2.4.2 : MIT License -kubernetes/kms v0.32.1 : Apache License 2.0 +go.yaml.in/yaml/v2 v3.0.4 : MIT License -kubernetes/mount-utils v0.32.1 : Apache License 2.0 +go-zap v1.27.0 : MIT License -kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.0 : Apache License 2.0 +Grafana 9.0.2 : GNU Affero General Public License v3.0 -kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.0.50 : Apache License 2.0 +Grafana 9.3.6 : GNU Affero General Public License v3.0 -kubernetes-sigs/structured-merge-diff v4.4.2 : Apache License 2.0 +gregjones/httpcache 20190611-snapshot-901d9072 : MIT License -mailru/easyjson v0.9.0 : MIT License +groupcache 20210331-snapshot-41bb18bf : Apache License 2.0 -mapstructure v1.5.0 : MIT License +grpc-ecosystem/go-grpc-middleware providers/prometheus/v1.0.1 : Apache License 2.0 -martian v3.3.3 : Apache License 2.0 +grpc-ecosystem/go-grpc-middleware v2.3.0 : Apache License 2.0 -mattn-go-runewidth v0.0.10 : MIT License +grpc-ecosystem/go-grpc-prometheus v1.2.0 : Apache License 2.0 -matttproud-golang_protobuf_extensions v1.0.4 : Apache License 2.0 +grpc-gateway v2.26.3 : BSD 3-clause "New" or "Revised" License -maxatome/go-testdeep 1.14.0 : BSD 2-clause "Simplified" License +grpc-go v1.76.0 : Apache License 2.0 -mendersoftware/mendertesting 0.0~git20200227.1396c95 : Apache License 2.0 +helm/helm v3.19.0 : Apache License 2.0 -Microsoft-go-winio v0.6.0 : MIT License +hpe-storage/dory v1.0.1 : Apache License 2.0 -mitchellh-hashstructure v2.0.2 : MIT License +hugo-staticsite v0.115.1 : Apache License 2.0 -moby/sys mountinfo/v0.7.2 : Apache License 2.0 +hugo-staticsite v0.121.1 : Apache License 2.0 -moby/sys userns/v0.1.0 : Apache License 2.0 +hugo-staticsite v0.124.1 : Apache License 2.0 -modern-go/concurrent 20180305-snapshot-bacd9c7e : Apache License 2.0 +hugo-staticsite v0.131.0 : Apache License 2.0 -modern-go/reflect2 v1.0.2 : Apache License 2.0 +inconshreveable/mousetrap v1.1.0 : Apache License 2.0 -mongodb/mongo-go-driver v1.14.0 : Apache License 2.0 +InfluxDB v2.7.10 : Apache License 2.0 -mschoch/smat v0.2.0 : Apache License 2.0 +Istio 1.17.1 : Apache License 2.0 -mwitkow/go-conntrack 20190716-snapshot-2f068394 : Apache License 2.0 +Istio 1.22.1 : Apache License 2.0 -natefinch/lumberjack v2.2.1 : MIT License +jarcoal/httpmock v1.4.1 : MIT License -NetApp/trident v25.02.1 : Apache License 2.0 +jonboulle-clockwork v0.5.0 : Apache License 2.0 -niemeyer/pretty 20200227-snapshot-a10e7cae : MIT License +josharian/intern v1.0.0 : MIT License -NYTimes-gziphandler v1.1.1 : Apache License 2.0 +jpillora-backoff 1.0.0 : MIT License -oklog/ulid v1.3.1 : Apache License 2.0 +jsoniter-go v1.1.12 : MIT License -olekukonko-tablewriter v0.0.5 : MIT License +jsonreference v0.21.2 : Apache License 2.0 -onsi/ginkgo 2.21.0 : MIT License +julienschmidt/httprouter v1.3.0 : BSD 3-clause "New" or "Revised" License -OpenCensus 0.2.1 : Apache License 2.0 +k8s.io/code-generator kubernetes-1.18.2-beta.0 : Apache License 2.0 -opencontainers/go-digest 1.0.0 : Apache License 2.0 +k8s.io/code-generator v0.34.1 : Apache License 2.0 -open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.27.0 : Apache License 2.0 +k8s.io/klog 2.130.1 : Apache License 2.0 -open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.28.0 : Apache License 2.0 +k8s.io/kube-openapi 20250710-snapshot-f3f2b991 : Apache License 2.0 -open-telemetry/opentelemetry-go metric/v1.35.0 : Apache License 2.0 +k8s.io/kube-openapi 20250910-snapshot : Apache License 2.0 -open-telemetry/opentelemetry-go sdk/metric/v1.35.0 : Apache License 2.0 +k8s.io/utils 20251002-snapshot-bc988d57 : Apache License 2.0 -open-telemetry/opentelemetry-go sdk/v1.35.0 : Apache License 2.0 +keybase/go-keychain v0.0.1 : MIT License -open-telemetry/opentelemetry-go trace/v1.35.0 : Apache License 2.0 +kisielk-gotool v1.0.0 : (MIT License AND BSD 3-clause "New" or "Revised" License) -open-telemetry/opentelemetry-go v1.35.0 : Apache License 2.0 +klauspost-compress v1.18.0 : BSD 3-clause "New" or "Revised" License -open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.35.0 : Apache License 2.0 +kops - kubernetes v1.21.0-alpha.2 : Apache License 2.0 -open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.60.0 : Apache License 2.0 +kr/pretty v0.3.1 : MIT License -open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.60.0 : Apache License 2.0 +Kubernetes 0.0.1 : Apache License 2.0 -open-telemetry/opentelemetry-go-instrumentation sdk/v1.1.0 : Apache License 2.0 +Kubernetes 0.1.0 : Apache License 2.0 -opentracing-opentracing-go v1.2.0 : Apache License 2.0 +Kubernetes 0.19.0 : Apache License 2.0 -osbuild-osbuild-composer 126 : Apache License 2.0 +Kubernetes 0.2 : Apache License 2.0 -pkg/browser 20240102-snapshot-5ac0b6a4 : BSD 2-clause "Simplified" License +Kubernetes 0.3.0 : Apache License 2.0 -pkg/errors v0.9.1 : BSD 2-clause "Simplified" License +Kubernetes 0.4.0 : Apache License 2.0 -pmezard-go-difflib 20190219-snapshot-5d4384ee : Apache License 2.0 +Kubernetes 0.4.1 : Apache License 2.0 -prometheus-client_model v0.6.1 : Apache License 2.0 +Kubernetes 0.4.2 : Apache License 2.0 -prometheus-common v0.62.0 : Apache License 2.0 +Kubernetes 0.4.3 : Apache License 2.0 -prometheus-procfs v0.16.1 : Apache License 2.0 +Kubernetes 0.5 : Apache License 2.0 -RoaringBitmap-roaring v2.5.0 : Apache License 2.0 +Kubernetes 0.8.0 : Apache License 2.0 -rogpeppe/go-internal v1.13.1 : BSD 3-clause "New" or "Revised" License +Kubernetes 0.8.2 : Apache License 2.0 -secureheader v0.2.0 : MIT License +Kubernetes 1.1.0 : Apache License 2.0 -sigs.k8s.io/json 20241010-snapshot-9aa6b5e7 : Apache License 2.0 +Kubernetes 1.1.0-rc1 : Apache License 2.0 -sigs.k8s.io/yaml v1.4.0 : Apache License 2.0 +Kubernetes 1.11.5 : Apache License 2.0 -Sirupsen/logrus v1.9.3 : MIT License +Kubernetes 1.1.2 : Apache License 2.0 -soheilhy/cmux v0.1.5 : Apache License 2.0 +Kubernetes 1.13.0-alpha.1 : Apache License 2.0 -spf13-afero 20250326-snapshot : Apache License 2.0 +Kubernetes 1.21.6 : Apache License 2.0 -spf13-afero v1.14.0 : Apache License 2.0 +Kubernetes 1.26.0-alpha.1 : Apache License 2.0 -spf13-cobra 1.9.1 : Apache License 2.0 +Kubernetes 1.26.0-alpha.2 : Apache License 2.0 -spiffe/go-spiffe v2.5.0 : Apache License 2.0 +Kubernetes 1.27.2 : Apache License 2.0 -stoewer/go-strcase v1.3.0 : MIT License +Kubernetes 1.33.0 : Apache License 2.0 -stretchr/objx v0.5.2 : MIT License +Kubernetes 1.5.4 : Apache License 2.0 -strfmt v0.23.0 : Apache License 2.0 +Kubernetes 1.8.0-alpha.3 : Apache License 2.0 -swag v0.23.1 : Apache License 2.0 +Kubernetes 1.8.10 : Apache License 2.0 -tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3 : MIT License +Kubernetes 1.8.8 : Apache License 2.0 -VictoriaMetrics v1.111.0 : Apache License 2.0 +Kubernetes 20200509-snapshot : Apache License 2.0 -VictoriaMetrics v1.112.0 : Apache License 2.0 +Kubernetes ccm/v22.0.0 : Apache License 2.0 -vishvananda-netlink 20250523-snapshot : Apache License 2.0 +Kubernetes providers/v0.21.0 : Apache License 2.0 -vishvananda-netlink v1.3.1 : Apache License 2.0 +Kubernetes v0.3.0-rc1 : Apache License 2.0 -vishvananda-netns v0.0.5 : Apache License 2.0 +Kubernetes v0.5.0 : Apache License 2.0 -x448/float16 v0.8.4 : MIT License +Kubernetes v0.7.0 : Apache License 2.0 -xdg-go/scram v1.1.2 : Apache License 2.0 +Kubernetes v0.8.1 : Apache License 2.0 -xdg-go/stringprep v1.0.4 : Apache License 2.0 +Kubernetes v0.8.3 : Apache License 2.0 -xhit/go-str2duration v2.1.0 : BSD 3-clause "New" or "Revised" License +Kubernetes v0.9.0 : Apache License 2.0 -xiang90-probing 20221125-snapshot-a49e3df8 : MIT License +Kubernetes v1.10.0-alpha.1 : Apache License 2.0 -yaml for Go 20141213-snapshot-9f9df343 : (MIT License AND Apache License 2.0) +Kubernetes v1.10.0-alpha.2 : Apache License 2.0 -yaml for Go v2.4.0 : Apache License 2.0 +Kubernetes v1.11.0-beta.1 : Apache License 2.0 -yaml for Go v3.0.1 : (MIT License AND Apache License 2.0) +Kubernetes v1.12.0-beta.1 : Apache License 2.0 -youmark/pkcs8 20181117-snapshot-1be2e3e5 : MIT License +Kubernetes v1.13.0-beta.1 : Apache License 2.0 -yuin/goldmark v1.4.13 : MIT License +Kubernetes v1.17.0-alpha.0 : Apache License 2.0 -zcalusic/sysinfo v1.1.3 : MIT License +Kubernetes v1.2.0-alpha.4 : Apache License 2.0 -zeebo/errs v1.4.0 : MIT License +Kubernetes v1.2.0-alpha.6 : Apache License 2.0 +Kubernetes v1.25.0-alpha.2 : Apache License 2.0 -Licenses: +Kubernetes v1.25.0-alpha.3 : Apache License 2.0 -Apache License 2.0 -(aws/aws-sdk-go-v2 config/v1.29.2, aws/aws-sdk-go-v2 credentials/v1.17.55, aws/aws-sdk-go-v2 feature/ec2/imds/v1.16.25, aws/aws-sdk-go-v2 internal/configsources/v1.3.32, aws/aws-sdk-go-v2 internal/endpoints/v2.6.32, aws/aws-sdk-go-v2 internal/ini/v1.8.2, aws/aws-sdk-go-v2 service/fsx/v1.52.0, aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.12.2, aws/aws-sdk-go-v2 service/internal/presigned-url/v1.12.10, aws/aws-sdk-go-v2 service/secretsmanager/v1.34.14, aws/aws-sdk-go-v2 service/sso/v1.24.12, aws/aws-sdk-go-v2 service/ssooidc/v1.28.11, aws/aws-sdk-go-v2 service/sts/v1.33.10, aws/aws-sdk-go-v2 v1.36.1, Azure/azure-sdk-for-go v2.0.0-beta, Azure/azure-sdk-for-go v3.0.0-beta, brunoga/deep v1.2.4, btree v1.0.1, census-instrumentation/opencensus-go v0.24.0, client-go v0.32.1, client_golang 20250409-snapshot, client_golang v1.22.0, cncf/udpa 20201120-snapshot-5459f2c9, container-storage-interface/spec v1.11.0, containerd/containerd v1.7.23, containerd/containerd v2.0.5, containerd/containerd v2.1.0, CoreOS v0.3.1, docker-compose v2.33.0, docker-go-plugins-helpers 20240701-snapshot-45e24314, docker-go-plugins-helpers 20241106-snapshot, docker-go-units v0.5.0, docker/buildx v0.20.1, elastic/go-sysinfo 184688adcb6ddaa744fe787e6e6a47a95f8b5e44, elastic/go-sysinfo 20250425-snapshot, elastic/go-windows v1.0.2, envoyproxy/go-control-plane envoy/v1.32.4, envoyproxy/go-control-plane ratelimit/v0.1.0, envoyproxy/go-control-plane v0.13.4, gengo 20240911-snapshot-2b36238f, github.com/aws/smithy-go 20250514-snapshot, github.com/aws/smithy-go v1.22.2, github.com/cncf/xds 20250326-snapshot-ae57f3c0, github.com/distribution/reference v0.6.0, github.com/google/cel-spec v0.23.0, github.com/kubernetes-csi/csi-lib-utils v0.16.0, github.com/mattermost/xml-roundtrip-validator 20230502-snapshot-3079e7b8, github.com/moby/spdystream v0.5.0, github.com/xdg-go/pbkdf2 1.0.0, go-etcd api/v3.5.16, go-etcd client/pkg/v3.5.16, go-etcd client/v2.305.16, go-etcd client/v3.5.16, go-etcd pkg/v3.5.16, go-etcd raft/v3.5.16, go-etcd server/v3.5.16, go-jose 4.0.5, go-logr/logr v1.4.2, go-logr/stdr v1.2.2, go-openapi/analysis v0.23.0, go-openapi/errors 20250603-snapshot, go-openapi/errors v0.22.1, go-openapi/jsonpointer v0.21.0, go-openapi/loads v0.22.0, go-openapi/runtime v0.28.0, go-openapi/spec v0.21.0, go-openapi/validate v0.24.0, go-systemd 20191104-snapshot-d3cd4ed1, go-systemd v22.5.0, go.opentelemetry.io/proto otlp/v1.3.1, godebug v1.1.0, golang-github-docker-go-connections-dev 0.4.0, golang-mock v1.1.1, golang/appengine v1.6.8, golang/glog v1.2.4, google-cloud-go 20250529-snapshot, google-cloud-go 20250610-snapshot, google-cloud-go accessapproval/v1.8.6, google-cloud-go accesscontextmanager/v1.9.6, google-cloud-go aiplatform/v1.85.0, google-cloud-go analytics/v0.28.0, google-cloud-go apigateway/v1.7.6, google-cloud-go apigeeconnect/v1.7.6, google-cloud-go apigeeregistry/v0.9.6, google-cloud-go appengine/v1.9.6, google-cloud-go area120/v0.9.6, google-cloud-go artifactregistry/v1.17.1, google-cloud-go asset/v1.21.0, google-cloud-go assuredworkloads/v1.12.6, google-cloud-go auth/oauth2adapt/v0.2.8, google-cloud-go auth/v0.16.1, google-cloud-go automl/v1.14.7, google-cloud-go baremetalsolution/v1.3.6, google-cloud-go batch/v1.12.2, google-cloud-go beyondcorp/v1.1.6, google-cloud-go bigquery/v1.67.0, google-cloud-go bigtable/v1.37.0, google-cloud-go billing/v1.20.4, google-cloud-go binaryauthorization/v1.9.5, google-cloud-go certificatemanager/v1.9.5, google-cloud-go channel/v1.19.5, google-cloud-go cloudbuild/v1.22.2, google-cloud-go clouddms/v1.8.7, google-cloud-go cloudtasks/v1.13.6, google-cloud-go compute/metadata/v0.7.0, google-cloud-go compute/v1.38.0, google-cloud-go contactcenterinsights/v1.17.3, google-cloud-go container/v1.42.4, google-cloud-go containeranalysis/v0.14.1, google-cloud-go datacatalog/v1.26.0, google-cloud-go dataflow/v0.10.6, google-cloud-go dataform/v0.11.2, google-cloud-go datafusion/v1.8.6, google-cloud-go datalabeling/v0.9.6, google-cloud-go dataplex/v1.25.2, google-cloud-go dataproc/v2.11.2, google-cloud-go dataqna/v0.9.6, google-cloud-go datastore/v1.20.0, google-cloud-go datastream/v1.14.1, google-cloud-go deploy/v1.27.1, google-cloud-go dialogflow/v1.68.2, google-cloud-go dlp/v1.22.1, google-cloud-go documentai/v1.37.0, google-cloud-go domains/v0.10.6, google-cloud-go edgecontainer/v1.4.3, google-cloud-go errorreporting/v0.3.2, google-cloud-go essentialcontacts/v1.7.6, google-cloud-go eventarc/v1.15.5, google-cloud-go filestore/v1.10.2, google-cloud-go firestore/v1.18.0, google-cloud-go functions/v1.19.6, google-cloud-go gkebackup/v1.7.0, google-cloud-go gkeconnect/v0.12.4, google-cloud-go gkehub/v0.15.6, google-cloud-go gkemulticloud/v1.5.3, google-cloud-go gsuiteaddons/v1.7.7, google-cloud-go iam/v1.5.2, google-cloud-go iap/v1.11.1, google-cloud-go ids/v1.5.6, google-cloud-go iot/v1.8.6, google-cloud-go kms/v1.21.2, google-cloud-go language/v1.14.5, google-cloud-go lifesciences/v0.10.6, google-cloud-go logging/v1.13.0, google-cloud-go longrunning/v0.6.7, google-cloud-go managedidentities/v1.7.6, google-cloud-go maps/v1.20.4, google-cloud-go mediatranslation/v0.9.6, google-cloud-go memcache/v1.11.6, google-cloud-go metastore/v1.14.6, google-cloud-go monitoring/v1.24.2, google-cloud-go netapp/v1.9.0, google-cloud-go networkconnectivity/v1.17.1, google-cloud-go networkmanagement/v1.19.1, google-cloud-go networksecurity/v0.10.6, google-cloud-go notebooks/v1.12.6, google-cloud-go optimization/v1.7.6, google-cloud-go orchestration/v1.11.9, google-cloud-go orgpolicy/v1.15.0, google-cloud-go osconfig/v1.14.5, google-cloud-go oslogin/v1.14.6, google-cloud-go phishingprotection/v0.9.6, google-cloud-go policytroubleshooter/v1.11.6, google-cloud-go privatecatalog/v0.10.7, google-cloud-go pubsub/v1.49.0, google-cloud-go pubsublite/v1.8.2, google-cloud-go recaptchaenterprise/v2.20.4, google-cloud-go recommendationengine/v0.9.6, google-cloud-go recommender/v1.13.5, google-cloud-go redis/v1.18.2, google-cloud-go resourcemanager/v1.10.6, google-cloud-go resourcesettings/v1.8.3, google-cloud-go retail/v1.20.0, google-cloud-go run/v1.9.3, google-cloud-go scheduler/v1.11.7, google-cloud-go secretmanager/v1.14.7, google-cloud-go security/v1.18.5, google-cloud-go securitycenter/v1.36.2, google-cloud-go servicedirectory/v1.12.6, google-cloud-go shell/v1.8.6, google-cloud-go spanner/v1.80.0, google-cloud-go speech/v1.27.1, google-cloud-go storage/v1.52.0, google-cloud-go storagetransfer/v1.12.4, google-cloud-go talent/v1.8.3, google-cloud-go texttospeech/v1.12.1, google-cloud-go tpu/v1.8.3, google-cloud-go trace/v1.11.6, google-cloud-go translate/v1.12.5, google-cloud-go v0.121.0, google-cloud-go video/v1.23.5, google-cloud-go videointelligence/v1.12.6, google-cloud-go vision/v2.9.5, google-cloud-go vmmigration/v1.8.6, google-cloud-go vmwareengine/v1.3.5, google-cloud-go vpcaccess/v1.8.6, google-cloud-go webrisk/v1.11.1, google-cloud-go websecurityscanner/v1.7.6, google-cloud-go workflows/v1.14.2, google-gofuzz v1.2.0, google/cel-go v0.22.0, google/gnostic-models v0.6.8, google/go-pkcs11 v0.3.0, google/pprof 20241029-snapshot-d1b30feb, google/s2a-go v0.1.9, googleapis/enterprise-certificate-proxy v0.3.6, googleapis/go-genproto 20250505-snapshot-f936aa4a, googleapis/go-genproto 20250512-snapshot-5a2f75b7, googleapis/go-genproto 20250603-snapshot-513f2392, googleapis/go-genproto 20250604-snapshot, GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.27.0, GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.51.0, GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.51.0, GoogleCloudPlatform/osconfig 20241004.00, groupcache 20210331-snapshot-41bb18bf, grpc-ecosystem/go-grpc-middleware v1.3.0, grpc-ecosystem/go-grpc-prometheus v1.2.0, grpc-go 20250607-snapshot, grpc-go 20250610-snapshot, grpc-go v1.73.0, inconshreveable/mousetrap v1.1.0, inspektor-gadget/inspektor-gadget v0.39.0, jonboulle-clockwork v0.4.0, jsonreference v0.21.0, k8s.io/code-generator kubernetes-1.18.2-beta.0, k8s.io/code-generator v0.32.1, k8s.io/klog 2.130.1, k8s.io/kube-openapi 20241105-snapshot-32ad38e4, k8s.io/utils 20241210-snapshot-24370bea, kubernetes-csi/csi-proxy client/v1.2.1, kubernetes-csi/external-snapshotter 20250117-snapshot, kubernetes-csi/external-snapshotter client/v8.2.0, kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.0, kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.0.50, kubernetes-sigs/structured-merge-diff v4.4.2, kubernetes/api 20250213-snapshot, kubernetes/api v0.32.1, kubernetes/apiextensions-apiserver 20241206-snapshot, kubernetes/apiextensions-apiserver v0.32.1, kubernetes/apimachinery 20250211-snapshot, kubernetes/apimachinery v0.32.1, kubernetes/apiserver v0.32.1, kubernetes/component-base v0.32.1, kubernetes/kms v0.32.1, kubernetes/mount-utils v0.32.1, martian v3.3.3, matttproud-golang_protobuf_extensions v1.0.4, mendersoftware/mendertesting 0.0~git20200227.1396c95, moby/sys mountinfo/v0.7.2, moby/sys userns/v0.1.0, modern-go/concurrent 20180305-snapshot-bacd9c7e, modern-go/reflect2 v1.0.2, mongodb/mongo-go-driver v1.14.0, mschoch/smat v0.2.0, mwitkow/go-conntrack 20190716-snapshot-2f068394, NetApp/trident v25.02.1, NYTimes-gziphandler v1.1.1, oklog/ulid v1.3.1, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.27.0, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.28.0, open-telemetry/opentelemetry-go metric/v1.35.0, open-telemetry/opentelemetry-go sdk/metric/v1.35.0, open-telemetry/opentelemetry-go sdk/v1.35.0, open-telemetry/opentelemetry-go trace/v1.35.0, open-telemetry/opentelemetry-go v1.35.0, open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.35.0, open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.60.0, open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.60.0, open-telemetry/opentelemetry-go-instrumentation sdk/v1.1.0, OpenCensus 0.2.1, opencontainers/go-digest 1.0.0, opentracing-opentracing-go v1.2.0, osbuild-osbuild-composer 126, pmezard-go-difflib 20190219-snapshot-5d4384ee, prometheus-client_model v0.6.1, prometheus-common v0.62.0, prometheus-procfs v0.16.1, RoaringBitmap-roaring v2.5.0, sigs.k8s.io/json 20241010-snapshot-9aa6b5e7, sigs.k8s.io/yaml v1.4.0, soheilhy/cmux v0.1.5, spf13-afero 20250326-snapshot, spf13-afero v1.14.0, spf13-cobra 1.9.1, spiffe/go-spiffe v2.5.0, strfmt v0.23.0, swag v0.23.1, VictoriaMetrics v1.111.0, VictoriaMetrics v1.112.0, vishvananda-netlink 20250523-snapshot, vishvananda-netlink v1.3.1, vishvananda-netns v0.0.5, xdg-go/scram v1.1.2, xdg-go/stringprep v1.0.4, yaml for Go 20141213-snapshot-9f9df343, yaml for Go v2.4.0, yaml for Go v3.0.1) +Kubernetes v1.2.5-rc1 : Apache License 2.0 -Apache License -Version 2.0, January 2004 +Kubernetes v1.26.0-beta.0 : Apache License 2.0 -========================= +Kubernetes v1.28.0-alpha.2 : Apache License 2.0 +Kubernetes v1.3.0-alpha.3 : Apache License 2.0 -http://www.apache.org/licenses/ +Kubernetes v1.34.0-rc.1 : Apache License 2.0 +Kubernetes v1.4.0-alpha.2 : Apache License 2.0 -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Kubernetes v1.7.0-alpha.2 : Apache License 2.0 -1. Definitions. +Kubernetes v1.8.0-beta.0 : Apache License 2.0 +Kubernetes v1.8.0-beta.1 : Apache License 2.0 -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. +kubernetes/api 20250901-snapshot : Apache License 2.0 +kubernetes/api v0.34.1 : Apache License 2.0 -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. +kubernetes/apiextensions-apiserver v0.34.1 : Apache License 2.0 +kubernetes/apimachinery 20250908-snapshot : Apache License 2.0 -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. +kubernetes/apimachinery 20250919-snapshot : Apache License 2.0 -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by +kubernetes/apimachinery v0.34.1 : Apache License 2.0 -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. +kubernetes/apiserver v0.34.1 : Apache License 2.0 +kubernetes/cloud-provider-azure pkg/azclient/v0.0.48 : Apache License 2.0 -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions -granted by this License. +kubernetes/cloud-provider-azure pkg/azclient/v0.0.49 : Apache License 2.0 +kubernetes/cloud-provider-azure pkg/azclient/v0.0.8 : Apache License 2.0 -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration +kubernetes/component-base v0.34.1 : Apache License 2.0 -files. +kubernetes-csi/csi-proxy client/v1.3.0 : Apache License 2.0 -"Object" form shall mean any form resulting from mechanical transformation or +kubernetes-csi/csi-proxy v1.0.0-rc.1 : Apache License 2.0 -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. +kubernetes-csi/external-snapshotter 20250117-snapshot : Apache License 2.0 +kubernetes-csi/external-snapshotter client/v8.2.0 : Apache License 2.0 -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included +kubernetes/kms v0.34.1 : Apache License 2.0 -in or attached to the work (an example is provided in the Appendix below). +kubernetes/mount-utils 20250911-snapshot : Apache License 2.0 -"Derivative Works" shall mean any work, whether in Source or Object form, that is +kubernetes/mount-utils v0.34.1 : Apache License 2.0 -based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an +kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.2 : Apache License 2.0 -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by +kubernetes-sigs/cloud-provider-azure 20250831-snapshot : Apache License 2.0 -name) to the interfaces of, the Work and Derivative Works thereof. +kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.9.3 : Apache License 2.0 -"Contribution" shall mean any work of authorship, including the original version +kubernetes-sigs/structured-merge-diff 20250925-snapshot : Apache License 2.0 -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work by +kubernetes-sigs/structured-merge-diff v4.6.0 : Apache License 2.0 -the copyright owner or by an individual or Legal Entity authorized to submit on -behalf of the copyright owner. For the purposes of this definition, "submitted" +kubernetes-sigs/structured-merge-diff v6.3.0 : Apache License 2.0 -means any form of electronic, verbal, or written communication sent to the -Licensor or its representatives, including but not limited to communication on +libstdc++ 12-20220222 : GNU General Public License v3.0 w/GCC Runtime Library exception -electronic mailing lists, source code control systems, and issue tracking systems -that are managed by, or on behalf of, the Licensor for the purpose of discussing +mailru/easyjson v0.9.0 : MIT License -and improving the Work, but excluding communication that is conspicuously marked -or otherwise designated in writing by the copyright owner as "Not a +martian v3.3.3 : Apache License 2.0 -Contribution." +mattn-go-runewidth v0.0.16 : MIT License -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of +maxatome/go-testdeep 1.14.0 : BSD 2-clause "Simplified" License -whom a Contribution has been received by Licensor and subsequently incorporated -within the Work. +mendersoftware/mendertesting 0.0~git20200227.1396c95 : Apache License 2.0 +Microsoft-go-winio v0.6.0 : MIT License -2. Grant of Copyright License. Subject to the terms and conditions of this -License, each Contributor hereby grants to You a perpetual, worldwide, +mitchellh-hashstructure v2.0.2 : MIT License -non-exclusive, no-charge, royalty-free, irrevocable copyright license to -reproduce, prepare Derivative Works of, publicly display, publicly perform, +moby/sys mountinfo/v0.7.2 : Apache License 2.0 -sublicense, and distribute the Work and such Derivative Works in Source or Object -form. +modern-go/concurrent 20180305-snapshot-bacd9c7e : Apache License 2.0 +modern-go/reflect2 20250322-snapshot-35a7c28c : Apache License 2.0 -3. Grant of Patent License. Subject to the terms and conditions of this License, -each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +mongodb/mongo-go-driver 20250809-snapshot : Apache License 2.0 -no-charge, royalty-free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and otherwise +mongodb/mongo-go-driver v1.17.4 : Apache License 2.0 -transfer the Work, where such license applies only to those patent claims -licensable by such Contributor that are necessarily infringed by their +mschoch/smat v0.2.0 : Apache License 2.0 -Contribution(s) alone or by combination of their Contribution(s) with the Work to -which such Contribution(s) was submitted. If You institute patent litigation +mwitkow/go-conntrack 20190716-snapshot-2f068394 : Apache License 2.0 -against any entity (including a cross-claim or counterclaim in a lawsuit) -alleging that the Work or a Contribution incorporated within the Work constitutes +natefinch/lumberjack v2.2.1 : MIT License -direct or contributory patent infringement, then any patent licenses granted to -You under this License for that Work shall terminate as of the date such +NetApp/trident v18.04.0 : Apache License 2.0 -litigation is filed. +NetApp/trident v20.04.0 : Apache License 2.0 -4. Redistribution. You may reproduce and distribute copies of the Work or +NetApp/trident v20.10.0 : Apache License 2.0 -Derivative Works thereof in any medium, with or without modifications, and in -Source or Object form, provided that You meet the following conditions: +NetApp/trident v21.01.0 : Apache License 2.0 +NetApp/trident v21.04.0 : Apache License 2.0 - a. You must give any other recipients of the Work or Derivative Works a copy of - this License; and +NetApp/trident v22.04.0 : Apache License 2.0 +NetApp/trident v22.07.0 : Apache License 2.0 - b. You must cause any modified files to carry prominent notices stating that - You changed the files; and +NetApp/trident v23.01.0 : Apache License 2.0 +NetApp/trident v23.04.0 : Apache License 2.0 - c. You must retain, in the Source form of any Derivative Works that You - distribute, all copyright, patent, trademark, and attribution notices from +NetApp/trident v23.07.0 : Apache License 2.0 - the Source form of the Work, excluding those notices that do not pertain to - any part of the Derivative Works; and +NetApp/trident v24.10.0 : Apache License 2.0 +NetApp/trident v25.06.1 : Apache License 2.0 - d. If the Work includes a "NOTICE" text file as part of its distribution, then - any Derivative Works that You distribute must include a readable copy of the +niemeyer/pretty 20200227-snapshot-a10e7cae : MIT License - attribution notices contained within such NOTICE file, excluding those - notices that do not pertain to any part of the Derivative Works, in at least +NYTimes-gziphandler v1.1.1 : Apache License 2.0 - one of the following places: within a NOTICE text file distributed as part of - the Derivative Works; within the Source form or documentation, if provided +oklog/ulid v1.3.1 : Apache License 2.0 - along with the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices normally appear. +olekukonko-tablewriter v0.0.5 : MIT License - The contents of the NOTICE file are for informational purposes only and do - not modify the License. You may add Your own attribution notices within +onsi/ginkgo v2.23.4 : MIT License - Derivative Works that You distribute, alongside or as an addendum to the - NOTICE text from the Work, provided that such additional attribution notices +OpenCensus 0.2.1 : Apache License 2.0 - cannot be construed as modifying the License. +opencontainers/go-digest 1.0.0 : Apache License 2.0 -You may add Your own copyright statement to Your modifications and may provide +openshift/api 20251013-snapshot-fe48e8fd : Apache License 2.0 -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, +open-telemetry/opentelemetry-go 20250909-snapshot : Apache License 2.0 -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. +open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.34.0 : Apache License 2.0 +open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.34.0 : Apache License 2.0 -5. Submission of Contributions. Unless You explicitly state otherwise, any -Contribution intentionally submitted for inclusion in the Work by You to the +open-telemetry/opentelemetry-go metric/v1.38.0 : Apache License 2.0 -Licensor shall be under the terms and conditions of this License, without any -additional terms or conditions. Notwithstanding the above, nothing herein shall +open-telemetry/opentelemetry-go sdk/metric/v1.37.0 : Apache License 2.0 -supersede or modify the terms of any separate license agreement you may have -executed with Licensor regarding such Contributions. +open-telemetry/opentelemetry-go sdk/v1.38.0 : Apache License 2.0 +open-telemetry/opentelemetry-go trace/v1.38.0 : Apache License 2.0 -6. Trademarks. This License does not grant permission to use the trade names, -trademarks, service marks, or product names of the Licensor, except as required +open-telemetry/opentelemetry-go v1.38.0 : Apache License 2.0 -for reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. +open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.36.0 : Apache License 2.0 +open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.61.0 : Apache License 2.0 -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in -writing, Licensor provides the Work (and each Contributor provides its +open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.61.0 : Apache License 2.0 -Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied, including, without limitation, any warranties or +open-telemetry/opentelemetry-go-instrumentation sdk/v1.2.1 : Apache License 2.0 -conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A -PARTICULAR PURPOSE. You are solely responsible for determining the +osbuild-osbuild-composer 143 : Apache License 2.0 -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. +osbuild-osbuild-composer 149 : Apache License 2.0 +pkg/browser 20240102-snapshot-5ac0b6a4 : BSD 2-clause "Simplified" License -8. Limitation of Liability. In no event and under no legal theory, whether in -tort (including negligence), contract, or otherwise, unless required by +pkg/errors v0.9.1 : BSD 2-clause "Simplified" License -applicable law (such as deliberate and grossly negligent acts) or agreed to in -writing, shall any Contributor be liable to You for damages, including any +pmezard-go-difflib 20190219-snapshot-5d4384ee : Apache License 2.0 -direct, indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to use the +prometheus-client_model v0.6.2 : Apache License 2.0 -Work (including but not limited to damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages or +prometheus-common 20250923-snapshot : Apache License 2.0 -losses), even if such Contributor has been advised of the possibility of such -damages. +prometheus-common v0.66.1 : Apache License 2.0 +prometheus-procfs v0.16.1 : Apache License 2.0 -9. Accepting Warranty or Additional Liability. While redistributing the Work or -Derivative Works thereof, You may choose to offer, and charge a fee for, +RoaringBitmap-roaring v2.0.0 : Apache License 2.0 -acceptance of support, warranty, indemnity, or other liability obligations and/or -rights consistent with this License. However, in accepting such obligations, You +RoaringBitmap-roaring v2.10.0 : Apache License 2.0 -may act only on Your own behalf and on Your sole responsibility, not on behalf of -any other Contributor, and only if You agree to indemnify, defend, and hold each +RoaringBitmap-roaring v2.3.0 : Apache License 2.0 -Contributor harmless for any liability incurred by, or claims asserted against, -such Contributor by reason of your accepting any such warranty or additional +RoaringBitmap-roaring v2.3.2 : Apache License 2.0 -liability. +RoaringBitmap-roaring v2.3.4 : Apache License 2.0 -END OF TERMS AND CONDITIONS +rogpeppe/go-internal v1.14.1 : BSD 3-clause "New" or "Revised" License +runc v1.3.0 : Apache License 2.0 -APPENDIX: How to apply the Apache License to your work +rusoto 0.45.0 : MIT License -To apply the Apache License to your work, attach the following boilerplate +rusoto 0.47.0 : MIT License -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be +secureheader v0.2.0 : MIT License -enclosed in the appropriate comment syntax for the file format. We also recommend -that a file or class name and description of purpose be included on the same +sigs.k8s.io/json 20241014-snapshot-cfa47c3a : Apache License 2.0 -"printed page" as the copyright notice for easier identification within -third-party archives. +sigs.k8s.io/randfill v1.0.0 : Apache License 2.0 +sigs.k8s.io/yaml v1.6.0 : Apache License 2.0 - Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, - Version 2.0 (the "License"); you may not use this file except in compliance +soheilhy/cmux v0.1.5 : Apache License 2.0 - with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law +spf13-afero 20250922-snapshot : Apache License 2.0 - or agreed to in writing, software distributed under the License is - distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +spf13-afero v1.15.0 : Apache License 2.0 - KIND, either express or implied. See the License for the specific language - governing permissions and limitations under the License. +spf13-cobra 1.10.1 : Apache License 2.0 ---- -BSD 2-Clause with views sentence +spf13-cobra 20250922-snapshot : Apache License 2.0 -(go-plist v1.0.1) -BSD 2-Clause with views sentence +spiffe/go-spiffe v2.5.0 : Apache License 2.0 -================================ +stoewer/go-strcase v1.3.0 : MIT License -Copyright (c) All rights reserved. +stretchr/objx v0.5.2 : MIT License +strfmt v0.24.0 : Apache License 2.0 -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +swag cmdutils/v0.25.1 : Apache License 2.0 +swag conv/v0.25.1 : Apache License 2.0 - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +swag fileutils/v0.25.1 : Apache License 2.0 +swag jsonname/v0.25.1 : Apache License 2.0 - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation +swag jsonutils/fixtures_test/v0.25.1 : Apache License 2.0 - and/or other materials provided with the distribution. +swag jsonutils/v0.25.1 : Apache License 2.0 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +swag loading/v0.25.1 : Apache License 2.0 -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +swag mangling/v0.25.1 : Apache License 2.0 -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +swag netutils/v0.25.1 : Apache License 2.0 -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +swag stringutils/v0.25.1 : Apache License 2.0 -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +swag typeutils/v0.25.1 : Apache License 2.0 -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +swag v0.25.1 : Apache License 2.0 -The views and conclusions contained in the software and documentation are those +swag yamlutils/v0.25.1 : Apache License 2.0 -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the copyright holders or contributors. +Telegraf v1.28.1 : MIT License ---- -BSD 2-clause "Simplified" License +teleport bastion v13.3.0 : Apache License 2.0 -(blackfriday v2.1.0) -Upstream-Contact: https://github.com/russross/blackfriday/issues/new +teleport bastion v14.3.0 : GNU Affero General Public License v3.0 +TensorFlow 20240227-snapshot : Apache License 2.0 -Files: * -Copyright: 2011 Russ Ross +tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3 : MIT License -License: BSD-2-clause +VictoriaMetrics v1.112.0 : Apache License 2.0 -Files: debian/* +vishvananda-netlink 20200426-snapshot : Apache License 2.0 -Copyright: 20142015 Tianon Gravi - 2015 Martina Ferrari +vishvananda-netlink v1.3.1 : Apache License 2.0 - 20152020 Anthony Fok - 2016 Dr. Tobias Quathamer +vishvananda-netns v0.0.5 : Apache License 2.0 - 2020 Reinhard Tartler -License: BSD-2-clause +vitessio/vitess v0.19.0-rc1 : Apache License 2.0 +@withfig/autocomplete 2.657.0 : ISC License -License: BSD-2-clause +withfig/autocomplete spec-build-number-0.1303.0 : MIT License -Redistribution and use in source and binary forms, with or without +x448/float16 v0.8.4 : MIT License - modification, are permitted provided that the following conditions are met: - . +xdg-go/scram v1.1.2 : Apache License 2.0 - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +xdg-go/stringprep v1.0.4 : Apache License 2.0 - . - 2. Redistributions in binary form must reproduce the above copyright notice, +xhit/go-str2duration v2.1.0 : BSD 3-clause "New" or "Revised" License - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +xiang90-probing 20221125-snapshot-a49e3df8 : MIT License - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +yaml for Go 20141213-snapshot-9f9df343 : (MIT License AND Apache License 2.0) - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +yaml for Go v2.4.0 : Apache License 2.0 - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +yaml for Go v3.0.1 : (MIT License AND Apache License 2.0) - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +youmark/pkcs8 20240726-snapshot-a2c0da24 : MIT License - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +yuin/goldmark v1.4.13 : MIT License - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE ---- +zcalusic/sysinfo 20250716-snapshot : MIT License -BSD 2-clause "Simplified" License -(gorilla/websocket v1.5.0) +zcalusic/sysinfo v1.1.3 : MIT License -Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. +zeebo/errs v1.4.0 : MIT License -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Licenses: +Apache License 2.0 - Redistributions of source code must retain the above copyright notice, this +(@aws-sdk/client-secrets-manager 3.181.0, @compose-generator/cli 1.0.0, amazon-ecs-agent v1.87.0, AWS SDK for Java 1.12.549, AWS SDK for Node.js 2.1040.0, AWS SDK for Node.js 2.1083.0, AWS SDK for Node.js 2.1114.0, AWS SDK for Node.js 2.1119.0, AWS SDK for Node.js 2.1143.0, AWS SDK for Node.js 2.1226.0, AWS SDK for Node.js 2.1263.0, AWS SDK for Node.js 2.1643.0, AWS SDK for Node.js 2.365.0, AWS SDK for Ruby 1.69.0, AWS SDK for Ruby 1.91.0, aws/aws-sdk-go-v2 config/v1.31.12, aws/aws-sdk-go-v2 credentials/v1.18.16, aws/aws-sdk-go-v2 feature/ec2/imds/v1.18.9, aws/aws-sdk-go-v2 internal/configsources/v1.4.9, aws/aws-sdk-go-v2 internal/endpoints/v2.7.9, aws/aws-sdk-go-v2 internal/ini/v1.8.3, aws/aws-sdk-go-v2 service/fsx/v1.62.0, aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.13.1, aws/aws-sdk-go-v2 service/internal/presigned-url/v1.13.9, aws/aws-sdk-go-v2 service/secretsmanager/v1.39.6, aws/aws-sdk-go-v2 service/sso/v1.29.6, aws/aws-sdk-go-v2 service/ssooidc/v1.35.1, aws/aws-sdk-go-v2 service/sts/v1.38.6, aws/aws-sdk-go-v2 v1.39.2, Azure/azure-sdk-for-go v2.0.0-beta, Azure/azure-sdk-for-go v3.1.0-beta, Azure/azure-sdk-for-go v5.0.0-beta, brunoga/deep 20250815-snapshot, brunoga/deep v1.2.5, btree v1.1.3, census-instrumentation/opencensus-go v0.24.0, client-go 20250901-snapshot, client-go v0.34.1, client_golang v1.23.2, cncf/udpa 20201120-snapshot-5459f2c9, container-storage-interface/spec v1.11.0, containerd/containerd api/v1.8.0-rc.4, containerd/containerd v2.0.5, containerd/containerd v2.1.3, CoreOS v0.3.1, coreos/ignition 2.22.0, csi-provisioner v3.0.0, Docker Moby v22.06.0-beta.0, Docker Moby v25.0.0-beta.1, docker-go-plugins-helpers 20240701-snapshot-45e24314, docker-go-units v0.5.0, docker-org 0.9.0, docker-org v0.1, docker-org v1.0.2, docker/buildx v0.18.0, elastic/go-sysinfo 184688adcb6ddaa744fe787e6e6a47a95f8b5e44, elastic/go-sysinfo 20250922-snapshot, elastic/go-windows v1.0.2, envoyproxy/go-control-plane envoy/v1.32.4, envoyproxy/go-control-plane ratelimit/v0.1.0, envoyproxy/go-control-plane v0.13.4, envoyproxy/protoc-gen-validate 1.2.1, etcd-io/raft v3.6.0, gengo 20250604-snapshot-85fd79db, github.com/aws/smithy-go 20250905-snapshot, github.com/aws/smithy-go metrics/smithyotelmetrics/v1.0.1, github.com/aws/smithy-go v1.23.0, github.com/cncf/xds 20250501-snapshot-2ac532fd, github.com/distribution/reference v0.6.0, github.com/google/cel-spec v0.24.0, github.com/kubernetes-csi/csi-lib-utils v0.22.0, github.com/mattermost/xml-roundtrip-validator v0.1.0, github.com/moby/spdystream v0.5.0, github.com/xdg-go/pbkdf2 1.0.0, go-etcd api/v3.6.4, go-etcd client/pkg/v3.6.4, go-etcd client/v3.6.4, go-etcd pkg/v3.6.4, go-etcd server/v3.6.4, go-jose v4.1.2, go-logr/logr v1.4.3, go-logr/stdr v1.2.2, go-openapi/analysis v0.24.0, go-openapi/errors v0.22.3, go-openapi/jsonpointer v0.22.1, go-openapi/loads v0.23.1, go-openapi/runtime v0.29.0, go-openapi/spec v0.22.0, go-openapi/validate v0.25.0, go-systemd 20191104-snapshot-d3cd4ed1, go-systemd v22.5.0, go.opentelemetry.io/proto otlp/v1.5.0, go.uber.org/mock 20241018-snapshot, godebug v1.1.0, golang-github-docker-go-connections-dev 0.4.0, golang-mock v1.1.1, golang/appengine v1.6.8, golang/glog v1.2.5, google-cloud-go accessapproval/v1.8.6, google-cloud-go accesscontextmanager/v1.9.6, google-cloud-go advisorynotifications/v1.2.0, google-cloud-go aiplatform/v1.48.0, google-cloud-go aiplatform/v1.89.0, google-cloud-go analytics/v0.28.1, google-cloud-go apigateway/v1.7.6, google-cloud-go apigeeconnect/v1.7.6, google-cloud-go apigeeregistry/v0.9.6, google-cloud-go appengine/v1.9.6, google-cloud-go area120/v0.9.6, google-cloud-go artifactregistry/v1.17.1, google-cloud-go asset/v1.21.1, google-cloud-go assuredworkloads/v1.12.6, google-cloud-go auth/oauth2adapt/v0.2.8, google-cloud-go auth/v0.17.0, google-cloud-go automl/v1.14.7, google-cloud-go baremetalsolution/v1.3.6, google-cloud-go batch/v1.12.2, google-cloud-go beyondcorp/v1.1.6, google-cloud-go bigquery/v1.69.0, google-cloud-go bigtable/v1.37.0, google-cloud-go billing/v1.20.4, google-cloud-go binaryauthorization/v1.9.5, google-cloud-go certificatemanager/v1.9.5, google-cloud-go channel/v1.19.5, google-cloud-go cloudbuild/v1.22.2, google-cloud-go clouddms/v1.8.7, google-cloud-go cloudtasks/v1.13.6, google-cloud-go compute/metadata/v0.9.0, google-cloud-go compute/v1.49.0, google-cloud-go contactcenterinsights/v1.17.3, google-cloud-go container/v1.43.0, google-cloud-go containeranalysis/v0.14.1, google-cloud-go datacatalog/v1.26.0, google-cloud-go dataflow/v0.11.0, google-cloud-go dataform/v0.12.0, google-cloud-go datafusion/v1.8.6, google-cloud-go datalabeling/v0.9.6, google-cloud-go dataplex/v1.25.3, google-cloud-go dataproc/v2.11.2, google-cloud-go dataqna/v0.9.7, google-cloud-go datastore/v1.20.0, google-cloud-go datastream/v1.14.1, google-cloud-go deploy/v1.27.2, google-cloud-go dialogflow/v1.68.2, google-cloud-go dlp/v1.23.0, google-cloud-go documentai/v1.37.0, google-cloud-go domains/v0.10.6, google-cloud-go edgecontainer/v1.4.3, google-cloud-go errorreporting/v0.3.2, google-cloud-go essentialcontacts/v1.7.6, google-cloud-go eventarc/v1.15.5, google-cloud-go filestore/v1.10.2, google-cloud-go firestore/v1.18.0, google-cloud-go functions/v1.19.6, google-cloud-go gkebackup/v1.8.0, google-cloud-go gkeconnect/v0.12.4, google-cloud-go gkehub/v0.15.6, google-cloud-go gkemulticloud/v1.5.3, google-cloud-go gsuiteaddons/v1.7.7, google-cloud-go iam/v1.5.2, google-cloud-go iap/v1.11.2, google-cloud-go ids/v1.5.6, google-cloud-go iot/v1.8.6, google-cloud-go kms/v1.22.0, google-cloud-go language/v1.14.5, google-cloud-go lifesciences/v0.10.6, google-cloud-go logging/v1.13.0, google-cloud-go longrunning/v0.6.7, google-cloud-go managedidentities/v1.7.6, google-cloud-go maps/v1.21.0, google-cloud-go mediatranslation/v0.9.6, google-cloud-go memcache/v1.11.6, google-cloud-go metastore/v1.14.7, google-cloud-go monitoring/v1.24.2, google-cloud-go netapp/v1.10.1, google-cloud-go networkconnectivity/v1.17.1, google-cloud-go networkmanagement/v1.19.1, google-cloud-go networksecurity/v0.10.6, google-cloud-go notebooks/v1.12.6, google-cloud-go optimization/v1.7.6, google-cloud-go orchestration/v1.11.9, google-cloud-go orgpolicy/v1.15.0, google-cloud-go osconfig/v1.14.6, google-cloud-go oslogin/v1.14.6, google-cloud-go phishingprotection/v0.9.6, google-cloud-go policytroubleshooter/v1.11.6, google-cloud-go privatecatalog/v0.10.7, google-cloud-go pubsub/v1.49.0, google-cloud-go pubsublite/v1.8.2, google-cloud-go recaptchaenterprise/v2.20.4, google-cloud-go recommendationengine/v0.9.6, google-cloud-go recommender/v1.13.5, google-cloud-go redis/v1.18.2, google-cloud-go resourcemanager/v1.10.6, google-cloud-go resourcesettings/v1.8.3, google-cloud-go retail/v1.21.0, google-cloud-go run/v1.10.0, google-cloud-go scheduler/v1.11.7, google-cloud-go secretmanager/v1.14.7, google-cloud-go security/v1.18.5, google-cloud-go securitycenter/v1.36.2, google-cloud-go servicedirectory/v1.12.6, google-cloud-go shell/v1.8.6, google-cloud-go spanner/v1.82.0, google-cloud-go speech/v1.27.1, google-cloud-go storage/v1.56.0, google-cloud-go storagetransfer/v1.13.0, google-cloud-go talent/v1.8.3, google-cloud-go texttospeech/v1.13.0, google-cloud-go tpu/v1.8.3, google-cloud-go trace/v1.11.6, google-cloud-go translate/v1.12.5, google-cloud-go v0.121.6, google-cloud-go video/v1.24.0, google-cloud-go videointelligence/v1.12.6, google-cloud-go vision/v2.9.5, google-cloud-go vmmigration/v1.8.6, google-cloud-go vmwareengine/v1.3.5, google-cloud-go vpcaccess/v1.8.6, google-cloud-go webrisk/v1.11.1, google-cloud-go websecurityscanner/v1.7.6, google-cloud-go workflows/v1.14.2, google-gofuzz v1.2.0, google/cel-go v0.26.0, google/gnostic-models v0.7.0, google/go-pkcs11 v0.3.0, google/pprof 20250403-snapshot-27863c87, google/s2a-go v0.1.9, googleapis/enterprise-certificate-proxy v0.3.6, googleapis/go-genproto 20250603-snapshot-513f2392, googleapis/go-genproto 20250818-snapshot-3122310a, googleapis/go-genproto 20251002-snapshot-7c0ddcbb, GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.29.0, GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.53.0, GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.53.0, groupcache 20210331-snapshot-41bb18bf, grpc-ecosystem/go-grpc-middleware providers/prometheus/v1.0.1, grpc-ecosystem/go-grpc-middleware v2.3.0, grpc-ecosystem/go-grpc-prometheus v1.2.0, grpc-go v1.76.0, helm/helm v3.19.0, hpe-storage/dory v1.0.1, hugo-staticsite v0.115.1, hugo-staticsite v0.121.1, hugo-staticsite v0.124.1, hugo-staticsite v0.131.0, inconshreveable/mousetrap v1.1.0, InfluxDB v2.7.10, Istio 1.17.1, Istio 1.22.1, jonboulle-clockwork v0.5.0, jsonreference v0.21.2, k8s.io/code-generator kubernetes-1.18.2-beta.0, k8s.io/code-generator v0.34.1, k8s.io/klog 2.130.1, k8s.io/kube-openapi 20250710-snapshot-f3f2b991, k8s.io/kube-openapi 20250910-snapshot, k8s.io/utils 20251002-snapshot-bc988d57, kops - kubernetes v1.21.0-alpha.2, Kubernetes 0.0.1, Kubernetes 0.1.0, Kubernetes 0.19.0, Kubernetes 0.2, Kubernetes 0.3.0, Kubernetes 0.4.0, Kubernetes 0.4.1, Kubernetes 0.4.2, Kubernetes 0.4.3, Kubernetes 0.5, Kubernetes 0.8.0, Kubernetes 0.8.2, Kubernetes 1.1.0, Kubernetes 1.1.0-rc1, Kubernetes 1.1.2, Kubernetes 1.11.5, Kubernetes 1.13.0-alpha.1, Kubernetes 1.21.6, Kubernetes 1.26.0-alpha.1, Kubernetes 1.26.0-alpha.2, Kubernetes 1.27.2, Kubernetes 1.33.0, Kubernetes 1.5.4, Kubernetes 1.8.0-alpha.3, Kubernetes 1.8.10, Kubernetes 1.8.8, Kubernetes 20200509-snapshot, Kubernetes ccm/v22.0.0, Kubernetes providers/v0.21.0, Kubernetes v0.3.0-rc1, Kubernetes v0.5.0, Kubernetes v0.7.0, Kubernetes v0.8.1, Kubernetes v0.8.3, Kubernetes v0.9.0, Kubernetes v1.10.0-alpha.1, Kubernetes v1.10.0-alpha.2, Kubernetes v1.11.0-beta.1, Kubernetes v1.12.0-beta.1, Kubernetes v1.13.0-beta.1, Kubernetes v1.17.0-alpha.0, Kubernetes v1.2.0-alpha.4, Kubernetes v1.2.0-alpha.6, Kubernetes v1.2.5-rc1, Kubernetes v1.25.0-alpha.2, Kubernetes v1.25.0-alpha.3, Kubernetes v1.26.0-beta.0, Kubernetes v1.28.0-alpha.2, Kubernetes v1.3.0-alpha.3, Kubernetes v1.34.0-rc.1, Kubernetes v1.4.0-alpha.2, Kubernetes v1.7.0-alpha.2, Kubernetes v1.8.0-beta.0, Kubernetes v1.8.0-beta.1, kubernetes-csi/csi-proxy client/v1.3.0, kubernetes-csi/csi-proxy v1.0.0-rc.1, kubernetes-csi/external-snapshotter 20250117-snapshot, kubernetes-csi/external-snapshotter client/v8.2.0, kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.2, kubernetes-sigs/cloud-provider-azure 20250831-snapshot, kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.9.3, kubernetes-sigs/structured-merge-diff 20250925-snapshot, kubernetes-sigs/structured-merge-diff v4.6.0, kubernetes-sigs/structured-merge-diff v6.3.0, kubernetes/api 20250901-snapshot, kubernetes/api v0.34.1, kubernetes/apiextensions-apiserver v0.34.1, kubernetes/apimachinery 20250908-snapshot, kubernetes/apimachinery 20250919-snapshot, kubernetes/apimachinery v0.34.1, kubernetes/apiserver v0.34.1, kubernetes/cloud-provider-azure pkg/azclient/v0.0.48, kubernetes/cloud-provider-azure pkg/azclient/v0.0.49, kubernetes/cloud-provider-azure pkg/azclient/v0.0.8, kubernetes/component-base v0.34.1, kubernetes/kms v0.34.1, kubernetes/mount-utils 20250911-snapshot, kubernetes/mount-utils v0.34.1, martian v3.3.3, mendersoftware/mendertesting 0.0~git20200227.1396c95, moby/sys mountinfo/v0.7.2, modern-go/concurrent 20180305-snapshot-bacd9c7e, modern-go/reflect2 20250322-snapshot-35a7c28c, mongodb/mongo-go-driver 20250809-snapshot, mongodb/mongo-go-driver v1.17.4, mschoch/smat v0.2.0, mwitkow/go-conntrack 20190716-snapshot-2f068394, NetApp/trident v18.04.0, NetApp/trident v20.04.0, NetApp/trident v20.10.0, NetApp/trident v21.01.0, NetApp/trident v21.04.0, NetApp/trident v22.04.0, NetApp/trident v22.07.0, NetApp/trident v23.01.0, NetApp/trident v23.04.0, NetApp/trident v23.07.0, NetApp/trident v24.10.0, NetApp/trident v25.06.1, NYTimes-gziphandler v1.1.1, oklog/ulid v1.3.1, open-telemetry/opentelemetry-go 20250909-snapshot, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.34.0, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.34.0, open-telemetry/opentelemetry-go metric/v1.38.0, open-telemetry/opentelemetry-go sdk/metric/v1.37.0, open-telemetry/opentelemetry-go sdk/v1.38.0, open-telemetry/opentelemetry-go trace/v1.38.0, open-telemetry/opentelemetry-go v1.38.0, open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.36.0, open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.61.0, open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.61.0, open-telemetry/opentelemetry-go-instrumentation sdk/v1.2.1, OpenCensus 0.2.1, opencontainers/go-digest 1.0.0, openshift/api 20251013-snapshot-fe48e8fd, osbuild-osbuild-composer 143, osbuild-osbuild-composer 149, pmezard-go-difflib 20190219-snapshot-5d4384ee, prometheus-client_model v0.6.2, prometheus-common 20250923-snapshot, prometheus-common v0.66.1, prometheus-procfs v0.16.1, RoaringBitmap-roaring v2.0.0, RoaringBitmap-roaring v2.10.0, RoaringBitmap-roaring v2.3.0, RoaringBitmap-roaring v2.3.2, RoaringBitmap-roaring v2.3.4, runc v1.3.0, sigs.k8s.io/json 20241014-snapshot-cfa47c3a, sigs.k8s.io/randfill v1.0.0, sigs.k8s.io/yaml v1.6.0, soheilhy/cmux v0.1.5, spf13-afero 20250922-snapshot, spf13-afero v1.15.0, spf13-cobra 1.10.1, spf13-cobra 20250922-snapshot, spiffe/go-spiffe v2.5.0, strfmt v0.24.0, swag cmdutils/v0.25.1, swag conv/v0.25.1, swag fileutils/v0.25.1, swag jsonname/v0.25.1, swag jsonutils/fixtures_test/v0.25.1, swag jsonutils/v0.25.1, swag loading/v0.25.1, swag mangling/v0.25.1, swag netutils/v0.25.1, swag stringutils/v0.25.1, swag typeutils/v0.25.1, swag v0.25.1, swag yamlutils/v0.25.1, teleport bastion v13.3.0, TensorFlow 20240227-snapshot, VictoriaMetrics v1.112.0, vishvananda-netlink 20200426-snapshot, vishvananda-netlink v1.3.1, vishvananda-netns v0.0.5, vitessio/vitess v0.19.0-rc1, xdg-go/scram v1.1.2, xdg-go/stringprep v1.0.4, yaml for Go 20141213-snapshot-9f9df343, yaml for Go v2.4.0, yaml for Go v3.0.1) - list of conditions and the following disclaimer. +Apache License +Version 2.0, January 2004 +========================= - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +http://www.apache.org/licenses/ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +1. Definitions. -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +"License" shall mean the terms and conditions for use, reproduction, and -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +distribution as defined by Sections 1 through 9 of this document. ---- -BSD 2-clause "Simplified" License -(pkg/errors v0.9.1) +"Licensor" shall mean the copyright owner or entity authorized by the copyright -Copyright (c) 2015, Dave Cheney +owner that is granting the License. -All rights reserved. +"Legal Entity" shall mean the union of the acting entity and all other entities -Redistribution and use in source and binary forms, with or without +that control, are controlled by, or are under common control with that entity. -modification, are permitted provided that the following conditions are met: +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -* Redistributions of source code must retain the above copyright notice, this +outstanding shares, or (iii) beneficial ownership of such entity. - list of conditions and the following disclaimer. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions -* Redistributions in binary form must reproduce the above copyright notice, +granted by this License. - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + +"Source" form shall mean the preferred form for making modifications, including + +but not limited to software source code, documentation source, and configuration + +files. + + + +"Object" form shall mean any form resulting from mechanical transformation or + +translation of a Source form, including but not limited to compiled object code, + +generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, made + +available under the License, as indicated by a copyright notice that is included + +in or attached to the work (an example is provided in the Appendix below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, that is + +based on (or derived from) the Work and for which the editorial revisions, + +annotations, elaborations, or other modifications represent, as a whole, an + +original work of authorship. For the purposes of this License, Derivative Works + +shall not include works that remain separable from, or merely link (or bind by + +name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version + +of the Work and any modifications or additions to that Work or Derivative Works + +thereof, that is intentionally submitted to Licensor for inclusion in the Work by + +the copyright owner or by an individual or Legal Entity authorized to submit on + +behalf of the copyright owner. For the purposes of this definition, "submitted" + +means any form of electronic, verbal, or written communication sent to the + +Licensor or its representatives, including but not limited to communication on + +electronic mailing lists, source code control systems, and issue tracking systems + +that are managed by, or on behalf of, the Licensor for the purpose of discussing + +and improving the Work, but excluding communication that is conspicuously marked + +or otherwise designated in writing by the copyright owner as "Not a + +Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of + +whom a Contribution has been received by Licensor and subsequently incorporated + +within the Work. + + + +2. Grant of Copyright License. Subject to the terms and conditions of this + +License, each Contributor hereby grants to You a perpetual, worldwide, + +non-exclusive, no-charge, royalty-free, irrevocable copyright license to + +reproduce, prepare Derivative Works of, publicly display, publicly perform, + +sublicense, and distribute the Work and such Derivative Works in Source or Object + +form. + + + +3. Grant of Patent License. Subject to the terms and conditions of this License, + +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, + +no-charge, royalty-free, irrevocable (except as stated in this section) patent + +license to make, have made, use, offer to sell, sell, import, and otherwise + +transfer the Work, where such license applies only to those patent claims + +licensable by such Contributor that are necessarily infringed by their + +Contribution(s) alone or by combination of their Contribution(s) with the Work to + +which such Contribution(s) was submitted. If You institute patent litigation + +against any entity (including a cross-claim or counterclaim in a lawsuit) + +alleging that the Work or a Contribution incorporated within the Work constitutes + +direct or contributory patent infringement, then any patent licenses granted to + +You under this License for that Work shall terminate as of the date such + +litigation is filed. + + + +4. Redistribution. You may reproduce and distribute copies of the Work or + +Derivative Works thereof in any medium, with or without modifications, and in + +Source or Object form, provided that You meet the following conditions: + + + + a. You must give any other recipients of the Work or Derivative Works a copy of + + this License; and + + + + b. You must cause any modified files to carry prominent notices stating that + + You changed the files; and + + + + c. You must retain, in the Source form of any Derivative Works that You + + distribute, all copyright, patent, trademark, and attribution notices from + + the Source form of the Work, excluding those notices that do not pertain to + + any part of the Derivative Works; and + + + + d. If the Work includes a "NOTICE" text file as part of its distribution, then + + any Derivative Works that You distribute must include a readable copy of the + + attribution notices contained within such NOTICE file, excluding those + + notices that do not pertain to any part of the Derivative Works, in at least + + one of the following places: within a NOTICE text file distributed as part of + + the Derivative Works; within the Source form or documentation, if provided + + along with the Derivative Works; or, within a display generated by the + + Derivative Works, if and wherever such third-party notices normally appear. + + The contents of the NOTICE file are for informational purposes only and do + + not modify the License. You may add Your own attribution notices within + + Derivative Works that You distribute, alongside or as an addendum to the + + NOTICE text from the Work, provided that such additional attribution notices + + cannot be construed as modifying the License. + + + +You may add Your own copyright statement to Your modifications and may provide + +additional or different license terms and conditions for use, reproduction, or + +distribution of Your modifications, or for any such Derivative Works as a whole, + +provided Your use, reproduction, and distribution of the Work otherwise complies + +with the conditions stated in this License. + + + +5. Submission of Contributions. Unless You explicitly state otherwise, any + +Contribution intentionally submitted for inclusion in the Work by You to the + +Licensor shall be under the terms and conditions of this License, without any + +additional terms or conditions. Notwithstanding the above, nothing herein shall + +supersede or modify the terms of any separate license agreement you may have + +executed with Licensor regarding such Contributions. + + + +6. Trademarks. This License does not grant permission to use the trade names, + +trademarks, service marks, or product names of the Licensor, except as required + +for reasonable and customary use in describing the origin of the Work and + +reproducing the content of the NOTICE file. + + + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in + +writing, Licensor provides the Work (and each Contributor provides its + +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + +either express or implied, including, without limitation, any warranties or + +conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + +PARTICULAR PURPOSE. You are solely responsible for determining the + +appropriateness of using or redistributing the Work and assume any risks + +associated with Your exercise of permissions under this License. + + + +8. Limitation of Liability. In no event and under no legal theory, whether in + +tort (including negligence), contract, or otherwise, unless required by + +applicable law (such as deliberate and grossly negligent acts) or agreed to in + +writing, shall any Contributor be liable to You for damages, including any + +direct, indirect, special, incidental, or consequential damages of any character + +arising as a result of this License or out of the use or inability to use the + +Work (including but not limited to damages for loss of goodwill, work stoppage, + +computer failure or malfunction, or any and all other commercial damages or + +losses), even if such Contributor has been advised of the possibility of such + +damages. + + + +9. Accepting Warranty or Additional Liability. While redistributing the Work or + +Derivative Works thereof, You may choose to offer, and charge a fee for, + +acceptance of support, warranty, indemnity, or other liability obligations and/or + +rights consistent with this License. However, in accepting such obligations, You + +may act only on Your own behalf and on Your sole responsibility, not on behalf of + +any other Contributor, and only if You agree to indemnify, defend, and hold each + +Contributor harmless for any liability incurred by, or claims asserted against, + +such Contributor by reason of your accepting any such warranty or additional + +liability. + + + +END OF TERMS AND CONDITIONS + + + +APPENDIX: How to apply the Apache License to your work + + + +To apply the Apache License to your work, attach the following boilerplate + +notice, with the fields enclosed by brackets "[]" replaced with your own + +identifying information. (Don't include the brackets!) The text should be + +enclosed in the appropriate comment syntax for the file format. We also recommend + +that a file or class name and description of purpose be included on the same + +"printed page" as the copyright notice for easier identification within + +third-party archives. + + + + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, + + Version 2.0 (the "License"); you may not use this file except in compliance + + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law + + or agreed to in writing, software distributed under the License is + + distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + + KIND, either express or implied. See the License for the specific language + + governing permissions and limitations under the License. + +--- + +BSD 2-Clause with views sentence + +(go-plist v1.0.1) + +BSD 2-Clause with views sentence + +================================ + + + +Copyright (c) All rights reserved. + + + +Redistribution and use in source and binary forms, with or without modification, + +are permitted provided that the following conditions are met: + + + + 1. Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + + + + 2. Redistributions in binary form must reproduce the above copyright notice, + + this list of conditions and the following disclaimer in the documentation + + and/or other materials provided with the distribution. + + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +The views and conclusions contained in the software and documentation are those + +of the authors and should not be interpreted as representing official policies, + +either expressed or implied, of the copyright holders or contributors. + +--- + +BSD 2-clause "Simplified" License + +(blackfriday v2.1.0) + +Upstream-Contact: https://github.com/russross/blackfriday/issues/new + + + +Files: * + +Copyright: 2011 Russ Ross + +License: BSD-2-clause + + + +Files: debian/* + +Copyright: 20142015 Tianon Gravi + + 2015 Martina Ferrari + + 20152020 Anthony Fok + + 2016 Dr. Tobias Quathamer + + 2020 Reinhard Tartler + +License: BSD-2-clause + + + +License: BSD-2-clause + + + +Redistribution and use in source and binary forms, with or without + + modification, are permitted provided that the following conditions are met: + + . + + 1. Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + + . + + 2. Redistributions in binary form must reproduce the above copyright notice, + + this list of conditions and the following disclaimer in the documentation + + and/or other materials provided with the distribution. + + . + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +--- + +BSD 2-clause "Simplified" License + +(pkg/errors v0.9.1) + +Copyright (c) 2015, Dave Cheney + +All rights reserved. + + + +Redistribution and use in source and binary forms, with or without + +modification, are permitted provided that the following conditions are met: + + + +* Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + + + +* Redistributions in binary form must reproduce the above copyright notice, + + this list of conditions and the following disclaimer in the documentation + + and/or other materials provided with the distribution. + + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +--- + +BSD 2-clause "Simplified" License + +(containerd/containerd v2.0.5, containerd/containerd v2.1.3, dnaeon/go-vcr v3.2.0, go-check-check 20201130-snapshot-10cb9826, gorilla/websocket 20250226-snapshot-e064f32e, maxatome/go-testdeep 1.14.0, pkg/browser 20240102-snapshot-5ac0b6a4) + +BSD Two Clause License + +====================== + + + +Redistribution and use in source and binary forms, with or without modification, + +are permitted provided that the following conditions are met: + + + + 1. Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + + + + 2. Redistributions in binary form must reproduce the above copyright notice, + + this list of conditions and the following disclaimer in the documentation + + and/or other materials provided with the distribution. + + + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + +SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + +DAMAGE. + +--- + +BSD 2-clause "Simplified" License + +(dnaeon/go-vcr v1.2.0) + +Copyright (c) 2015-2016 Marin Atanasov Nikolov + +All rights reserved. + + + +Redistribution and use in source and binary forms, with or without + +modification, are permitted provided that the following conditions + +are met: + + + + 1. Redistributions of source code must retain the above copyright + + notice, this list of conditions and the following disclaimer + + in this position and unchanged. + + 2. Redistributions in binary form must reproduce the above copyright + + notice, this list of conditions and the following disclaimer in the + + documentation and/or other materials provided with the distribution. + + + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + +IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +--- + +BSD 3-clause "New" or "Revised" License + +(kisielk-gotool v1.0.0) + +Copyright (c) 2009 The Go Authors. All rights reserved. + + + + + + + +Redistribution and use in source and binary forms, with or without + + modification, are permitted provided that the following conditions are + + met: + + + + * Redistributions of source code must retain the above copyright + + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + + copyright notice, this list of conditions and the following disclaimer + + in the documentation and/or other materials provided with the + + distribution. + + * Neither the name of Google Inc. nor the names of its + + contributors may be used to endorse or promote products derived from + + this software without specific prior written permission. + + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +--- + +BSD 3-clause "New" or "Revised" License + +(go-inf-inf v0.9.1) + +Copyright (c) 2012 Pter Surnyi. Portions Copyright (c) 2009 The Go + +Authors. All rights reserved. + + + +Redistribution and use in source and binary forms, with or without + +modification, are permitted provided that the following conditions are + +met: + + + + * Redistributions of source code must retain the above copyright + +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + +copyright notice, this list of conditions and the following disclaimer + +in the documentation and/or other materials provided with the + +distribution. + + * Neither the name of Google Inc. nor the names of its + +contributors may be used to endorse or promote products derived from + +this software without specific prior written permission. + + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +--- + +BSD 3-clause "New" or "Revised" License + +(julienschmidt/httprouter v1.3.0) + +BSD 3-Clause License + + + +Copyright (c) 2013, Julien Schmidt + +All rights reserved. + + + +Redistribution and use in source and binary forms, with or without + +modification, are permitted provided that the following conditions are met: + + + +1. Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + + + +2. Redistributions in binary form must reproduce the above copyright notice, + + this list of conditions and the following disclaimer in the documentation + + and/or other materials provided with the distribution. + + + +3. Neither the name of the copyright holder nor the names of its + + contributors may be used to endorse or promote products derived from + + this software without specific prior written permission. @@ -1971,187 +2673,269 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- -BSD 2-clause "Simplified" License +BSD 3-clause "New" or "Revised" License -(containerd/containerd v2.0.5, containerd/containerd v2.1.0, dnaeon/go-vcr v3.2.0, github.com/redis/go-redis v9.7.0, go-check-check 20201130-snapshot-10cb9826, inspektor-gadget/inspektor-gadget v0.39.0, maxatome/go-testdeep 1.14.0, pkg/browser 20240102-snapshot-5ac0b6a4) +(go-flowrate 20140419-snapshot-cca7078d) -BSD Two Clause License +Copyright (c) 2014 The Go-FlowRate Authors. All rights reserved. -====================== +Redistribution and use in source and binary forms, with or without -Redistribution and use in source and binary forms, with or without modification, +modification, are permitted provided that the following conditions are -are permitted provided that the following conditions are met: +met: - 1. Redistributions of source code must retain the above copyright notice, this + * Redistributions of source code must retain the above copyright - list of conditions and the following disclaimer. + notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, + * Redistributions in binary form must reproduce the above copyright - this list of conditions and the following disclaimer in the documentation + notice, this list of conditions and the following disclaimer in the - and/or other materials provided with the distribution. + documentation and/or other materials provided with the + distribution. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * Neither the name of the go-flowrate project nor the names of its -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + contributors may be used to endorse or promote products derived -SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + from this software without specific prior written permission. -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DAMAGE. +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- -BSD 2-clause "Simplified" License +BSD 3-clause "New" or "Revised" License -(dnaeon/go-vcr v1.2.0) +(gogo/protobuf v1.3.2) -Copyright (c) 2015-2016 Marin Atanasov Nikolov +Copyright 2010 The Go Authors. All rights reserved. -All rights reserved. +https://github.com/golang/protobuf Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions +modification, are permitted provided that the following conditions are -are met: +met: - 1. Redistributions of source code must retain the above copyright + * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer +notice, this list of conditions and the following disclaimer. - in this position and unchanged. + * Redistributions in binary form must reproduce the above - 2. Redistributions in binary form must reproduce the above copyright +copyright notice, this list of conditions and the following disclaimer - notice, this list of conditions and the following disclaimer in the +in the documentation and/or other materials provided with the - documentation and/or other materials provided with the distribution. +distribution. + + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- BSD 3-clause "New" or "Revised" License -(kisielk-gotool v1.0.0) +(client9/misspell v0.3.4) -Copyright (c) 2009 The Go Authors. All rights reserved. +which are covered under a BSD License. +* https://golang.org/pkg/strings/#Replacer + +* https://golang.org/src/strings/replace.go + +* https://github.com/golang/go/blob/master/LICENSE + +Copyright (c) 2009 The Go Authors. All rights reserved. + Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are +modification, are permitted provided that the following conditions are - met: +met: - * Redistributions of source code must retain the above copyright + * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above + * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer +copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the +in the documentation and/or other materials provided with the - distribution. +distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from +contributors may be used to endorse or promote products derived from - this software without specific prior written permission. +this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- BSD 3-clause "New" or "Revised" License -(go-inf-inf v0.9.1) +(evanphx/json-patch v4.12.0, evanphx/json-patch v5.6.0) -Copyright (c) 2012 Pter Surnyi. Portions Copyright (c) 2009 The Go +Copyright (c) 2014, Evan Phoenix -Authors. All rights reserved. +All rights reserved. + + + +Redistribution and use in source and binary forms, with or without + +modification, are permitted provided that the following conditions are met: + + + +* Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + + this list of conditions and the following disclaimer in the documentation + + and/or other materials provided with the distribution. + +* Neither the name of the Evan Phoenix nor the names of its contributors + + may be used to endorse or promote products derived from this software + + without specific prior written permission. + + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + +--- + +BSD 3-clause "New" or "Revised" License + +(golang-snappy-go-dev v0.0.4) + +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. @@ -2203,79 +2987,75 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- -BSD 3-clause "New" or "Revised" License - -(julienschmidt/httprouter v1.3.0) - -BSD 3-Clause License - - +BSD 3-clause "New" or "Revised" License -Copyright (c) 2013, Julien Schmidt +(Go programming language 0) -All rights reserved. +Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - +modification, are permitted provided that the following conditions are +met: -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, + * Redistributions in binary form must reproduce the above - this list of conditions and the following disclaimer in the documentation +copyright notice, this list of conditions and the following disclaimer - and/or other materials provided with the distribution. +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its -3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from - contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- BSD 3-clause "New" or "Revised" License -(grpc-gateway v1.16.0) +(bits-and-blooms/bitset 20250923-snapshot, bits-and-blooms/bitset v1.20.0, containerd/containerd v2.0.5, containerd/containerd v2.1.3, evanphx/json-patch v5.9.11, exp 20250718-snapshot-645b1fa8, fsnotify-fsnotify v1.9.0, github.com/antlr4-go/antlr v4.13.1, github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61, github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b, Go programming language 1.23rc1, Go programming language 20170806-snapshot, Go programming language go1.20rc1, go-flags v1.6.1, go-plist v1.0.1, Golang Protobuf 20250505-snapshot, Golang Protobuf v1.36.10, Golang Protobuf v1.5.4, golang-github-googleapis-gax-go-dev 2.13.0, golang-github-spf13-pflag-dev 20250906-snapshot, golang-github-spf13-pflag-dev v1.0.10, golang.org/x/crypto v0.43.0, golang.org/x/lint 20190308-snapshot-d0100b6b, golang.org/x/mod v0.28.0, golang.org/x/net v0.46.0, golang.org/x/oauth2 v0.32.0, golang.org/x/sys 20250923-snapshot, golang.org/x/sys v0.37.0, golang.org/x/term v0.36.0, golang.org/x/time v0.14.0, golang.org/x/tools 20250916-snapshot, golang.org/x/tools v0.37.0, golang.org/x/xerrors 20200804-snapshot-5ec99f83, golang/sync v0.17.0, golang/text 20240806-snapshot, golang/text v0.30.0, golang/text v0.5.0, Gonum numerical packages v0.16.0, google/go-cmp v0.7.0, googleapis/gax-go v2.15.0, googleapis/google-api-go-client 20250917-snapshot, googleapis/google-api-go-client v0.252.0, Googleuuid v1.6.0, gorilla/mux v1.8.1, grpc-gateway v2.26.3, klauspost-compress v1.18.0, rogpeppe/go-internal v1.14.1, xhit/go-str2duration v2.1.0) -Copyright (c) 2015, Gengo, Inc. +Copyright (c) , All rights reserved. @@ -2287,25 +3067,27 @@ are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, + * Redistributions of source code must retain the above copyright notice, this + + list of conditions and the following disclaimer. + - this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation - * Redistributions in binary form must reproduce the above copyright notice, + and/or other materials provided with the distribution. - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + * Neither the name of the nor the names of its contributors may + be used to endorse or promote products derived from this software without - * Neither the name of Gengo, Inc. nor the names of its + specific prior written permission. - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. @@ -2319,449 +3101,447 @@ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- -BSD 3-clause "New" or "Revised" License +Educational Community License v2.0 -(go-flowrate 20140419-snapshot-cca7078d) +(docker-org v0.1) -Copyright (c) 2014 The Go-FlowRate Authors. All rights reserved. +Educational Community License +Version 2.0, April 2007 +============================= -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +http://www.osedu.org/licenses/ - * Redistributions of source code must retain the above copyright +The Educational Community License version 2.0 ("ECL") consists of the Apache 2.0 - notice, this list of conditions and the following disclaimer. +license, modified to change the scope of the patent grant in section 3 to be +specific to the needs of the education communities using this license. The +original Apache 2.0 license can be found at: - * Redistributions in binary form must reproduce the above copyright +http://www.apache.org/licenses/LICENSE-2.0 - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - * Neither the name of the go-flowrate project nor the names of its +1. Definitions. - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +"Licensor" shall mean the copyright owner or entity authorized by the copyright -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +owner that is granting the License. -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +"Legal Entity" shall mean the union of the acting entity and all other entities -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +that control, are controlled by, or are under common control with that entity. -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +For the purposes of this definition, "control" means -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE ---- + i. the power, direct or indirect, to cause the direction or management of such -BSD 3-clause "New" or "Revised" License + entity, whether by contract or otherwise, or -(gogo/protobuf v1.3.2) -Copyright 2010 The Go Authors. All rights reserved. -https://github.com/golang/protobuf + ii. ownership of fifty percent (50%) or more of the outstanding shares, or -Redistribution and use in source and binary forms, with or without + iii. beneficial ownership of such entity. -modification, are permitted provided that the following conditions are -met: +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above +"Source" form shall mean the preferred form for making modifications, including -copyright notice, this list of conditions and the following disclaimer +but not limited to software source code, documentation source, and configuration -in the documentation and/or other materials provided with the +files. -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from +"Object" form shall mean any form resulting from mechanical transformation or -this software without specific prior written permission. +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +"Work" shall mean the work of authorship, whether in Source or Object form, made -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +available under the License, as indicated by a copyright notice that is included -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +in or attached to the work (an example is provided in the Appendix below). -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +"Derivative Works" shall mean any work, whether in Source or Object form, that is -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +based on (or derived from) the Work and for which the editorial revisions, -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +annotations, elaborations, or other modifications represent, as a whole, an -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +original work of authorship. For the purposes of this License, Derivative Works -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +shall not include works that remain separable from, or merely link (or bind by ---- +name) to the interfaces of, the Work and Derivative Works thereof. -BSD 3-clause "New" or "Revised" License -(client9/misspell v0.3.4) -which are covered under a BSD License. +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work by -* https://golang.org/pkg/strings/#Replacer +the copyright owner or by an individual or Legal Entity authorized to submit on -* https://golang.org/src/strings/replace.go +behalf of the copyright owner. For the purposes of this definition, "submitted" -* https://github.com/golang/go/blob/master/LICENSE +means any form of electronic, verbal, or written communication sent to the +Licensor or its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue tracking systems -Copyright (c) 2009 The Go Authors. All rights reserved. +that are managed by, or on behalf of, the Licensor for the purpose of discussing +and improving the Work, but excluding communication that is conspicuously marked +or otherwise designated in writing by the copyright owner as "Not a -Redistribution and use in source and binary forms, with or without +Contribution." -modification, are permitted provided that the following conditions are -met: +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of +whom a Contribution has been received by Licensor and subsequently incorporated - * Redistributions of source code must retain the above copyright +within the Work. -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer +2. Grant of Copyright License. -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its +Subject to the terms and conditions of this License, each Contributor hereby -contributors may be used to endorse or promote products derived from +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -this software without specific prior written permission. +irrevocable copyright license to reproduce, prepare Derivative Works of, publicly +display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +3. Grant of Patent License. -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +Subject to the terms and conditions of this License, each Contributor hereby -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +irrevocable (except as stated in this section) patent license to make, have made, -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +use, offer to sell, sell, import, and otherwise transfer the Work, where such -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +license applies only to those patent claims licensable by such Contributor that -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +are necessarily infringed by their Contribution(s) alone or by combination of ---- +their Contribution(s) with the Work to which such Contribution(s) was submitted. -BSD 3-clause "New" or "Revised" License +If You institute patent litigation against any entity (including a cross-claim or -(evanphx/json-patch v4.12.0, evanphx/json-patch v5.6.0) +counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated -Copyright (c) 2014, Evan Phoenix +within the Work constitutes direct or contributory patent infringement, then any -All rights reserved. +patent licenses granted to You under this License for that Work shall terminate +as of the date such litigation is filed. Any patent license granted hereby with +respect to contributions by an individual employed by an institution or -Redistribution and use in source and binary forms, with or without +organization is limited to patent claims where the individual that is the author -modification, are permitted provided that the following conditions are met: +of the Work is also the inventor of the patent claims licensed, and where the +organization or institution has the right to grant such license under applicable +grant and research funding agreements. No other express or implied licenses are -* Redistributions of source code must retain the above copyright notice, this +granted. - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation +4. Redistribution. - and/or other materials provided with the distribution. -* Neither the name of the Evan Phoenix nor the names of its contributors - may be used to endorse or promote products derived from this software +You may reproduce and distribute copies of the Work or Derivative Works thereof - without specific prior written permission. +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + 1. You must give any other recipients of the Work or Derivative Works a copy of -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + this License; and -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + 2. You must cause any modified files to carry prominent notices stating that -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + You changed the files; and -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + 3. You must retain, in the Source form of any Derivative Works that You ---- + distribute, all copyright, patent, trademark, and attribution notices from -BSD 3-clause "New" or "Revised" License + the Source form of the Work, excluding those notices that do not pertain to -(golang-snappy-go-dev v0.0.4) + any part of the Derivative Works; and -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + 4. If the Work includes a "NOTICE" text file as part of its distribution, then -Redistribution and use in source and binary forms, with or without + any Derivative Works that You distribute must include a readable copy of the -modification, are permitted provided that the following conditions are + attribution notices contained within such NOTICE file, excluding those -met: + notices that do not pertain to any part of the Derivative Works, in at least + one of the following places: within a NOTICE text file distributed as part of + the Derivative Works; within the Source form or documentation, if provided - * Redistributions of source code must retain the above copyright + along with the Derivative Works; or, within a display generated by the -notice, this list of conditions and the following disclaimer. + Derivative Works, if and wherever such third-party notices normally appear. - * Redistributions in binary form must reproduce the above + The contents of the NOTICE file are for informational purposes only and do -copyright notice, this list of conditions and the following disclaimer + not modify the License. You may add Your own attribution notices within -in the documentation and/or other materials provided with the + Derivative Works that You distribute, alongside or as an addendum to the -distribution. + NOTICE text from the Work, provided that such additional attribution notices - * Neither the name of Google Inc. nor the names of its + cannot be construed as modifying the License. -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +distribution of Your modifications, or for any such Derivative Works as a whole, + +provided Your use, reproduction, and distribution of the Work otherwise complies + +with the conditions stated in this License. + + + +5. Submission of Contributions. + + + +Unless You explicitly state otherwise, any Contribution intentionally submitted + +for inclusion in the Work by You to the Licensor shall be under the terms and + +conditions of this License, without any additional terms or conditions. + +Notwithstanding the above, nothing herein shall supersede or modify the terms of + +any separate license agreement you may have executed with Licensor regarding such + +Contributions. -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +6. Trademarks. -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +This License does not grant permission to use the trade names, trademarks, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +service marks, or product names of the Licensor, except as required for -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +reasonable and customary use in describing the origin of the Work and reproducing -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +the content of the NOTICE file. -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE ---- -BSD 3-clause "New" or "Revised" License +7. Disclaimer of Warranty. -(bits-and-blooms/bitset 20241228-snapshot, bits-and-blooms/bitset v1.20.0, containerd/containerd v2.0.5, containerd/containerd v2.1.0, evanphx/json-patch v5.9.11, exp 20240711-snapshot-8a7402ab, fsnotify-fsnotify v1.7.0, github.com/antlr4-go/antlr v4.13.0, github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61, github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b, go-flags v1.6.1, go-plist v1.0.1, Golang Protobuf v1.36.6, Golang Protobuf v1.5.4, golang-github-googleapis-gax-go-dev 2.13.0, golang-github-spf13-pflag-dev v1.0.6, golang.org/x/crypto v0.39.0, golang.org/x/lint 20190308-snapshot-d0100b6b, golang.org/x/mod v0.25.0, golang.org/x/net 20250404-snapshot, golang.org/x/net 20250607-snapshot, golang.org/x/net v0.41.0, golang.org/x/oauth2 20250510-snapshot, golang.org/x/oauth2 v0.30.0, golang.org/x/sys 20250608-snapshot, golang.org/x/sys v0.33.0, golang.org/x/term v0.32.0, golang.org/x/time v0.11.0, golang.org/x/tools 20250611-snapshot, golang.org/x/tools v0.33.0, golang.org/x/xerrors 20200804-snapshot-5ec99f83, golang/sync 20250607-snapshot, golang/sync v0.15.0, golang/telemetry 20240517-snapshot-bda55230, golang/text 20240806-snapshot, golang/text v0.26.0, golang/tools v0.33.0, google/go-cmp v0.7.0, googleapis/gax-go 20250602-snapshot, googleapis/gax-go v2.14.2, googleapis/google-api-go-client 20250505-snapshot, googleapis/google-api-go-client 20250603-snapshot, googleapis/google-api-go-client v0.234.0, Googleuuid v1.6.0, gorilla/mux v1.8.1, grpc-gateway v2.20.0, inspektor-gadget/inspektor-gadget v0.39.0, klauspost-compress v1.18.0, rogpeppe/go-internal v1.13.1, xhit/go-str2duration v2.1.0) -Copyright (c) , -All rights reserved. +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -Redistribution and use in source and binary forms, with or without modification, +including, without limitation, any warranties or conditions of TITLE, -are permitted provided that the following conditions are met: +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or redistributing +the Work and assume any risks associated with Your exercise of permissions under - * Redistributions of source code must retain the above copyright notice, this +this License. - list of conditions and the following disclaimer. +8. Limitation of Liability. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate and +grossly negligent acts) or agreed to in writing, shall any Contributor be liable - * Neither the name of the nor the names of its contributors may +to You for damages, including any direct, indirect, special, incidental, or - be used to endorse or promote products derived from this software without +consequential damages of any character arising as a result of this License or out - specific prior written permission. +of the use or inability to use the Work (including but not limited to damages for +loss of goodwill, work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor has been advised of +the possibility of such damages. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +9. Accepting Warranty or Additional Liability. -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +While redistributing the Work or Derivative Works thereof, You may choose to -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +offer, and charge a fee for, acceptance of support, warranty, indemnity, or other -OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +liability obligations and/or rights consistent with this License. However, in -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +accepting such obligations, You may act only on Your own behalf and on Your sole -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +responsibility, not on behalf of any other Contributor, and only if You agree to -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +indemnify, defend, and hold each Contributor harmless for any liability incurred ---- +by, or claims asserted against, such Contributor by reason of your accepting any -Expat License +such warranty or additional liability. -(fxamacker/cbor 2.7.0) -Expat License -============= +END OF TERMS AND CONDITIONS -Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd +APPENDIX: How to apply the Educational Community License to your work - and Clark Cooper -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. +To apply the Educational Community License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" replaced with your -Permission is hereby granted, free of charge, to any person obtaining a copy of +own identifying information. (Don't include the brackets!) The text should be -this software and associated documentation files (the "Software"), to deal in the +enclosed in the appropriate comment syntax for the file format. We also recommend -Software without restriction, including without limitation the rights to use, +that a file or class name and description of purpose be included on the same -copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +"printed page" as the copyright notice for easier identification within -Software, and to permit persons to whom the Software is furnished to do so, +third-party archives. -subject to the following conditions: + Copyright [yyyy] [name of copyright owner] -The above copyright notice and this permission notice shall be included in all + Licensed under the Educational Community License, Version 2.0 (the "License"); -copies or substantial portions of the Software. + you may not use this file except in compliance with the License. You may obtain + a copy of the License at + http://www.osedu.org/licenses/ECL-2.0 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + Unless required by applicable law or agreed to in writing, software distributed -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + CONDITIONS OF ANY KIND, either express or implied. See the License for the -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + specific language governing permissions and limitations under the License. --- GNU Affero General Public License v3.0 -(Grafana 10.2.6) +(Grafana 9.0.2, Grafana 9.3.6, teleport bastion v14.3.0) GNU AFFERO GENERAL PUBLIC LICENSE @@ -3977,63 +4757,79 @@ any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see -. +. --- -GNU General Public License v2.0 with Linux Syscall Note +GNU General Public License v3.0 w/GCC Runtime Library exception + +(libstdc++ 12-20220222) + +GCC RUNTIME LIBRARY EXCEPTION + +============================= + + + +Version 3.1, 31 March 2009 + + + +Copyright 2009 Free Software Foundation, Inc. + + + +Everyone is permitted to copy and distribute verbatim copies of this license -(inspektor-gadget/inspektor-gadget v0.39.0) +document, but changing it is not allowed. -The GNU General Public License v2.0 w/Linux-syscall-note -======================================================== +This GCC Runtime Library Exception ("Exception") is an additional permission +under section 7 of the GNU General Public License, version 3 ("GPLv3"). It -NOTE! This copyright does *not* cover user programs that use kernel services by +applies to a given file (the "Runtime Library") that bears a notice placed by the -normal system calls - this is merely considered normal use of the kernel, and +copyright holder of the file stating that the file is governed by GPLv3 along -does *not* fall under the heading of "derived work". Also note that the GPL below +with this Exception. -is copyrighted by the Free Software Foundation, but the instance of code that it -refers to (the Linux kernel) is copyrighted by me and others who actually wrote -it. +When you use GCC to compile a program, GCC may combine portions of certain GCC +header files and runtime libraries with the compiled program. The purpose of this +Exception is to allow compilation of non-GPL (including proprietary) programs to -Also note that the only valid version of the GPL as far as the kernel is +use, in this way, the header files and runtime libraries covered by this -concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or +Exception. -whatever), unless explicitly otherwise stated. -Linus Torvalds +GNU GENERAL PUBLIC LICENSE +-------------------------- -Version 2, June 1991 +Version 3,29 June 2007 --------------------- +Copyright (C) 2007 Free Software Foundation, Inc. -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. @@ -4041,21 +4837,25 @@ Preamble -The licenses for most software are designed to take away your freedom to share +The GNU General Public License is a free, copyleft license for software and other + +kinds of works. + + -and change it. By contrast, the GNU General Public License is intended to +The licenses for most software and other practical works are designed to take -guarantee your freedom to share and change free software--to make sure the +away your freedom to share and change the works. By contrast, the GNU General -software is free for all its users. This General Public License applies to most +Public License is intended to guarantee your freedom to share and change all -of the Free Software Foundation's software and to any other program whose authors +versions of a program--to make sure it remains free software for all its users. -commit to using it. (Some other Free Software Foundation software is covered by +We, the Free Software Foundation, use the GNU General Public License for most of -the GNU Library General Public License instead.) You can apply it to your +our software; it applies also to any other work released this way by its authors. -programs, too. +You can apply it to your programs, too. @@ -4063,65 +4863,85 @@ When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to -distribute copies of free software (and charge for this service if you wish), +distribute copies of free software (and charge for them if you wish), that you -that you receive source code or can get it if you want it, that you can change +receive source code or can get it if you want it, that you can change the -the software or use pieces of it in new free programs; and that you know you can +software or use pieces of it in new free programs, and that you know you can do -do these things. +these things. -To protect your rights, we need to make restrictions that forbid anyone to deny +To protect your rights, we need to prevent others from denying you these rights -you these rights or to ask you to surrender the rights. These restrictions +or asking you to surrender the rights. Therefore, you have certain -translate to certain responsibilities for you if you distribute copies of the +responsibilities if you distribute copies of the software, or if you modify it: -software, or if you modify it. +responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a -fee, you must give the recipients all the rights that you have. You must make +fee, you must pass on to the recipients the same freedoms that you received. You + +must make sure that they, too, receive or can get the source code. And you must + +show them these terms so they know their rights. + -sure that they, too, receive or can get the source code. And you must show them -these terms so they know their rights. +Developers that use the GNU GPL protect your rights with two steps: (1) assert +copyright on the software, and (2) offer you this License giving you legal +permission to copy, distribute and/or modify it. -We protect your rights with two steps: (1) copyright the software, and (2) offer -you this license which gives you legal permission to copy, distribute and/or -modify the software. +For the developers' and authors' protection, the GPL clearly explains that there +is no warranty for this free software. For both users' and authors' sake, the GPL +requires that modified versions be marked as changed, so that their problems will -Also, for each author's protection and ours, we want to make certain that +not be attributed erroneously to authors of previous versions. -everyone understands that there is no warranty for this free software. If the -software is modified by someone else and passed on, we want its recipients to -know that what they have is not the original, so that any problems introduced by +Some devices are designed to deny users access to install or run modified -others will not reflect on the original authors' reputations. +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to change +the software. The systematic pattern of such abuse occurs in the area of products -Finally, any free program is threatened constantly by software patents. We wish +for individuals to use, which is precisely where it is most unacceptable. -to avoid the danger that redistributors of a free program will individually +Therefore, we have designed this version of the GPL to prohibit the practice for -obtain patent licenses, in effect making the program proprietary. To prevent +those products. If such problems arise substantially in other domains, we stand -this, we have made it clear that any patent must be licensed for everyone's free +ready to extend this provision to those domains in future versions of the GPL, as -use or not licensed at all. +needed to protect the freedom of users. + + + +Finally, every program is threatened constantly by software patents. States + +should not allow patents to restrict development and use of software on + +general-purpose computers, but in those that do, we wish to avoid the special + +danger that patents applied to a free program could make it effectively + +proprietary. To prevent this, the GPL assures that patents cannot be used to + +render the program non-free. @@ -4131,1273 +4951,1285 @@ follow. -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +TERMS AND CONDITIONS + + + +0. Definitions. + + + +This License refers to version 3 of the GNU General Public License. + + + +Copyright also means copyright-like laws that apply to other kinds of works, + +such as semiconductor masks. + + + +The Program refers to any copyrightable work licensed under this License. Each + +licensee is addressed as you. Licensees and recipients may be individuals + +or organizations. + + + +To modify a work means to copy from or adapt all or part of the work in a + +fashion requiring copyright permission, other than the making of an exact copy. + +The resulting work is called a modified version of the earlier work or a work + +based on the earlier work. + + + +A covered work means either the unmodified Program or a work based on the + +Program. + + + +To propagate a work means to do anything with it that, without permission, + +would make you directly or secondarily liable for infringement under applicable + +copyright law, except executing it on a computer or modifying a private copy. + +Propagation includes copying, distribution (with or without modification), making -0. This License applies to any program or other work which contains a notice +available to the public, and in some countries other activities as well. -placed by the copyright holder saying it may be distributed under the terms of -this General Public License. The "Program", below, refers to any such program or -work, and a "work based on the Program" means either the Program or any +To convey a work means any kind of propagation that enables other parties to -derivative work under copyright law: that is to say, a work containing the +make or receive copies. Mere interaction with a user through a computer network, -Program or a portion of it, either verbatim or with modifications and/or +with no transfer of a copy, is not conveying. -translated into another language. (Hereinafter, translation is included without -limitation in the term "modification".) Each licensee is addressed as "you". +An interactive user interface displays Appropriate Legal Notices to the extent +that it includes a convenient and prominently visible feature that (1) displays -Activities other than copying, distribution and modification are not covered by +an appropriate copyright notice, and (2) tells the user that there is no warranty -this License; they are outside its scope. The act of running the Program is not +for the work (except to the extent that warranties are provided), that licensees -restricted, and the output from the Program is covered only if its contents +may convey the work under this License, and how to view a copy of this License. -constitute a work based on the Program (independent of having been made by +If the interface presents a list of user commands or options, such as a menu, a -running the Program). Whether that is true depends on what the Program does. +prominent item in the list meets this criterion. -1. You may copy and distribute verbatim copies of the Program's source code as +1. Source Code. -you receive it, in any medium, provided that you conspicuously and appropriately -publish on each copy an appropriate copyright notice and disclaimer of warranty; -keep intact all the notices that refer to this License and to the absence of any +The source code for a work means the preferred form of the work for making -warranty; and give any other recipients of the Program a copy of this License +modifications to it. Object code means any non-source form of a work. -along with the Program. +A Standard Interface means an interface that either is an official standard -You may charge a fee for the physical act of transferring a copy, and you may at +defined by a recognized standards body, or, in the case of interfaces specified -your option offer warranty protection in exchange for a fee. +for a particular programming language, one that is widely used among developers +working in that language. -2. You may modify your copy or copies of the Program or any portion of it, thus -forming a work based on the Program, and copy and distribute such modifications +The System Libraries of an executable work include anything, other than the -or work under the terms of Section 1 above, provided that you also meet all of +work as a whole, that (a) is included in the normal form of packaging a Major -these conditions: +Component, but which is not part of that Major Component, and (b) serves only to +enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code - a) You must cause the modified files to carry prominent notices stating +form. A Major Component, in this context, means a major essential component - that you changed the files and the date of any change. +(kernel, window system, and so on) of the specific operating system (if any) on +which the executable work runs, or a compiler used to produce the work, or an +object code interpreter used to run it. - b) You must cause any work that you distribute or publish, that in whole or - in part contains or is derived from the Program or any part thereof, to be - licensed as a whole at no charge to all third parties under the terms of +The Corresponding Source for a work in object code form means all the source - this License. +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose - c) If the modified program normally reads commands interactively when run, +tools or generally available free programs which are used unmodified in - you must cause it, when started running for such interactive use in the +performing those activities but which are not part of the work. For example, - most ordinary way, to print or display an announcement including an +Corresponding Source includes interface definition files associated with source - appropriate copyright notice and a notice that there is no warranty (or +files for the work, and the source code for shared libraries and dynamically - else, saying that you provide a warranty) and that users may redistribute +linked subprograms that the work is specifically designed to require, such as by - the program under these conditions, and telling the user how to view a copy +intimate data communication or control flow between those subprograms and other - of this License. (Exception: if the Program itself is interactive but does +parts of the work. - not normally print such an announcement, your work based on the Program is - not required to print an announcement.) +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. -These requirements apply to the modified work as a whole. If identifiable -sections of that work are not derived from the Program, and can be reasonably -considered independent and separate works in themselves, then this License, and +The Corresponding Source for a work in source code form is that same work. -its terms, do not apply to those sections when you distribute them as separate -works. But when you distribute the same sections as part of a whole which is a -work based on the Program, the distribution of the whole must be on the terms of +2. Basic Permissions. -this License, whose permissions for other licensees extend to the entire whole, -and thus to each and every part regardless of who wrote it. +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This -Thus, it is not the intent of this section to claim rights or contest your rights +License explicitly affirms your unlimited permission to run the unmodified -to work written entirely by you; rather, the intent is to exercise the right to +Program. The output from running a covered work is covered by this License only -control the distribution of derivative or collective works based on the Program. +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. -In addition, mere aggregation of another work not based on the Program with the -Program (or with a work based on the Program) on a volume of a storage or -distribution medium does not bring the other work under the scope of this +You may make, run and propagate covered works that you do not convey, without -License. +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, -3. You may copy and distribute the Program (or a work based on it, under Section +provided that you comply with the terms of this License in conveying all material -2) in object code or executable form under the terms of Sections 1 and 2 above +for which you do not control copyright. Those thus making or running the covered -provided that you also do one of the following: +works for you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your copyrighted +material outside their relationship with you. - a) Accompany it with the complete corresponding machine-readable source - code, which must be distributed under the terms of Sections 1 and 2 above - on a medium customarily used for software interchange; or, +Conveying under any other circumstances is permitted solely under the conditions +stated below. Sublicensing is not allowed; section 10 makes it unnecessary. - b) Accompany it with a written offer, valid for at least three years, to - give any third party, for a charge no more than your cost of physically +3. Protecting Users' Legal Rights From Anti-Circumvention Law. - performing source distribution, a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of Sections 1 - and 2 above on a medium customarily used for software interchange; or, +No covered work shall be deemed part of an effective technological measure under +any applicable law fulfilling obligations under article 11 of the WIPO copyright +treaty adopted on 20 December 1996, or similar laws prohibiting or restricting - c) Accompany it with the information you received as to the offer to +circumvention of such measures. - distribute corresponding source code. (This alternative is allowed only for - noncommercial distribution and only if you received the program in object - code or executable form with such an offer, in accord with Subsection b +When you convey a covered work, you waive any legal power to forbid circumvention - above.) +of technological measures to the extent such circumvention is effected by +exercising rights under this License with respect to the covered work, and you +disclaim any intention to limit operation or modification of the work as a means -The source code for a work means the preferred form of the work for making +of enforcing, against the work's users, your or third parties' legal rights to -modifications to it. For an executable work, complete source code means all the +forbid circumvention of technological measures. -source code for all modules it contains, plus any associated interface definition -files, plus the scripts used to control compilation and installation of the -executable. However, as a special exception, the source code distributed need not +4. Conveying Verbatim Copies. -include anything that is normally distributed (in either source or binary form) -with the major components (compiler, kernel, and so on) of the operating system -on which the executable runs, unless that component itself accompanies the +You may convey verbatim copies of the Program's source code as you receive it, in -executable. +any medium, provided that you conspicuously and appropriately publish on each +copy an appropriate copyright notice; keep intact all notices stating that this +License and any non-permissive terms added in accord with section 7 apply to the -If distribution of executable or object code is made by offering access to copy +code; keep intact all notices of the absence of any warranty; and give all -from a designated place, then offering equivalent access to copy the source code +recipients a copy of this License along with the Program. -from the same place counts as distribution of the source code, even though third -parties are not compelled to copy the source along with the object code. +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. -4. You may not copy, modify, sublicense, or distribute the Program except as -expressly provided under this License. Any attempt otherwise to copy, modify, -sublicense or distribute the Program is void, and will automatically terminate +5. Conveying Modified Source Versions. -your rights under this License. However, parties who have received copies, or -rights, from you under this License will not have their licenses terminated so -long as such parties remain in full compliance. +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: -5. You are not required to accept this License, since you have not signed it. -However, nothing else grants you permission to modify or distribute the Program -or its derivative works. These actions are prohibited by law if you do not accept + * a) The work must carry prominent notices stating that you modified it, and -this License. Therefore, by modifying or distributing the Program (or any work + giving a relevant date. -based on the Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying the Program -or works based on it. + * b) The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to keep intact all notices. -6. Each time you redistribute the Program (or any work based on the Program), the -recipient automatically receives a license from the original licensor to copy, -distribute or modify the Program subject to these terms and conditions. You may + * c) You must license the entire work, as a whole, under this License to anyone -not impose any further restrictions on the recipients' exercise of the rights + who comes into possession of a copy. This License will therefore apply, along -granted herein. You are not responsible for enforcing compliance by third parties + with any applicable section 7 additional terms, to the whole of the work, and -to this License. + all its parts, regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not invalidate + such permission if you have separately received it. -7. If, as a consequence of a court judgment or allegation of patent infringement -or for any other reason (not limited to patent issues), conditions are imposed on -you (whether by court order, agreement or otherwise) that contradict the + * d) If the work has interactive user interfaces, each must display Appropriate -conditions of this License, they do not excuse you from the conditions of this + Legal Notices; however, if the Program has interactive interfaces that do not -License. If you cannot distribute so as to satisfy simultaneously your + display Appropriate Legal Notices, your work need not make them do so. -obligations under this License and any other pertinent obligations, then as a -consequence you may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by all those +A compilation of a covered work with other separate and independent works, which -who receive copies directly or indirectly through you, then the only way you +are not by their nature extensions of the covered work, and which are not -could satisfy both it and this License would be to refrain entirely from +combined with it such as to form a larger program, in or on a volume of a storage -distribution of the Program. +or distribution medium, is called an aggregate if the compilation and its +resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a -If any portion of this section is held invalid or unenforceable under any +covered work in an aggregate does not cause this License to apply to the other -particular circumstance, the balance of the section is intended to apply and the +parts of the aggregate. -section as a whole is intended to apply in other circumstances. +6. Conveying Non-Source Forms. -It is not the purpose of this section to induce you to infringe any patents or -other property right claims or to contest validity of any such claims; this -section has the sole purpose of protecting the integrity of the free software +You may convey a covered work in object code form under the terms of sections 4 -distribution system, which is implemented by public license practices. Many +and 5, provided that you also convey the machine-readable Corresponding Source -people have made generous contributions to the wide range of software distributed +under the terms of this License, in one of these ways: -through that system in reliance on consistent application of that system; it is -up to the author/donor to decide if he or she is willing to distribute software -through any other system and a licensee cannot impose that choice. + * a) Convey the object code in, or embodied in, a physical product (including a + physical distribution medium), accompanied by the Corresponding Source fixed + on a durable physical medium customarily used for software interchange. -This section is intended to make thoroughly clear what is believed to be a -consequence of the rest of this License. + * b) Convey the object code in, or embodied in, a physical product (including a + physical distribution medium), accompanied by a written offer, valid for at -8. If the distribution and/or use of the Program is restricted in certain + least three years and valid for as long as you offer spare parts or customer -countries either by patents or by copyrighted interfaces, the original copyright + support for that product model, to give anyone who possesses the object code -holder who places the Program under this License may add an explicit geographical + either (1) a copy of the Corresponding Source for all the software in the -distribution limitation excluding those countries, so that distribution is + product that is covered by this License, on a durable physical medium -permitted only in or among countries not thus excluded. In such case, this + customarily used for software interchange, for a price no more than your -License incorporates the limitation as if written in the body of this License. + reasonable cost of physically performing this conveying of source, or (2) + access to copy the Corresponding Source from a network server at no charge. -9. The Free Software Foundation may publish revised and/or new versions of the -General Public License from time to time. Such new versions will be similar in + * c) Convey individual copies of the object code with a copy of the written -spirit to the present version, but may differ in detail to address new problems + offer to provide the Corresponding Source. This alternative is allowed only -or concerns. + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. -Each version is given a distinguishing version number. If the Program specifies a -version number of this License which applies to it and "any later version", you + * d) Convey the object code by offering access from a designated place (gratis -have the option of following the terms and conditions either of that version or + or for a charge), and offer equivalent access to the Corresponding Source in -of any later version published by the Free Software Foundation. If the Program + the same way through the same place at no further charge. You need not -does not specify a version number of this License, you may choose any version + require recipients to copy the Corresponding Source along with the object -ever published by the Free Software Foundation. + code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by you or a third + party) that supports equivalent copying facilities, provided you maintain -10. If you wish to incorporate parts of the Program into other free programs + clear directions next to the object code saying where to find the -whose distribution conditions are different, write to the author to ask for + Corresponding Source. Regardless of what server hosts the Corresponding -permission. For software which is copyrighted by the Free Software Foundation, + Source, you remain obligated to ensure that it is available for as long as -write to the Free Software Foundation; we sometimes make exceptions for this. Our + needed to satisfy these requirements. -decision will be guided by the two goals of preserving the free status of all -derivatives of our free software and of promoting the sharing and reuse of -software generally. + * e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the work + are being offered to the general public at no charge under subsection 6d. -NO WARRANTY +A separable portion of the object code, whose source code is excluded from the -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE +Corresponding Source as a System Library, need not be included in conveying the -PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED +object code work. -IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" -WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +A User Product is either (1) a consumer product, which means any tangible -PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +personal property which is normally used for personal, family, or household -PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +purposes, or (2) anything designed or sold for incorporation into a dwelling. In -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +determining whether a product is a consumer product, doubtful cases shall be +resolved in favor of coverage. For a particular product received by a particular +user, normally used refers to a typical or common use of that class of product, -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +regardless of the status of the particular user or of the way in which the -ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +particular user actually uses, or expects or is expected to use, the product. A -PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, +product is a consumer product regardless of whether the product has substantial -SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY +commercial, industrial or non-consumer uses, unless such uses represent the only -TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +significant mode of use of the product. -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF -THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER -PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +Installation Information for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute modified +versions of a covered work in that User Product from a modified version of its -END OF TERMS AND CONDITIONS +Corresponding Source. The information must suffice to ensure that the continued ---- +functioning of the modified object code is in no case prevented or interfered -ISC License +with solely because modification has been made. -(containerd/containerd v2.0.5, containerd/containerd v2.1.0, inspektor-gadget/inspektor-gadget v0.39.0) -ISC License (ISCL) -================== +If you convey an object code work under this section in, or with, or specifically +for use in, a User Product, and the conveying occurs as part of a transaction in +which the right of possession and use of the User Product is transferred to the -Copyright (c) 4-digit year, Company or Person's Name +recipient in perpetuity or for a fixed term (regardless of how the transaction is +characterized), the Corresponding Source conveyed under this section must be +accompanied by the Installation Information. But this requirement does not apply -Permission to use, copy, modify, and/or distribute this software for any purpose +if neither you nor any third party retains the ability to install modified object -with or without fee is hereby granted, provided that the above copyright notice +code on the User Product (for example, the work has been installed in ROM). -and this permission notice appear in all copies. +The requirement to provide Installation Information does not include a -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +requirement to continue to provide support service, warranty, or updates for a -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +work that has been modified or installed by the recipient, or for the User -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +Product in which it has been modified or installed. Access to a network may be -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +denied when the modification itself materially and adversely affects the -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +operation of the network or violates the rules and protocols for communication -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +across the network. -THIS SOFTWARE. ---- -ISC License +Corresponding Source conveyed, and Installation Information provided, in accord -(go-spew 20180930-snapshot-d8f796af) +with this section must be in a format that is publicly documented (and with an -ISC License +implementation available to the public in source code form), and must require no +special password or key for unpacking, reading or copying. -Copyright (c) 2012-2016 Dave Collins +7. Additional Terms. -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above +Additional permissions are terms that supplement the terms of this License by -copyright notice and this permission notice appear in all copies. +making exceptions from one or more of its conditions. Additional permissions that +are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable law. -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +If additional permissions apply only to part of the Program, that part may be -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +used separately under those permissions, but the entire Program remains governed -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +by this License without regard to the additional permissions. -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +When you convey a copy of a covered work, you may at your option remove any -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE +additional permissions from that copy, or from any part of it. (Additional ---- +permissions may be written to require their own removal in certain cases when you -MIT License +modify the work.) You may place additional permissions on material, added by you -(josharian/intern v1.0.0) +to a covered work, for which you have or can give appropriate copyright -MIT License +permission. -Copyright (c) 2019 Josh Bleecher Snyder +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights + * a) Disclaiming warranty or limiting liability differently from the terms of -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + sections 15 and 16 of this License; or -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + * b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed -The above copyright notice and this permission notice shall be included in all + by works containing it; or -copies or substantial portions of the Software. + * c) Prohibiting misrepresentation of the origin of that material, or requiring -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + that modified versions of such material be marked in reasonable ways as -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + different from the original version; or -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * d) Limiting the use for publicity purposes of names of licensors or authors -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + of the material; or -SOFTWARE ---- -MIT License + * e) Declining to grant rights under trademark law for use of some trade names, -(github.com/rivo/uniseg v0.1.0) + trademarks, or service marks; or -MIT License + * f) Requiring indemnification of licensors and authors of that material by -Copyright (c) 2019 Oliver Kuederle + anyone who conveys the material (or modified versions of it) with contractual + assumptions of liability to the recipient, for any liability that these + contractual assumptions directly impose on those licensors and authors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights +All other non-permissive additional terms are considered further restrictions -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +within the meaning of section 10. If the Program as you received it, or any part -copies of the Software, and to permit persons to whom the Software is +of it, contains a notice stating that it is governed by this License along with a -furnished to do so, subject to the following conditions: +term that is a further restriction, you may remove that term. If a license +document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms -The above copyright notice and this permission notice shall be included in all +of that license document, provided that the further restriction does not survive -copies or substantial portions of the Software. +such relicensing or conveying. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +If you add terms to a covered work in accord with this section, you must place, -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +in the relevant source files, a statement of the additional terms that apply to -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +those files, or a notice indicating where to find the applicable terms. -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +Additional terms, permissive or non-permissive, may be stated in the form of a -SOFTWARE +separately written license, or stated as exceptions; the above requirements apply ---- +either way. -MIT License -(kisielk-gotool v1.0.0) -Copyright (c) 2013 Kamil Kisiel +8. Termination. -Permission is hereby granted, free of charge, to any person obtaining +You may not propagate or modify a covered work except as expressly provided under -a copy of this software and associated documentation files (the +this License. Any attempt otherwise to propagate or modify it is void, and will -"Software"), to deal in the Software without restriction, including +automatically terminate your rights under this License (including any patent -without limitation the rights to use, copy, modify, merge, publish, +licenses granted under the third paragraph of section 11). -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated (a) provisionally, unless and until the +copyright holder explicitly and finally terminates your license, and (b) -The above copyright notice and this permission notice shall be +permanently, if the copyright holder fails to notify you of the violation by some -included in all copies or substantial portions of the Software. +reasonable means prior to 60 days after the cessation. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +Moreover, your license from a particular copyright holder is reinstated -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +permanently if the copyright holder notifies you of the violation by some -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +reasonable means, this is the first time you have received notice of violation of -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +this License (for any work) from that copyright holder, and you cure the -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +violation prior to 30 days after your receipt of the notice. -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ---- +Termination of your rights under this section does not terminate the licenses of -MIT License +parties who have received copies or rights from you under this License. If your -(client9/misspell v0.3.4) +rights have been terminated and not permanently reinstated, you do not qualify to -The MIT License (MIT) +receive new licenses for the same material under section 10. -Copyright (c) 2015-2017 Nick Galbreath +9. Acceptance Not Required for Having Copies. -Permission is hereby granted, free of charge, to any person obtaining a copy +You are not required to accept this License in order to receive or run a copy of -of this software and associated documentation files (the "Software"), to deal +the Program. Ancillary propagation of a covered work occurring solely as a -in the Software without restriction, including without limitation the rights +consequence of using peer-to-peer transmission to receive a copy likewise does -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +not require acceptance. However, nothing other than this License grants you -copies of the Software, and to permit persons to whom the Software is +permission to propagate or modify any covered work. These actions infringe -furnished to do so, subject to the following conditions: +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +10. Automatic Licensing of Downstream Recipients. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +Each time you convey a covered work, the recipient automatically receives a -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +license from the original licensors, to run, modify and propagate that work, -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +subject to this License. You are not responsible for enforcing compliance by -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +third parties with this License. -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE ---- +An entity transaction is a transaction transferring control of an organization, -MIT License +or substantially all assets of one, or subdividing an organization, or merging -(gregjones/httpcache 20190611-snapshot-901d9072) +organizations. If propagation of a covered work results from an entity -Copyright 2012 Greg Jones (greg.jones@gmail.com) +transaction, each party to that transaction who receives a copy of the work also +receives whatever licenses to the work the party's predecessor in interest had or +could give under the previous paragraph, plus a right to possession of the -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Corresponding Source of the work from the predecessor in interest, if the +predecessor has it or can get it with reasonable efforts. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +You may not impose any further restrictions on the exercise of the rights granted +or affirmed under this License. For example, you may not impose a license fee, -THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +royalty, or other charge for exercise of rights granted under this License, and ---- +you may not initiate litigation (including a cross-claim or counterclaim in a -MIT License +lawsuit) alleging that any patent claim is infringed by making, using, selling, -(armon/go-socks5 20160902-snapshot-e7533296) +offering for sale, or importing the Program or any portion of it. -The MIT License (MIT) +11. Patents. -Copyright (c) 2014 Armon Dadgar +A contributor is a copyright holder who authorizes use under this License of -Permission is hereby granted, free of charge, to any person obtaining a copy of +the Program or a work on which the Program is based. The work thus licensed is -this software and associated documentation files (the "Software"), to deal in +called the contributor's contributor version. -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, +A contributor's essential patent claims are all patent claims owned or -subject to the following conditions: +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would -The above copyright notice and this permission notice shall be included in all +be infringed only as a consequence of further modification of the contributor -copies or substantial portions of the Software. +version. For purposes of this definition, control includes the right to grant +patent sublicenses in a manner consistent with the requirements of this License. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +Each contributor grants you a non-exclusive, worldwide, royalty-free patent -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +license under the contributor's essential patent claims, to make, use, sell, -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +offer for sale, import and otherwise run, modify and propagate the contents of -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +its contributor version. -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ---- -MIT License +In the following three paragraphs, a patent license is any express agreement or -(errcheck v1.5.0-alpha) +commitment, however denominated, not to enforce a patent (such as an express -Copyright (c) 2013 Kamil Kisiel +permission to practice a patent or covenant not to sue for patent infringement). +To grant such a patent license to a party means to make such an agreement or +commitment not to enforce a patent against the party. -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without +If you convey a covered work, knowingly relying on a patent license, and the -restriction, including without limitation the rights to use, +Corresponding Source of the work is not available for anyone to copy, free of -copy, modify, merge, publish, distribute, sublicense, and/or sell +charge and under the terms of this License, through a publicly available network -copies of the Software, and to permit persons to whom the +server or other readily accessible means, then you must either (1) cause the -Software is furnished to do so, subject to the following +Corresponding Source to be so available, or (2) arrange to deprive yourself of -conditions: +the benefit of the patent license for this particular work, or (3) arrange, in a +manner consistent with the requirements of this License, to extend the patent +license to downstream recipients. Knowingly relying means you have actual -The above copyright notice and this permission notice shall be +knowledge that, but for the patent license, your conveying the covered work in a -included in all copies or substantial portions of the Software. +country, or your recipient's use of the covered work in a country, would infringe +one or more identifiable patents in that country that you have reason to believe +are valid. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +If, pursuant to or in connection with a single transaction or arrangement, you -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +convey, or propagate by procuring conveyance of, a covered work, and grant a -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +patent license to some of the parties receiving the covered work authorizing them -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +to use, propagate, modify or convey a specific copy of the covered work, then the -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +patent license you grant is automatically extended to all recipients of the -OTHER DEALINGS IN THE SOFTWARE +covered work and works based on it. ---- -MIT License -(mattn-go-runewidth v0.0.10) +A patent license is discriminatory if it does not include within the scope of -The MIT License (MIT) +its coverage, prohibits the exercise of, or is conditioned on the non-exercise of +one or more of the rights that are specifically granted under this License. You +may not convey a covered work if you are a party to an arrangement with a third -Copyright (c) 2016 Yasuhiro Matsumoto +party that is in the business of distributing software, under which you make +payment to the third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties who would -Permission is hereby granted, free of charge, to any person obtaining a copy +receive the covered work from you, a discriminatory patent license (a) in -of this software and associated documentation files (the "Software"), to deal +connection with copies of the covered work conveyed by you (or copies made from -in the Software without restriction, including without limitation the rights +those copies), or (b) primarily for and in connection with specific products or -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +compilations that contain the covered work, unless you entered into that -copies of the Software, and to permit persons to whom the Software is +arrangement, or that patent license was granted, prior to 28 March 2007. -furnished to do so, subject to the following conditions: +Nothing in this License shall be construed as excluding or limiting any implied -The above copyright notice and this permission notice shall be included in all +license or other defenses to infringement that may otherwise be available to you -copies or substantial portions of the Software. +under applicable patent law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +12. No Surrender of Others' Freedom. -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +If conditions are imposed on you (whether by court order, agreement or otherwise) -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +that contradict the conditions of this License, they do not excuse you from the -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +conditions of this License. If you cannot convey a covered work so as to satisfy -SOFTWARE +simultaneously your obligations under this License and any other pertinent ---- +obligations, then as a consequence you may not convey it at all. For example, if -MIT License +you agree to terms that obligate you to collect a royalty for further conveying -(diskv v2.0.1) +from those to whom you convey the Program, the only way you could satisfy both -Copyright (c) 2011-2012 Peter Bourgon +those terms and this License would be to refrain entirely from conveying the +Program. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal +13. Use with the GNU Affero General Public License. -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +Notwithstanding any other provision of this License, you have permission to link -furnished to do so, subject to the following conditions: +or combine any covered work with a work licensed under version 3 of the GNU +Affero General Public License into a single combined work, and to convey the +resulting work. The terms of this License will continue to apply to the part -The above copyright notice and this permission notice shall be included in +which is the covered work, but the special requirements of the GNU Affero General -all copies or substantial portions of the Software. +Public License, section 13, concerning interaction through a network will apply +to the combination as such. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +14. Revised Versions of this License. -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +The Free Software Foundation may publish revised and/or new versions of the GNU -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +General Public License from time to time. Such new versions will be similar in -THE SOFTWARE +spirit to the present version, but may differ in detail to address new problems ---- +or concerns. -MIT License -(jpillora-backoff 1.0.0) -Files: * +Each version is given a distinguishing version number. If the Program specifies -Copyright: 2017 Jaime Pillora +that a certain numbered version of the GNU General Public License or any later -License: Expat +version applies to it, you have the option of following the terms and conditions +either of that numbered version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of the GNU -Files: debian/* +General Public License, you may choose any version ever published by the Free -Copyright: 2018 Dmitry Smirnov +Software Foundation. -License: Expat +If the Program specifies that a proxy can decide which future versions of the GNU -License: Expat +General Public License can be used, that proxy's public statement of acceptance +of a version permanently authorizes you to choose that version for the Program. -Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal +Later license versions may give you additional or different permissions. However, - in the Software without restriction, including without limitation the rights +no additional obligations are imposed on any author or copyright holder as a - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +result of your choosing to follow a later version. - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - . +15. Disclaimer of Warranty. - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - . +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +PARTIES PROVIDE THE PROGRAM AS IS WITHOUT WARRANTY OF ANY KIND, EITHER - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - SOFTWARE ---- -MIT License +16. Limitation of Liability. -(yaml for Go 20141213-snapshot-9f9df343) -Copyright (c) 2006 Kirill Simonov +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS -Permission is hereby granted, free of charge, to any person obtaining a copy of +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, -this software and associated documentation files (the "Software"), to deal in +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE -the Software without restriction, including without limitation the rights to +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE -of the Software, and to permit persons to whom the Software is furnished to do +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY -so, subject to the following conditions: +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -The above copyright notice and this permission notice shall be included in all +17. Interpretation of Sections 15 and 16. -copies or substantial portions of the Software. +If the disclaimer of warranty and limitation of liability provided above cannot -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +be given local legal effect according to their terms, reviewing courts shall -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +apply local law that most closely approximates an absolute waiver of all civil -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +liability in connection with the Program, unless a warranty or assumption of -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +liability accompanies a copy of the Program in return for a fee. -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +END OF TERMS AND CONDITIONS ---- -MIT License -(github.com/konsorten/go-windows-terminal-sequences v1.0.1) -(The MIT License) +How to Apply These Terms to Your New Programs -Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) +If you develop a new program, and you want it to be of the greatest possible use +to the public, the best way to achieve this is to make it free software which -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +everyone can redistribute and change under these terms. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion of +warranty; and each file should have at least the copyright line and a pointer -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +to where the full notice is found. ---- -MIT License -(olekukonko-tablewriter v0.0.5) + +Copyright (C) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal +This program is free software: you can redistribute it and/or modify -in the Software without restriction, including without limitation the rights +it under the terms of the GNU General Public License as published by -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +the Free Software Foundation, either version 3 of the License, or -copies of the Software, and to permit persons to whom the Software is +(at your option) any later version. -furnished to do so, subject to the following conditions: +This program is distributed in the hope that it will be useful, -The above copyright notice and this permission notice shall be included in +but WITHOUT ANY WARRANTY; without even the implied warranty of -all copies or substantial portions of the Software. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +You should have received a copy of the GNU General Public License -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +along with this program. If not, see . -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +Also add information on how to contact you by electronic and paper mail. -THE SOFTWARE ---- -MIT License +If the program does terminal interaction, make it output a short notice like this -(GoDoc Text v0.2.0) +when it starts in an interactive mode: -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: github.com/kr/text -Source: https://github.com/kr/text/ + Copyright (C) -Files: * +This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. -Copyright: 2013 Keith Rarick +This is free software, and you are welcome to redistribute it -License: Expat +under certain conditions; type `show c' for details. -Files: debian/* +The hypothetical commands `show w' and `show c' should show the appropriate parts -Copyright: 2013 Tonnerre Lombard +of the General Public License. Of course, your program's commands might be -License: Expat +different; for a GUI interface, you would use an about box. -License: Expat +You should also get your employer (if you work as a programmer) or school, if +any, to sign a copyright disclaimer for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see -Permission is hereby granted, free of charge, to any person obtaining a copy +. - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +The GNU General Public License does not permit incorporating your program into - copies of the Software, and to permit persons to whom the Software is +proprietary programs. If your program is a subroutine library, you may consider - furnished to do so, subject to the following conditions: +it more useful to permit linking proprietary applications with the library. If - . +this is what you want to do, use the GNU Lesser General Public License instead of - The above copyright notice and this permission notice shall be included in +this License. But first, please read - all copies or substantial portions of the Software. +. - . +--- - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +Go BSD License with Patent Provision - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +(Go programming language 20160322-snapshot) - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +Copyright 2009 The Go Authors. All rights reserved. - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +Redistribution and use in source and binary forms, with or without modification, - THE SOFTWARE +are permitted provided that the following conditions are met: ---- -MIT License -(alecthomas-kingpin v2.4.0, alecthomas-units 20211218-snapshot-b94a6e3c, Azure/azure-sdk-for-go 20240522-snapshot, Azure/azure-sdk-for-go 20250605-snapshot, Azure/azure-sdk-for-go sdk/azcore/v1.18.0, Azure/azure-sdk-for-go sdk/azidentity/v1.8.2, Azure/azure-sdk-for-go sdk/internal/v1.11.0, Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/compute/armcompute/v5.7.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerservice/armcontainerservice/v4.8.0, Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.4.0, Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0, Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.6.0, AzureAD/microsoft-authentication-library-for-go 20250410-snapshot, AzureAD/microsoft-authentication-library-for-go v1.4.2, cenkalti/backoff v4.3.0, cespare/xxhash v2.3.0, cli/cli v2.74.0, containerd/containerd v2.0.5, containerd/containerd v2.1.0, cpuguy83-go-md2man v2.0.6, dgryski/go-rendezvous 20200823-snapshot-9f7001d1, dominikh/go-tools 20190523-snapshot-ea95bdfd, felixge/httpsnoop v1.0.4, go humanize 20250512-snapshot-b48bc01a, Go Testify v1.10.0, go-restful v3.11.0, go-task/slim-sprig v3.0.0, go-zap v1.27.0, go.etcd.io/bbolt v1.3.11, go.uber.org/goleak v1.3.0, go.uber.org/multierr v1.11.0, golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd, golang-github-ghodss-yaml-dev 20240620-snapshot, golang-jwt/jwt v4.5.0, golang-jwt/jwt v5.2.2, golang-stats v0.7.0, gomega v1.35.1, govalidator 20230301-snapshot-a9d515a0, inspektor-gadget/inspektor-gadget v0.39.0, jarcoal/httpmock v1.4.0, keybase/go-keychain 20231219-snapshot-57a3676c, kr/pretty v0.3.1, mailru/easyjson v0.9.0, mapstructure v1.5.0, Microsoft-go-winio v0.6.0, mitchellh-hashstructure v2.0.2, natefinch/lumberjack v2.2.1, niemeyer/pretty 20200227-snapshot-a10e7cae, onsi/ginkgo 2.21.0, secureheader v0.2.0, Sirupsen/logrus v1.9.3, stoewer/go-strcase v1.3.0, stretchr/objx v0.5.2, tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3, xiang90-probing 20221125-snapshot-a49e3df8, yaml for Go v3.0.1, youmark/pkcs8 20181117-snapshot-1be2e3e5, yuin/goldmark v1.4.13, zcalusic/sysinfo v1.1.3, zeebo/errs v1.4.0) + * Redistributions of source code must retain the above copyright notice, this -The MIT License + list of conditions and the following disclaimer. -=============== + * Redistributions in binary form must reproduce the above copyright notice, -Copyright (c) + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in the + * Neither the name of Google Inc. nor the names of its contributors may be used -Software without restriction, including without limitation the rights to use, + to endorse or promote products derived from this software without specific -copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + prior written permission. -Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -The above copyright notice and this permission notice shall be included in all +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -copies or substantial portions of the Software. +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- +Subject to the terms and conditions of this License, Google hereby grants to You -MIT License +a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(beorn7-perks v1.0.1) +(except as stated in this section) patent license to make, have made, use, offer -Copyright (C) 2013 Blake Mizerany +to sell, sell, import, and otherwise transfer this implementation of Go, where +such license applies only to those patent claims licensable by Google that are +necessarily infringed by use of this implementation of Go. If You institute -Permission is hereby granted, free of charge, to any person obtaining +patent litigation against any entity (including a cross-claim or counterclaim in -a copy of this software and associated documentation files (the +a lawsuit) alleging that this implementation of Go or a Contribution incorporated -"Software"), to deal in the Software without restriction, including +within this implementation of Go constitutes direct or contributory patent -without limitation the rights to use, copy, modify, merge, publish, +infringement, then any patent licenses granted to You under this License for this -distribute, sublicense, and/or sell copies of the Software, and to +implementation of Go shall terminate as of the date such litigation is filed. -permit persons to whom the Software is furnished to do so, subject to +--- -the following conditions: +ISC License +(@withfig/autocomplete 2.657.0, containerd/containerd v2.0.5, containerd/containerd v2.1.3) +ISC License (ISCL) -The above copyright notice and this permission notice shall be +================== -included in all copies or substantial portions of the Software. +Copyright (c) 4-digit year, Company or Person's Name -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +Permission to use, copy, modify, and/or distribute this software for any purpose -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +with or without fee is hereby granted, provided that the above copyright notice -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +and this permission notice appear in all copies. -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ---- +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -MIT License +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -(x448/float16 v0.8.4) +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -MIT License +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -Copyright (c) 2019 Montgomery Edwards and Faye Amacker +THIS SOFTWARE. +--- +ISC License -Permission is hereby granted, free of charge, to any person obtaining a copy +(go-spew 20180930-snapshot-d8f796af) -of this software and associated documentation files (the "Software"), to deal +ISC License -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +Copyright (c) 2012-2016 Dave Collins -furnished to do so, subject to the following conditions: +Permission to use, copy, modify, and/or distribute this software for any -The above copyright notice and this permission notice shall be included in all +purpose with or without fee is hereby granted, provided that the above -copies or substantial portions of the Software. +copyright notice and this permission notice appear in all copies. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -SOFTWARE +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE --- MIT License -(blang-semver v4.0.0) +(josharian/intern v1.0.0) -The MIT License +MIT License -Copyright (c) 2014 Benedikt Lang +Copyright (c) 2019 Josh Bleecher Snyder @@ -5415,9 +6247,9 @@ furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in +The above copyright notice and this permission notice shall be included in all -all copies or substantial portions of the Software. +copies or substantial portions of the Software. @@ -5431,21 +6263,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -THE SOFTWARE +SOFTWARE --- MIT License -(jsoniter-go v1.1.12) +(github.com/rivo/uniseg v0.2.0) MIT License -Copyright (c) 2016 json-iterator +Copyright (c) 2019 Oliver Kuederle @@ -5481,876 +6313,996 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +SOFTWARE --- MIT License -(BurntSushi/toml v0.3.1) - -The MIT License (MIT) - +(kisielk-gotool v1.0.0) +Copyright (c) 2013 Kamil Kisiel -Copyright (c) 2013 TOML authors +Permission is hereby granted, free of charge, to any person obtaining -Permission is hereby granted, free of charge, to any person obtaining a copy +a copy of this software and associated documentation files (the -of this software and associated documentation files (the "Software"), to deal +"Software"), to deal in the Software without restriction, including -in the Software without restriction, including without limitation the rights +without limitation the rights to use, copy, modify, merge, publish, -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +distribute, sublicense, and/or sell copies of the Software, and to -copies of the Software, and to permit persons to whom the Software is +permit persons to whom the Software is furnished to do so, subject to -furnished to do so, subject to the following conditions: +the following conditions: -The above copyright notice and this permission notice shall be included in +The above copyright notice and this permission notice shall be -all copies or substantial portions of the Software. +included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -THE SOFTWARE +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --- -Mozilla Public License 2.0 - -(inspektor-gadget/inspektor-gadget v0.39.0) - -Mozilla Public License - -Version 2.0 +MIT License -====================== +(client9/misspell v0.3.4) +The MIT License (MIT) +Copyright (c) 2015-2017 Nick Galbreath -1. Definitions --------------- +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal - 1.1. "Contributor" +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is - means each individual or legal entity that creates, contributes to the creation +furnished to do so, subject to the following conditions: - of, or owns Covered Software. +The above copyright notice and this permission notice shall be included in all - 1.2. "Contributor Version" +copies or substantial portions of the Software. - means the combination of the Contributions of others (if any) used by a +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - Contributor and that particular Contributor's Contribution. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - 1.3. "Contribution" +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE - means Covered Software of a particular Contributor. +--- +MIT License +(gregjones/httpcache 20190611-snapshot-901d9072) - 1.4. "Covered Software" +Copyright 2012 Greg Jones (greg.jones@gmail.com) - means Source Code Form to which the initial Contributor has attached the notice +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - in Exhibit A, the Executable Form of such Source Code Form, and Modifications - of such Source Code Form, in each case including portions thereof. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - 1.5. "Incompatible With Secondary Licenses" +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +--- - means +MIT License +(armon/go-socks5 20160902-snapshot-e7533296) +The MIT License (MIT) - a. +Copyright (c) 2014 Armon Dadgar - that the initial Contributor has attached the notice described in Exhibit B - to the Covered Software; or +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in - b. +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, - that the Covered Software was made available under the terms of version 1.1 +subject to the following conditions: - or earlier of the License, but not also under the terms of a Secondary - License. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - 1.6. "Executable Form" +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - means any form of the work other than Source Code Form. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - 1.7. "Larger Work" +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +--- - means a work that combines Covered Software with other material, in a separate +MIT License - file or files, that is not Covered Software. +(errcheck v1.5.0-alpha) +Copyright (c) 2013 Kamil Kisiel - 1.8. "License" +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation - means this document. +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell - 1.9. "Licensable" +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed - by this License. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. - 1.10. "Modifications" +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - means any of the following: +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - a. +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE - any file in Source Code Form that results from an addition to, deletion +--- - from, or modification of the contents of Covered Software; or +MIT License +(diskv v2.0.1) +Copyright (c) 2011-2012 Peter Bourgon - b. +Permission is hereby granted, free of charge, to any person obtaining a copy - any new file in Source Code Form that contains any Covered Software. +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - 1.11. "Patent Claims" of a Contributor +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - means any patent claim(s), including without limitation, method, process, and - apparatus claims, in any patent Licensable by such Contributor that would be +The above copyright notice and this permission notice shall be included in - infringed, but for the grant of the License, by the making, using, selling, +all copies or substantial portions of the Software. - offering for sale, having made, import, or transfer of either its Contributions - or its Contributor Version. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - 1.12. "Secondary License" +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - means either the GNU General Public License, Version 2.0, the GNU Lesser +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - General Public License, Version 2.1, the GNU Affero General Public License, +THE SOFTWARE - Version 3.0, or any later versions of those licenses. +--- +MIT License +(docker-org v0.5.0) - 1.13. "Source Code Form" +Files: * +Copyright: 2016 David Calavera +License: Expat - means the form of the work preferred for making modifications. +Files: debian/* - 1.14. "You" (or "Your") +Copyright: 2016 Tim Potter +License: Expat +Comment: Debian packaging is licensed under the same terms as upstream - means an individual or a legal entity exercising rights under this License. For - legal entities, "You" includes any entity that controls, is controlled by, or - is under common control with You. For purposes of this definition, "control" +License: Expat - means (a) the power, direct or indirect, to cause the direction or management + Copyright (c) 2016 David Calavera - of such entity, whether by contract or otherwise, or (b) ownership of more than + . - fifty percent (50%) of the outstanding shares or beneficial ownership of such - entity. +Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, -2. License Grants and Conditions + distribute, sublicense, and/or sell copies of the Software, and to --------------------------------- + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + . + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. - 2.1. Grants + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - license: + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - a. + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +--- +MIT License - under intellectual property rights (other than patent or trademark) +(jpillora-backoff 1.0.0) - Licensable by such Contributor to use, reproduce, make available, modify, +Files: * - display, perform, distribute, and otherwise exploit its Contributions, +Copyright: 2017 Jaime Pillora - either on an unmodified basis, with Modifications, or as part of a Larger +License: Expat - Work; and +Files: debian/* - b. +Copyright: 2018 Dmitry Smirnov +License: Expat - under Patent Claims of such Contributor to make, use, sell, offer for sale, - have made, import, and otherwise transfer either its Contributions or its +License: Expat - Contributor Version. +Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights - 2.2. Effective Date + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The licenses granted in Section2.1 with respect to any Contribution become + . - effective for each Contribution on the date the Contributor first distributes + The above copyright notice and this permission notice shall be included in all - such Contribution. + copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - 2.3. Limitations on Grant Scope + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - The licenses granted in this Section2 are the only rights granted under this + SOFTWARE - License. No additional rights or licenses will be implied from the distribution +--- - or licensing of Covered Software under this License. Notwithstanding +MIT License - Section2.1(b) above, no patent license is granted by a Contributor: +(yaml for Go 20141213-snapshot-9f9df343) +Copyright (c) 2006 Kirill Simonov - a. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in - for any code that a Contributor has removed from Covered Software; or +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do - b. +so, subject to the following conditions: - for infringements caused by: (i) Your and any other third party's +The above copyright notice and this permission notice shall be included in all - modifications of Covered Software, or (ii) the combination of its +copies or substantial portions of the Software. - Contributions with other software (except as part of its Contributor - Version); or +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - c. +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - under Patent Claims infringed by Covered Software in the absence of its +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - Contributions. +SOFTWARE +--- +MIT License - This License does not grant any rights in the trademarks, service marks, or +(github.com/konsorten/go-windows-terminal-sequences v1.0.1) - logos of any Contributor (except as may be necessary to comply with the notice +(The MIT License) - requirements in Section3.4). +Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de) - 2.4. Subsequent Licenses +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - No Contributor makes additional grants as a result of Your choice to distribute +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - the Covered Software under a subsequent version of this License (see - Section10.2) or under the terms of a Secondary License (if permitted under the - terms of Section3.3). +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +--- +MIT License +(govalidator v11.0.1) +The MIT License (MIT) - 2.5. Representation +Copyright (c) 2014-2020 Alex Saskevich - Each Contributor represents that the Contributor believes its Contributions are - its original creation(s) or it has sufficient rights to grant the rights to its - Contributions conveyed by this License. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is - 2.6. Fair Use +furnished to do so, subject to the following conditions: - This License is not intended to limit any rights You have under applicable +The above copyright notice and this permission notice shall be included in all - copyright doctrines of fair use, fair dealing, or other equivalents. +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - 2.7. Conditions +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - Section2.1. +SOFTWARE +--- +MIT License +(olekukonko-tablewriter v0.0.5) +Copyright (C) 2014 by Oleku Konko -3. Responsibilities -------------------- +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - 3.1. Distribution of Source Form +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the +The above copyright notice and this permission notice shall be included in - terms of this License. You must inform recipients that the Source Code Form of +all copies or substantial portions of the Software. - the Covered Software is governed by the terms of this License, and how they can - obtain a copy of this License. You may not attempt to alter or restrict the - recipients' rights in the Source Code Form. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - 3.2. Distribution of Executable Form +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE +--- - If You distribute Covered Software in Executable Form then: +MIT License +(GoDoc Text v0.2.0) +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ - a. +Upstream-Name: github.com/kr/text +Source: https://github.com/kr/text/ - such Covered Software must also be made available in Source Code Form, as - described in Section3.1, and You must inform recipients of the Executable +Files: * - Form how they can obtain a copy of such Source Code Form by reasonable +Copyright: 2013 Keith Rarick - means in a timely manner, at a charge no more than the cost of distribution +License: Expat - to the recipient; and +Files: debian/* - b. +Copyright: 2013 Tonnerre Lombard +License: Expat - You may distribute such Executable Form under the terms of this License, or - sublicense it under different terms, provided that the license for the +License: Expat - Executable Form does not attempt to limit or alter the recipients' rights - in the Source Code Form under this License. +Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - 3.3. Distribution of a Larger Work + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . - You may create and distribute a Larger Work under terms of Your choice, + The above copyright notice and this permission notice shall be included in - provided that You also comply with the requirements of this License for the + all copies or substantial portions of the Software. - Covered Software. If the Larger Work is a combination of Covered Software with + . - a work governed by one or more Secondary Licenses, and the Covered Software is + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - not Incompatible With Secondary Licenses, this License permits You to + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - additionally distribute such Covered Software under the terms of such Secondary + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - License(s), so that the recipient of the Larger Work may, at their option, + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - further distribute the Covered Software under the terms of either this License + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - or such Secondary License(s). + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE +--- +MIT License +(alecthomas-kingpin v2.4.0, alecthomas-units 20211218-snapshot-b94a6e3c, AstroProfundis/sysinfo 20211201-snapshot-9f959380, Azure/azure-sdk-for-go 20240704-snapshot, Azure/azure-sdk-for-go 20250604-snapshot, Azure/azure-sdk-for-go 20250706-snapshot, Azure/azure-sdk-for-go 20250804-snapshot, Azure/azure-sdk-for-go 20250922-snapshot, Azure/azure-sdk-for-go sdk/azcore/v1.19.1, Azure/azure-sdk-for-go sdk/azidentity/v1.12.0, Azure/azure-sdk-for-go sdk/internal/v1.11.2, Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/billing/armbilling/v0.7.0, Azure/azure-sdk-for-go sdk/resourcemanager/blockchain/armblockchain/v0.6.0, Azure/azure-sdk-for-go sdk/resourcemanager/connectedvmware/armconnectedvmware/v0.1.0, Azure/azure-sdk-for-go sdk/resourcemanager/connectedvmware/armconnectedvmware/v1.0.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/edgeorder/armedgeorder/v0.3.0, Azure/azure-sdk-for-go sdk/resourcemanager/edgeorder/armedgeorder/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/elastic/armelastic/v0.5.0, Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.5.0, Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0, Azure/azure-sdk-for-go sdk/resourcemanager/msi/armmsi/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.3.0, Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.8.1, AzureAD/microsoft-authentication-library-for-go v1.5.0, bingoohuang/golog 20230128-snapshot-a8993f58, cenkalti/backoff v4.3.0, cespare/xxhash v2.3.0, containerd/containerd v2.0.5, containerd/containerd v2.1.3, cpuguy83-go-md2man v2.0.6, dominikh/go-tools 20190523-snapshot-ea95bdfd, felixge/httpsnoop v1.0.4, fxamacker/cbor v2.9.0, Gitea 1.21.3, github.com/expr-lang/expr v1.16.6, github.com/go-viper/mapstructure v2.4.0, go humanize 20250512-snapshot-b48bc01a, Go Logrus v1.9.3, Go Testify v1.11.1, Go Testify v1.9.0, go-faker/faker v4.6.1, go-restful v3.12.2, go-task/slim-sprig v3.0.0, go-zap v1.27.0, go.uber.org/automaxprocs v1.6.0, go.uber.org/goleak v1.3.0, go.uber.org/multierr v1.11.0, go.yaml.in/yaml/v2 v2.4.2, go.yaml.in/yaml/v2 v3.0.4, golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd, golang-github-ghodss-yaml-dev 20240620-snapshot, golang-jwt/jwt v5.3.0, golang-stats v0.7.1, gomega v1.37.0, govalidator 20230301-snapshot-a9d515a0, jarcoal/httpmock v1.4.1, keybase/go-keychain v0.0.1, kr/pretty v0.3.1, mailru/easyjson v0.9.0, mattn-go-runewidth v0.0.16, Microsoft-go-winio v0.6.0, mitchellh-hashstructure v2.0.2, natefinch/lumberjack v2.2.1, niemeyer/pretty 20200227-snapshot-a10e7cae, onsi/ginkgo v2.23.4, secureheader v0.2.0, stoewer/go-strcase v1.3.0, stretchr/objx v0.5.2, Telegraf v1.28.1, tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3, withfig/autocomplete spec-build-number-0.1303.0, xiang90-probing 20221125-snapshot-a49e3df8, yaml for Go v3.0.1, youmark/pkcs8 20240726-snapshot-a2c0da24, yuin/goldmark v1.4.13, zcalusic/sysinfo 20250716-snapshot, zcalusic/sysinfo v1.1.3, zeebo/errs v1.4.0) - 3.4. Notices +The MIT License +=============== - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations of +Copyright (c) - liability) contained within the Source Code Form of the Covered Software, - except that You may alter any license notices to the extent required to remedy - known factual inaccuracies. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, - 3.5. Application of Additional Terms +subject to the following conditions: - You may choose to offer, and to charge a fee for, warranty, support, indemnity +The above copyright notice and this permission notice shall be included in all - or liability obligations to one or more recipients of Covered Software. +copies or substantial portions of the Software. - However, You may do so only on Your own behalf, and not on behalf of any - Contributor. You must make it absolutely clear that any such warranty, support, - indemnity, or liability obligation is offered by You alone, and You hereby +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - agree to indemnify every Contributor for any liability incurred by such +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - Contributor as a result of warranty, support, indemnity or liability terms You +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - offer. You may include additional disclaimers of warranty and limitations of +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - liability specific to any jurisdiction. +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--- +MIT License +(beorn7-perks v1.0.1) -4. Inability to Comply Due to Statute or Regulation +Copyright (C) 2013 Blake Mizerany ---------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining -If it is impossible for You to comply with any of the terms of this License with +a copy of this software and associated documentation files (the -respect to some or all of the Covered Software due to statute, judicial order, or +"Software"), to deal in the Software without restriction, including -regulation then You must: (a) comply with the terms of this License to the +without limitation the rights to use, copy, modify, merge, publish, -maximum extent possible; and (b) describe the limitations and the code they +distribute, sublicense, and/or sell copies of the Software, and to -affect. Such description must be placed in a text file included with all +permit persons to whom the Software is furnished to do so, subject to -distributions of the Covered Software under this License. Except to the extent +the following conditions: -prohibited by statute or regulation, such description must be sufficiently -detailed for a recipient of ordinary skill to be able to understand it. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -5. Termination +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, --------------- +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - 5.1. The rights granted under this License will terminate automatically if You +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - fail to comply with any of its terms. However, if You become compliant, then +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - the rights granted under this License from a particular Contributor are +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE - reinstated (a) provisionally, unless and until such Contributor explicitly and +--- - finally terminates Your grants, and (b) on an ongoing basis, if such +MIT License - Contributor fails to notify You of the non-compliance by some reasonable means +(x448/float16 v0.8.4) - prior to 60 days after You have come back into compliance. Moreover, Your +MIT License - grants from a particular Contributor are reinstated on an ongoing basis if such - Contributor notifies You of the non-compliance by some reasonable means, this - is the first time You have received notice of non-compliance with this License +Copyright (c) 2019 Montgomery Edwards and Faye Amacker - from such Contributor, and You become compliant prior to 30 days after Your - receipt of the notice. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal - 5.2. If You initiate litigation against any entity by asserting a patent +in the Software without restriction, including without limitation the rights - infringement claim (excluding declaratory judgment actions, counter-claims, and +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - cross-claims) alleging that a Contributor Version directly or indirectly +copies of the Software, and to permit persons to whom the Software is - infringes any patent, then the rights granted to You by any and all +furnished to do so, subject to the following conditions: - Contributors for the Covered Software under Section2.1 of this License shall - terminate. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - 5.3. In the event of termination under Sections5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - termination shall survive termination. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -6. Disclaimer of Warranty +SOFTWARE -------------------------- +--- +MIT License +(blang-semver v4.0.0) -Covered Software is provided under this License on an "as is" basis, without +The MIT License -warranty of any kind, either expressed, implied, or statutory, including, without -limitation, warranties that the Covered Software is free of defects, -merchantable, fit for a particular purpose or non-infringing. The entire risk as +Copyright (c) 2014 Benedikt Lang -to the quality and performance of the Covered Software is with You. Should any -Covered Software prove defective in any respect, You (not any Contributor) assume -the cost of any necessary servicing, repair, or correction. This disclaimer of +Permission is hereby granted, free of charge, to any person obtaining a copy -warranty constitutes an essential part of this License. No use of any Covered +of this software and associated documentation files (the "Software"), to deal -Software is authorized under this License except under this disclaimer. +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -7. Limitation of Liability --------------------------- +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -Under no circumstances and under no legal theory, whether tort (including -negligence), contract, or otherwise, shall any Contributor, or anyone who +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -distributes Covered Software as permitted above, be liable to You for any direct, +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -indirect, special, incidental, or consequential damages of any character +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -including, without limitation, damages for lost profits, loss of goodwill, work +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -stoppage, computer failure or malfunction, or any and all other commercial +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -damages or losses, even if such party shall have been informed of the possibility +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -of such damages. This limitation of liability shall not apply to liability for +THE SOFTWARE -death or personal injury resulting from such party's negligence to the extent +--- -applicable law prohibits such limitation. Some jurisdictions do not allow the +MIT License -exclusion or limitation of incidental or consequential damages, so this exclusion +(docker-org v0.1.0, docker-org v0.2.0, docker-org v0.3.0) -and limitation may not apply to You. +Copyright (c) 2016 David Calavera +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the -8. Litigation +"Software"), to deal in the Software without restriction, including -------------- +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to -Any litigation relating to this License may be brought only in the courts of a +the following conditions: -jurisdiction where the defendant maintains its principal place of business and -such litigation shall be governed by laws of that jurisdiction, without reference -to its conflict-of-law provisions. Nothing in this Section shall prevent a +The above copyright notice and this permission notice shall be -party's ability to bring cross-claims or counter-claims. +included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -9. Miscellaneous +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ----------------- +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -This License represents the complete agreement concerning the subject matter +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE -hereof. If any provision of this License is held to be unenforceable, such +--- -provision shall be reformed only to the extent necessary to make it enforceable. +MIT License -Any law or regulation which provides that the language of a contract shall be +(jsoniter-go v1.1.12) -construed against the drafter shall not be used to construe this License against +MIT License -a Contributor. +Copyright (c) 2016 json-iterator -10. Versions of the License +Permission is hereby granted, free of charge, to any person obtaining a copy ---------------------------- +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - 10.1. New Versions +The above copyright notice and this permission notice shall be included in all - Mozilla Foundation is the license steward. Except as provided in Section10.3, +copies or substantial portions of the Software. - no one other than the license steward has the right to modify or publish new - versions of this License. Each version will be given a distinguishing version - number. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - 10.2. Effect of New Versions +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE +--- - You may distribute the Covered Software under the terms of the version of the +MIT License - License under which You originally received the Covered Software, or under the +(BurntSushi/toml v0.3.1) - terms of any subsequent version published by the license steward. +The MIT License (MIT) +Copyright (c) 2013 TOML authors - 10.3. Modified Versions +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal - If you create software not governed by this License, and you want to create a +in the Software without restriction, including without limitation the rights - new license for such software, you may create and use a modified version of +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - this License if you rename the license and remove any references to the name of +copies of the Software, and to permit persons to whom the Software is - the license steward (except to note that such modified license differs from +furnished to do so, subject to the following conditions: - this License). +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. - 10.4. Distributing Source Code Form that is Incompatible With Secondary - Licenses +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - If You choose to distribute Source Code Form that is Incompatible With +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - Secondary Licenses under the terms of this version of the License, the notice +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - described in Exhibit B of this License must be attached. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE +--- +MIT License +(rusoto 0.45.0, rusoto 0.47.0) -Exhibit A - Source Code Form License Notice +The MIT License (MIT) -------------------------------------------- +Copyright (c) 2017 Rusoto Project Developers - This Source Code Form is subject to the terms of the Mozilla Public License, - v. 2.0. If a copy of the MPL was not distributed with this file, You can - obtain one at http://mozilla.org/MPL/2.0/. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights -If it is not possible or desirable to put the notice in a particular file, then +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -You may include the notice in a location (such as a LICENSE file in a relevant +copies of the Software, and to permit persons to whom the Software is -directory) where a recipient would be likely to look for such a notice. +furnished to do so, subject to the following conditions: -You may add additional accurate notices of copyright ownership. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -Exhibit B - "Incompatible With Secondary Licenses" Notice +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ---------------------------------------------------------- +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - This Source Code Form is "Incompatible With Secondary Licenses", as defined +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - by the Mozilla Public License, v. 2.0. +SOFTWARE --- \ No newline at end of file diff --git a/NOTICE_ASUP_module.txt b/NOTICE_ASUP_module.txt index 9d0e17765..4980c2d12 100644 --- a/NOTICE_ASUP_module.txt +++ b/NOTICE_ASUP_module.txt @@ -3,12 +3,12 @@ NetApp Notice Report Copyright 2025 About this document -The following copyright statements and licenses apply to the software components that are distributed with the Trident-ASUP version 25.06.0 product. This product does not necessarily use all the software components referred to below. +The following copyright statements and licenses apply to the software components that are distributed with the Trident-ASUP version 25.10.0 product. This product does not necessarily use all the software components referred to below. Where required, source code is published at the following location. https://opensource.netapp.com/ -You may also request a copy of the open source code by submitting a written request to ng-opensource-request@netapp.com or by writing to: +You may also request a copy of the open source code by submitting a written request to ng-opensource-request@netapp.com or by writing to: NetApp Inc. Attention: IP Legal Department (Open Source Request) @@ -25,1329 +25,1359 @@ Your request must include: 6.Your return mailing address and email. This offer is valid for three years from the date you acquired the Trident-ASUP products or for as long asthe applicable license requires this offer to be valid. We may charge you a fee to cover the cost of physical media and processing. Notwithstanding any other agreement or provision, NetApp disclaims all liability and warranties with respect to any source code made available by any method provided above. -Components: +Components: -7-Zip 24.09 : BSD 3-clause "New" or "Revised" License +7-Zip 25.01 : GNU Lesser General Public License v2.1 or later -alecthomas-kingpin v2.4.0 : MIT License +alecthomas-kingpin v2.4.0 : MIT License -alecthomas-units 20240927-snapshot-0f3dac36 : MIT License +alecthomas-units 20240927-snapshot-0f3dac36 : MIT License -armon/go-socks5 20160902-snapshot-e7533296 : MIT License +armon/go-socks5 20160902-snapshot-e7533296 : MIT License -aws/aws-sdk-go-v2 20250128-snapshot : Apache License 2.0 +AstroProfundis/sysinfo 20211201-snapshot-9f959380 : MIT License -aws/aws-sdk-go-v2 config/v1.29.2 : Apache License 2.0 +aws/aws-sdk-go-v2 config/v1.29.2 : Apache License 2.0 -aws/aws-sdk-go-v2 credentials/v1.17.55 : Apache License 2.0 +aws/aws-sdk-go-v2 credentials/v1.17.55 : Apache License 2.0 -aws/aws-sdk-go-v2 feature/ec2/imds/v1.16.25 : Apache License 2.0 +aws/aws-sdk-go-v2 feature/ec2/imds/v1.16.25 : Apache License 2.0 -aws/aws-sdk-go-v2 internal/configsources/v1.3.29 : Apache License 2.0 +aws/aws-sdk-go-v2 internal/configsources/v1.3.32 : Apache License 2.0 -aws/aws-sdk-go-v2 internal/endpoints/v2.6.29 : Apache License 2.0 +aws/aws-sdk-go-v2 internal/endpoints/v2.6.32 : Apache License 2.0 -aws/aws-sdk-go-v2 internal/ini/v1.8.2 : Apache License 2.0 +aws/aws-sdk-go-v2 internal/ini/v1.8.2 : Apache License 2.0 -aws/aws-sdk-go-v2 service/fsx/v1.51.6 : Apache License 2.0 +aws/aws-sdk-go-v2 service/fsx/v1.52.0 : Apache License 2.0 -aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.12.2 : Apache License 2.0 +aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.12.2 : Apache License 2.0 -aws/aws-sdk-go-v2 service/internal/presigned-url/v1.12.10 : Apache License 2.0 +aws/aws-sdk-go-v2 service/internal/presigned-url/v1.12.10 : Apache License 2.0 -aws/aws-sdk-go-v2 service/secretsmanager/v1.34.14 : Apache License 2.0 +aws/aws-sdk-go-v2 service/secretsmanager/v1.34.14 : Apache License 2.0 -aws/aws-sdk-go-v2 service/ssooidc/v1.28.11 : Apache License 2.0 +aws/aws-sdk-go-v2 service/ssooidc/v1.28.11 : Apache License 2.0 -aws/aws-sdk-go-v2 service/sso/v1.24.12 : Apache License 2.0 +aws/aws-sdk-go-v2 service/sso/v1.24.12 : Apache License 2.0 -aws/aws-sdk-go-v2 service/sts/v1.33.10 : Apache License 2.0 +aws/aws-sdk-go-v2 service/sts/v1.33.10 : Apache License 2.0 -aws/aws-sdk-go-v2 v1.34.0 : Apache License 2.0 +aws/aws-sdk-go-v2 v1.36.1 : Apache License 2.0 -AzureAD/microsoft-authentication-library-for-go v1.2.2 : MIT License +AzureAD/microsoft-authentication-library-for-go 20250410-snapshot : MIT License -Azure/azure-sdk-for-go 20240522-snapshot : MIT License +AzureAD/microsoft-authentication-library-for-go v1.4.2 : MIT License -Azure/azure-sdk-for-go 20250120-snapshot : MIT License +Azure/azure-sdk-for-go 20240522-snapshot : MIT License -Azure/azure-sdk-for-go sdk/azcore/v1.17.0 : MIT License +Azure/azure-sdk-for-go 20241215-snapshot : Apache License 2.0 -Azure/azure-sdk-for-go sdk/azidentity/v1.7.0 : MIT License +Azure/azure-sdk-for-go sdk/azcore/v1.18.0 : MIT License -Azure/azure-sdk-for-go sdk/internal/v1.10.0 : MIT License +Azure/azure-sdk-for-go sdk/azidentity/v1.8.2 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0 : MIT License +Azure/azure-sdk-for-go sdk/internal/v1.11.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/compute/armcompute/v5.7.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/compute/armcompute/v5.7.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/containerservice/armcontainerservice/v4.8.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.4.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/containerservice/armcontainerservice/v4.8.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.4.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.2.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.2.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0 : MIT License -Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.6.0 : MIT License +Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0 : MIT License -Azure/azure-sdk-for-go v2.0.0-beta : Apache License 2.0 +Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.6.0 : MIT License -Azure/azure-sdk-for-go v3.0.0-beta : Apache License 2.0 +Azure/azure-sdk-for-go v2.0.0-beta : Apache License 2.0 -beorn7-perks v1.0.1 : MIT License +Azure/azure-sdk-for-go v3.0.0-beta : Apache License 2.0 -bits-and-blooms/bitset 20241228-snapshot : BSD 3-clause "New" or "Revised" License +beorn7-perks v1.0.1 : MIT License -bits-and-blooms/bitset v1.20.0 : BSD 3-clause "New" or "Revised" License +bits-and-blooms/bitset 20250923-snapshot : BSD 3-clause "New" or "Revised" License -blang-semver v4.0.0 : MIT License +bits-and-blooms/bitset v1.20.0 : BSD 3-clause "New" or "Revised" License -btree v1.1.3 : Apache License 2.0 +blang-semver v4.0.0 : MIT License -BurntSushi/toml v0.3.1 : MIT License +brunoga/deep 20250830-snapshot : Apache License 2.0 -cenkalti/backoff v4.3.0 : MIT License +brunoga/deep v1.2.4 : Apache License 2.0 -census-instrumentation/opencensus-go v0.24.0 : Apache License 2.0 +btree v1.1.3 : Apache License 2.0 -cespare/xxhash v2.3.0 : MIT License +BurntSushi/toml v0.3.1 : MIT License -client9/misspell v0.3.4 : (MIT License AND BSD 3-clause "New" or "Revised" License) +cenkalti/backoff v4.3.0 : MIT License -client-go v0.33.1 : Apache License 2.0 +census-instrumentation/opencensus-go v0.24.0 : Apache License 2.0 -client_golang 20250429-snapshot : Apache License 2.0 +cespare/xxhash v2.3.0 : MIT License -client_golang v1.22.0 : Apache License 2.0 +cli/cli v2.76.1 : MIT License -cncf/udpa 20201120-snapshot-5459f2c9 : Apache License 2.0 +client9/misspell v0.3.4 : (MIT License AND BSD 3-clause "New" or "Revised" License) -containerd/containerd v2.0.5 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) +client-go v0.34.1 : Apache License 2.0 -containerd/containerd v2.1.0 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) +client_golang 20250429-snapshot : Apache License 2.0 -container-storage-interface/spec v1.9.0 : Apache License 2.0 +client_golang v1.22.0 : Apache License 2.0 -CoreOS v0.3.1 : Apache License 2.0 +cncf/udpa 20201120-snapshot-5459f2c9 : Apache License 2.0 -diskv v2.0.1 : MIT License +containerd/containerd v2.0.5 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) -dnaeon/go-vcr v1.2.0 : BSD 2-clause "Simplified" License +containerd/containerd v2.1.3 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License) -dnaeon/go-vcr v3.2.0 : BSD 2-clause "Simplified" License +container-storage-interface/spec v1.11.0 : Apache License 2.0 -docker/buildx v0.18.0 : Apache License 2.0 +CoreOS v0.3.1 : Apache License 2.0 -docker-go-plugins-helpers 20240701-snapshot-45e24314 : Apache License 2.0 +coreos/ignition 2.21.0 : Apache License 2.0 -docker-go-units v0.5.0 : Apache License 2.0 +coreos/ignition 2.22.0 : Apache License 2.0 -dominikh/go-tools 20190523-snapshot-ea95bdfd : MIT License +dgryski/go-rendezvous 20200823-snapshot-9f7001d1 : MIT License -elastic/go-sysinfo 20250425-snapshot : Apache License 2.0 +diskv v2.0.1 : MIT License -elastic/go-windows v1.0.2 : Apache License 2.0 +dnaeon/go-vcr v1.2.0 : BSD 2-clause "Simplified" License -envoyproxy/go-control-plane envoy/v1.32.4 : Apache License 2.0 +dnaeon/go-vcr v3.2.0 : BSD 2-clause "Simplified" License -envoyproxy/go-control-plane ratelimit/v0.1.0 : Apache License 2.0 +docker/buildx v0.18.0 : Apache License 2.0 -envoyproxy/go-control-plane v0.13.4 : Apache License 2.0 +docker-go-plugins-helpers 20240701-snapshot-45e24314 : Apache License 2.0 -errcheck v1.5.0-alpha : MIT License +docker-go-units v0.5.0 : Apache License 2.0 -evanphx/json-patch v4.12.0 : BSD 3-clause "New" or "Revised" License +dominikh/go-tools 20190523-snapshot-ea95bdfd : MIT License -evanphx/json-patch v5.6.0 : BSD 3-clause "New" or "Revised" License +elastic/go-sysinfo 184688adcb6ddaa744fe787e6e6a47a95f8b5e44 : Apache License 2.0 -evanphx/json-patch v5.9.0 : BSD 3-clause "New" or "Revised" License +elastic/go-sysinfo 20250922-snapshot : Apache License 2.0 -exp 20240711-snapshot-8a7402ab : BSD 3-clause "New" or "Revised" License +elastic/go-windows v1.0.2 : Apache License 2.0 -felixge/httpsnoop v1.0.4 : MIT License +envoyproxy/go-control-plane envoy/v1.32.4 : Apache License 2.0 -fsnotify-fsnotify v1.7.0 : BSD 3-clause "New" or "Revised" License +envoyproxy/go-control-plane ratelimit/v0.1.0 : Apache License 2.0 -fxamacker/cbor 2.7.0 : Expat License +envoyproxy/go-control-plane v0.13.4 : Apache License 2.0 -gengo 20250207-snapshot-1244d319 : Apache License 2.0 +envoyproxy/protoc-gen-validate 1.2.1 : Apache License 2.0 -github.com/antlr4-go/antlr v4.13.0 : BSD 3-clause "New" or "Revised" License +errcheck v1.5.0-alpha : MIT License -github.com/aws/smithy-go 20250514-snapshot : Apache License 2.0 +etcd-io/raft v3.6.0 : Apache License 2.0 -github.com/aws/smithy-go v1.22.2 : Apache License 2.0 +evanphx/json-patch v4.12.0 : BSD 3-clause "New" or "Revised" License -github.com/cncf/xds 20250326-snapshot-ae57f3c0 : Apache License 2.0 +evanphx/json-patch v5.6.0 : BSD 3-clause "New" or "Revised" License -github.com/distribution/reference v0.6.0 : Apache License 2.0 +evanphx/json-patch v5.9.11 : BSD 3-clause "New" or "Revised" License -github.com/google/cel-spec v0.23.0 : Apache License 2.0 +exp 20250718-snapshot-645b1fa8 : BSD 3-clause "New" or "Revised" License -github.com/konsorten/go-windows-terminal-sequences v1.0.1 : MIT License +felixge/httpsnoop v1.0.4 : MIT License -github.com/kubernetes-csi/csi-lib-utils v0.16.0 : Apache License 2.0 +fsnotify-fsnotify v1.9.0 : BSD 3-clause "New" or "Revised" License -github.com/mattermost/xml-roundtrip-validator 20230502-snapshot-3079e7b8 : Apache License 2.0 +fxamacker/cbor v2.9.0 : MIT License -github.com/moby/spdystream v0.5.0 : Apache License 2.0 +gengo 20250604-snapshot-85fd79db : Apache License 2.0 -github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61 : BSD 3-clause "New" or "Revised" License +github.com/antlr4-go/antlr v4.13.0 : BSD 3-clause "New" or "Revised" License -github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b : BSD 3-clause "New" or "Revised" License +github.com/aws/smithy-go 20250514-snapshot : Apache License 2.0 -github.com/rivo/uniseg v0.1.0 : MIT License +github.com/aws/smithy-go v1.22.2 : Apache License 2.0 -github.com/xdg-go/pbkdf2 1.0.0 : Apache License 2.0 +github.com/cncf/xds 20250326-snapshot-ae57f3c0 : Apache License 2.0 -go-check-check 20201130-snapshot-10cb9826 : BSD 2-clause "Simplified" License +github.com/distribution/reference v0.6.0 : Apache License 2.0 -godebug v1.1.0 : Apache License 2.0 +github.com/google/cel-spec v0.24.0 : Apache License 2.0 -GoDoc Text v0.2.0 : MIT License +github.com/konsorten/go-windows-terminal-sequences v1.0.1 : MIT License -go-etcd api/v3.5.21 : Apache License 2.0 +github.com/kubernetes-csi/csi-lib-utils v0.16.0 : Apache License 2.0 -go-etcd client/pkg/v3.5.21 : Apache License 2.0 +github.com/mattermost/xml-roundtrip-validator 20230502-snapshot-3079e7b8 : Apache License 2.0 -go-etcd client/v2.305.21 : Apache License 2.0 +github.com/moby/spdystream v0.5.0 : Apache License 2.0 -go-etcd client/v3.5.21 : Apache License 2.0 +github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61 : BSD 3-clause "New" or "Revised" License -go-etcd pkg/v3.5.21 : Apache License 2.0 +github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b : BSD 3-clause "New" or "Revised" License -go-etcd raft/v3.5.21 : Apache License 2.0 +github.com/redis/go-redis v9.7.0 : BSD 2-clause "Simplified" License -go-etcd server/v3.5.21 : Apache License 2.0 +github.com/rivo/uniseg v0.1.0 : MIT License -go.etcd.io/bbolt v1.3.11 : MIT License +github.com/xdg-go/pbkdf2 1.0.0 : Apache License 2.0 -go-flags v1.4.0 : BSD 3-clause "New" or "Revised" License +go-check-check 20201130-snapshot-10cb9826 : BSD 2-clause "Simplified" License -go-flowrate 20140419-snapshot-cca7078d : BSD 3-clause "New" or "Revised" License +godebug v1.1.0 : Apache License 2.0 -gogo/protobuf v1.3.2 : BSD 3-clause "New" or "Revised" License +GoDoc Text v0.2.0 : MIT License -go humanize 20250512-snapshot-b48bc01a : MIT License +go-etcd api/v3.6.4 : Apache License 2.0 -go-inf-inf v0.9.1 : BSD 3-clause "New" or "Revised" License +go-etcd client/pkg/v3.6.4 : Apache License 2.0 -go-jose 4.0.5 : Apache License 2.0 +go-etcd client/v3.6.4 : Apache License 2.0 -golang/appengine v1.6.8 : Apache License 2.0 +go-etcd pkg/v3.6.4 : Apache License 2.0 -golang-github-docker-go-connections-dev 0.4.0 : Apache License 2.0 +go-etcd server/v3.6.4 : Apache License 2.0 -golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd : MIT License +go-flags v1.6.1 : BSD 3-clause "New" or "Revised" License -golang-github-ghodss-yaml-dev 20240620-snapshot : MIT License +go-flowrate 20140419-snapshot-cca7078d : BSD 3-clause "New" or "Revised" License -golang-github-googleapis-gax-go-dev 2.13.0 : BSD 3-clause "New" or "Revised" License +gogo/protobuf v1.3.2 : BSD 3-clause "New" or "Revised" License -golang-github-spf13-pflag-dev v1.0.5 : BSD 3-clause "New" or "Revised" License +go humanize 20250512-snapshot-b48bc01a : MIT License -golang/glog v1.2.4 : Apache License 2.0 +go-inf-inf v0.9.1 : BSD 3-clause "New" or "Revised" License -golang-jwt/jwt v4.5.2 : MIT License +go-jose 4.0.5 : Apache License 2.0 -golang-jwt/jwt v5.2.2 : MIT License +golang/appengine v1.6.8 : Apache License 2.0 -golang-mock v1.6.0 : Apache License 2.0 +golang-github-docker-go-connections-dev 0.4.0 : Apache License 2.0 -golang.org/x/crypto v0.39.0 : BSD 3-clause "New" or "Revised" License +golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd : MIT License -golang.org/x/lint 20190308-snapshot-d0100b6b : BSD 3-clause "New" or "Revised" License +golang-github-ghodss-yaml-dev 20240620-snapshot : MIT License -golang.org/x/mod v0.25.0 : BSD 3-clause "New" or "Revised" License +golang-github-googleapis-gax-go-dev 2.13.0 : BSD 3-clause "New" or "Revised" License -golang.org/x/net 20250404-snapshot : BSD 3-clause "New" or "Revised" License +golang-github-spf13-pflag-dev v1.0.7 : BSD 3-clause "New" or "Revised" License -golang.org/x/net 20250607-snapshot : BSD 3-clause "New" or "Revised" License +golang/glog v1.2.4 : Apache License 2.0 -golang.org/x/net v0.41.0 : BSD 3-clause "New" or "Revised" License +golang-jwt/jwt v5.2.2 : MIT License -golang.org/x/oauth2 v0.28.0 : BSD 3-clause "New" or "Revised" License +golang-mock v1.6.0 : Apache License 2.0 -golang.org/x/sys 20250608-snapshot : BSD 3-clause "New" or "Revised" License +golang.org/x/crypto v0.40.0 : BSD 3-clause "New" or "Revised" License -golang.org/x/sys v0.33.0 : BSD 3-clause "New" or "Revised" License +golang.org/x/lint 20190308-snapshot-d0100b6b : BSD 3-clause "New" or "Revised" License -golang.org/x/term v0.32.0 : BSD 3-clause "New" or "Revised" License +golang.org/x/mod v0.26.0 : BSD 3-clause "New" or "Revised" License -golang.org/x/time v0.9.0 : BSD 3-clause "New" or "Revised" License +golang.org/x/net 20250827-snapshot : BSD 3-clause "New" or "Revised" License -golang.org/x/tools 20250611-snapshot : BSD 3-clause "New" or "Revised" License +golang.org/x/net v0.42.0 : BSD 3-clause "New" or "Revised" License -golang.org/x/tools v0.33.0 : BSD 3-clause "New" or "Revised" License +golang.org/x/oauth2 v0.30.0 : BSD 3-clause "New" or "Revised" License -golang.org/x/xerrors 20200804-snapshot-5ec99f83 : BSD 3-clause "New" or "Revised" License +golang.org/x/sys 20250923-snapshot : BSD 3-clause "New" or "Revised" License -Golang Protobuf 20240812-snapshot : BSD 3-clause "New" or "Revised" License +golang.org/x/sys v0.37.0 : BSD 3-clause "New" or "Revised" License -Golang Protobuf v1.36.6 : BSD 3-clause "New" or "Revised" License +golang.org/x/term v0.33.0 : BSD 3-clause "New" or "Revised" License -Golang Protobuf v1.5.4 : BSD 3-clause "New" or "Revised" License +golang.org/x/time v0.11.0 : BSD 3-clause "New" or "Revised" License -golang-set 20250321-snapshot : MIT License +golang.org/x/tools v0.35.0 : BSD 3-clause "New" or "Revised" License -golang-snappy-go-dev v0.0.4 : BSD 3-clause "New" or "Revised" License +golang.org/x/xerrors 20200804-snapshot-5ec99f83 : BSD 3-clause "New" or "Revised" License -golang-stats v0.7.0 : MIT License +Golang Protobuf 20250505-snapshot : BSD 3-clause "New" or "Revised" License -golang/sync 20250607-snapshot : BSD 3-clause "New" or "Revised" License +Golang Protobuf v1.36.6 : BSD 3-clause "New" or "Revised" License -golang/sync v0.15.0 : BSD 3-clause "New" or "Revised" License +Golang Protobuf v1.5.4 : BSD 3-clause "New" or "Revised" License -golang/telemetry 20240517-snapshot-bda55230 : BSD 3-clause "New" or "Revised" License +golang-set 20250321-snapshot : MIT License -golang/text 20240806-snapshot : BSD 3-clause "New" or "Revised" License +golang-set v2.8.0 : MIT License -golang/text v0.26.0 : BSD 3-clause "New" or "Revised" License +golang-snappy-go-dev v0.0.4 : BSD 3-clause "New" or "Revised" License -golang/tools v0.33.0 : BSD 3-clause "New" or "Revised" License +golang-stats v0.7.1 : MIT License -go-logr/logr v1.4.2 : Apache License 2.0 +golang/sync v0.16.0 : BSD 3-clause "New" or "Revised" License -go-logr/stdr v1.2.2 : Apache License 2.0 +golang/telemetry 20250709-snapshot-8d8967af : BSD 3-clause "New" or "Revised" License -gomega v1.35.1 : MIT License +golang/text 20240806-snapshot : BSD 3-clause "New" or "Revised" License -googleapis/enterprise-certificate-proxy 0.3.4 : Apache License 2.0 +golang/text v0.28.0 : BSD 3-clause "New" or "Revised" License -googleapis/gax-go v2.14.1 : BSD 3-clause "New" or "Revised" License +go-logr/logr v1.4.2 : Apache License 2.0 -googleapis/go-genproto 20250115-snapshot-1a7da9e5 : Apache License 2.0 +go-logr/stdr v1.2.2 : Apache License 2.0 -googleapis/go-genproto 20250122-snapshot-138b5a5a : Apache License 2.0 +gomega v1.35.1 : MIT License -googleapis/go-genproto 20250324-snapshot-b45e905d : Apache License 2.0 +googleapis/enterprise-certificate-proxy v0.3.6 : Apache License 2.0 -googleapis/go-genproto 20250603-snapshot-513f2392 : Apache License 2.0 +googleapis/gax-go v2.14.2 : BSD 3-clause "New" or "Revised" License -googleapis/go-genproto 20250604-snapshot : Apache License 2.0 +googleapis/go-genproto 20250505-snapshot-f936aa4a : Apache License 2.0 -googleapis/google-api-go-client 20250505-snapshot : BSD 3-clause "New" or "Revised" License +googleapis/go-genproto 20250512-snapshot-5a2f75b7 : Apache License 2.0 -googleapis/google-api-go-client v0.218.0 : BSD 3-clause "New" or "Revised" License +googleapis/go-genproto 20250603-snapshot-513f2392 : Apache License 2.0 -google/cel-go v0.23.2 : Apache License 2.0 +googleapis/google-api-go-client 20250917-snapshot : BSD 3-clause "New" or "Revised" License -google-cloud-go accessapproval/v1.8.3 : Apache License 2.0 +googleapis/google-api-go-client v0.234.0 : BSD 3-clause "New" or "Revised" License -google-cloud-go accesscontextmanager/v1.9.3 : Apache License 2.0 +google/cel-go v0.26.0 : Apache License 2.0 -google-cloud-go aiplatform/v1.70.0 : Apache License 2.0 +google-cloud-go accessapproval/v1.8.6 : Apache License 2.0 -google-cloud-go analytics/v0.25.3 : Apache License 2.0 +google-cloud-go accesscontextmanager/v1.9.6 : Apache License 2.0 -google-cloud-go apigateway/v1.7.3 : Apache License 2.0 +google-cloud-go aiplatform/v1.85.0 : Apache License 2.0 -google-cloud-go apigeeconnect/v1.7.3 : Apache License 2.0 +google-cloud-go analytics/v0.28.0 : Apache License 2.0 -google-cloud-go apigeeregistry/v0.9.3 : Apache License 2.0 +google-cloud-go apigateway/v1.7.6 : Apache License 2.0 -google-cloud-go appengine/v1.9.3 : Apache License 2.0 +google-cloud-go apigeeconnect/v1.7.6 : Apache License 2.0 -google-cloud-go area120/v0.9.3 : Apache License 2.0 +google-cloud-go apigeeregistry/v0.9.6 : Apache License 2.0 -google-cloud-go artifactregistry/v1.16.1 : Apache License 2.0 +google-cloud-go appengine/v1.9.6 : Apache License 2.0 -google-cloud-go asset/v1.20.4 : Apache License 2.0 +google-cloud-go area120/v0.9.6 : Apache License 2.0 -google-cloud-go assuredworkloads/v1.12.3 : Apache License 2.0 +google-cloud-go artifactregistry/v1.17.1 : Apache License 2.0 -google-cloud-go auth/oauth2adapt/v0.2.7 : Apache License 2.0 +google-cloud-go asset/v1.21.0 : Apache License 2.0 -google-cloud-go auth/v0.14.0 : Apache License 2.0 +google-cloud-go assuredworkloads/v1.12.6 : Apache License 2.0 -google-cloud-go automl/v1.14.4 : Apache License 2.0 +google-cloud-go auth/oauth2adapt/v0.2.8 : Apache License 2.0 -google-cloud-go baremetalsolution/v1.3.3 : Apache License 2.0 +google-cloud-go auth/v0.16.1 : Apache License 2.0 -google-cloud-go batch/v1.11.5 : Apache License 2.0 +google-cloud-go automl/v1.14.7 : Apache License 2.0 -google-cloud-go beyondcorp/v1.1.3 : Apache License 2.0 +google-cloud-go baremetalsolution/v1.3.6 : Apache License 2.0 -google-cloud-go bigquery/v1.66.0 : Apache License 2.0 +google-cloud-go batch/v1.12.2 : Apache License 2.0 -google-cloud-go bigtable/v1.34.0 : Apache License 2.0 +google-cloud-go beyondcorp/v1.1.6 : Apache License 2.0 -google-cloud-go billing/v1.20.1 : Apache License 2.0 +google-cloud-go bigquery/v1.67.0 : Apache License 2.0 -google-cloud-go binaryauthorization/v1.9.3 : Apache License 2.0 +google-cloud-go bigtable/v1.37.0 : Apache License 2.0 -google-cloud-go certificatemanager/v1.9.3 : Apache License 2.0 +google-cloud-go billing/v1.20.4 : Apache License 2.0 -google-cloud-go channel/v1.19.2 : Apache License 2.0 +google-cloud-go binaryauthorization/v1.9.5 : Apache License 2.0 -google-cloud-go cloudbuild/v1.20.0 : Apache License 2.0 +google-cloud-go certificatemanager/v1.9.5 : Apache License 2.0 -google-cloud-go clouddms/v1.8.3 : Apache License 2.0 +google-cloud-go channel/v1.19.5 : Apache License 2.0 -google-cloud-go cloudtasks/v1.13.3 : Apache License 2.0 +google-cloud-go cloudbuild/v1.22.2 : Apache License 2.0 -google-cloud-go compute/metadata/v0.6.0 : Apache License 2.0 +google-cloud-go clouddms/v1.8.7 : Apache License 2.0 -google-cloud-go compute/v1.33.0 : Apache License 2.0 +google-cloud-go cloudtasks/v1.13.6 : Apache License 2.0 -google-cloud-go contactcenterinsights/v1.17.1 : Apache License 2.0 +google-cloud-go compute/metadata/v0.7.0 : Apache License 2.0 -google-cloud-go containeranalysis/v0.13.3 : Apache License 2.0 +google-cloud-go compute/v1.38.0 : Apache License 2.0 -google-cloud-go container/v1.42.1 : Apache License 2.0 +google-cloud-go contactcenterinsights/v1.17.3 : Apache License 2.0 -google-cloud-go datacatalog/v1.24.3 : Apache License 2.0 +google-cloud-go containeranalysis/v0.14.1 : Apache License 2.0 -google-cloud-go dataflow/v0.10.3 : Apache License 2.0 +google-cloud-go container/v1.42.4 : Apache License 2.0 -google-cloud-go dataform/v0.10.3 : Apache License 2.0 +google-cloud-go datacatalog/v1.26.0 : Apache License 2.0 -google-cloud-go datafusion/v1.8.3 : Apache License 2.0 +google-cloud-go dataflow/v0.10.6 : Apache License 2.0 -google-cloud-go datalabeling/v0.9.3 : Apache License 2.0 +google-cloud-go dataform/v0.11.2 : Apache License 2.0 -google-cloud-go dataplex/v1.21.0 : Apache License 2.0 +google-cloud-go datafusion/v1.8.6 : Apache License 2.0 -google-cloud-go dataproc/v2.10.1 : Apache License 2.0 +google-cloud-go datalabeling/v0.9.6 : Apache License 2.0 -google-cloud-go dataqna/v0.9.3 : Apache License 2.0 +google-cloud-go dataplex/v1.25.2 : Apache License 2.0 -google-cloud-go datastore/v1.20.0 : Apache License 2.0 +google-cloud-go dataproc/v2.11.2 : Apache License 2.0 -google-cloud-go datastream/v1.12.1 : Apache License 2.0 +google-cloud-go dataqna/v0.9.6 : Apache License 2.0 -google-cloud-go deploy/v1.26.1 : Apache License 2.0 +google-cloud-go datastore/v1.20.0 : Apache License 2.0 -google-cloud-go dialogflow/v1.64.1 : Apache License 2.0 +google-cloud-go datastream/v1.14.1 : Apache License 2.0 -google-cloud-go dlp/v1.20.1 : Apache License 2.0 +google-cloud-go deploy/v1.27.1 : Apache License 2.0 -google-cloud-go documentai/v1.35.1 : Apache License 2.0 +google-cloud-go dialogflow/v1.68.2 : Apache License 2.0 -google-cloud-go domains/v0.10.3 : Apache License 2.0 +google-cloud-go dlp/v1.22.1 : Apache License 2.0 -google-cloud-go edgecontainer/v1.4.1 : Apache License 2.0 +google-cloud-go documentai/v1.37.0 : Apache License 2.0 -google-cloud-go errorreporting/v0.3.2 : Apache License 2.0 +google-cloud-go domains/v0.10.6 : Apache License 2.0 -google-cloud-go essentialcontacts/v1.7.3 : Apache License 2.0 +google-cloud-go edgecontainer/v1.4.3 : Apache License 2.0 -google-cloud-go eventarc/v1.15.1 : Apache License 2.0 +google-cloud-go errorreporting/v0.3.2 : Apache License 2.0 -google-cloud-go filestore/v1.9.3 : Apache License 2.0 +google-cloud-go essentialcontacts/v1.7.6 : Apache License 2.0 -google-cloud-go firestore/v1.18.0 : Apache License 2.0 +google-cloud-go eventarc/v1.15.5 : Apache License 2.0 -google-cloud-go functions/v1.19.3 : Apache License 2.0 +google-cloud-go filestore/v1.10.2 : Apache License 2.0 -google-cloud-go gkebackup/v1.6.3 : Apache License 2.0 +google-cloud-go firestore/v1.18.0 : Apache License 2.0 -google-cloud-go gkeconnect/v0.12.1 : Apache License 2.0 +google-cloud-go functions/v1.19.6 : Apache License 2.0 -google-cloud-go gkehub/v0.15.3 : Apache License 2.0 +google-cloud-go gkebackup/v1.7.0 : Apache License 2.0 -google-cloud-go gkemulticloud/v1.5.1 : Apache License 2.0 +google-cloud-go gkeconnect/v0.12.4 : Apache License 2.0 -google-cloud-go gsuiteaddons/v1.7.3 : Apache License 2.0 +google-cloud-go gkehub/v0.15.6 : Apache License 2.0 -google-cloud-go iam/v1.3.1 : Apache License 2.0 +google-cloud-go gkemulticloud/v1.5.3 : Apache License 2.0 -google-cloud-go iap/v1.10.3 : Apache License 2.0 +google-cloud-go gsuiteaddons/v1.7.7 : Apache License 2.0 -google-cloud-go ids/v1.5.3 : Apache License 2.0 +google-cloud-go iam/v1.5.2 : Apache License 2.0 -google-cloud-go iot/v1.8.3 : Apache License 2.0 +google-cloud-go iap/v1.11.1 : Apache License 2.0 -google-cloud-go kms/v1.20.5 : Apache License 2.0 +google-cloud-go ids/v1.5.6 : Apache License 2.0 -google-cloud-go language/v1.14.3 : Apache License 2.0 +google-cloud-go iot/v1.8.6 : Apache License 2.0 -google-cloud-go lifesciences/v0.10.3 : Apache License 2.0 +google-cloud-go kms/v1.21.2 : Apache License 2.0 -google-cloud-go logging/v1.13.0 : Apache License 2.0 +google-cloud-go language/v1.14.5 : Apache License 2.0 -google-cloud-go longrunning/v0.6.4 : Apache License 2.0 +google-cloud-go lifesciences/v0.10.6 : Apache License 2.0 -google-cloud-go managedidentities/v1.7.3 : Apache License 2.0 +google-cloud-go logging/v1.13.0 : Apache License 2.0 -google-cloud-go maps/v1.17.1 : Apache License 2.0 +google-cloud-go longrunning/v0.6.7 : Apache License 2.0 -google-cloud-go mediatranslation/v0.9.3 : Apache License 2.0 +google-cloud-go managedidentities/v1.7.6 : Apache License 2.0 -google-cloud-go memcache/v1.11.3 : Apache License 2.0 +google-cloud-go maps/v1.20.4 : Apache License 2.0 -google-cloud-go metastore/v1.14.3 : Apache License 2.0 +google-cloud-go mediatranslation/v0.9.6 : Apache License 2.0 -google-cloud-go monitoring/v1.23.0 : Apache License 2.0 +google-cloud-go memcache/v1.11.6 : Apache License 2.0 -google-cloud-go netapp/v1.6.0 : Apache License 2.0 +google-cloud-go metastore/v1.14.6 : Apache License 2.0 -google-cloud-go networkconnectivity/v1.16.1 : Apache License 2.0 +google-cloud-go monitoring/v1.24.2 : Apache License 2.0 -google-cloud-go networkmanagement/v1.18.0 : Apache License 2.0 +google-cloud-go netapp/v1.9.0 : Apache License 2.0 -google-cloud-go networksecurity/v0.10.3 : Apache License 2.0 +google-cloud-go networkconnectivity/v1.17.1 : Apache License 2.0 -google-cloud-go notebooks/v1.12.3 : Apache License 2.0 +google-cloud-go networkmanagement/v1.19.1 : Apache License 2.0 -google-cloud-go optimization/v1.7.3 : Apache License 2.0 +google-cloud-go networksecurity/v0.10.6 : Apache License 2.0 -google-cloud-go orchestration/v1.11.4 : Apache License 2.0 +google-cloud-go notebooks/v1.12.6 : Apache License 2.0 -google-cloud-go orgpolicy/v1.14.2 : Apache License 2.0 +google-cloud-go optimization/v1.7.6 : Apache License 2.0 -google-cloud-go osconfig/v1.14.3 : Apache License 2.0 +google-cloud-go orchestration/v1.11.9 : Apache License 2.0 -google-cloud-go oslogin/v1.14.3 : Apache License 2.0 +google-cloud-go orgpolicy/v1.15.0 : Apache License 2.0 -google-cloud-go phishingprotection/v0.9.3 : Apache License 2.0 +google-cloud-go osconfig/v1.14.5 : Apache License 2.0 -google-cloud-go policytroubleshooter/v1.11.3 : Apache License 2.0 +google-cloud-go oslogin/v1.14.6 : Apache License 2.0 -google-cloud-go privatecatalog/v0.10.4 : Apache License 2.0 +google-cloud-go phishingprotection/v0.9.6 : Apache License 2.0 -google-cloud-go pubsublite/v1.8.2 : Apache License 2.0 +google-cloud-go policytroubleshooter/v1.11.6 : Apache License 2.0 -google-cloud-go pubsub/v1.45.3 : Apache License 2.0 +google-cloud-go privatecatalog/v0.10.7 : Apache License 2.0 -google-cloud-go recaptchaenterprise/v2.19.4 : Apache License 2.0 +google-cloud-go pubsublite/v1.8.2 : Apache License 2.0 -google-cloud-go recommendationengine/v0.9.3 : Apache License 2.0 +google-cloud-go pubsub/v1.49.0 : Apache License 2.0 -google-cloud-go recommender/v1.13.3 : Apache License 2.0 +google-cloud-go recaptchaenterprise/v2.20.4 : Apache License 2.0 -google-cloud-go redis/v1.17.3 : Apache License 2.0 +google-cloud-go recommendationengine/v0.9.6 : Apache License 2.0 -google-cloud-go resourcemanager/v1.10.3 : Apache License 2.0 +google-cloud-go recommender/v1.13.5 : Apache License 2.0 -google-cloud-go resourcesettings/v1.8.3 : Apache License 2.0 +google-cloud-go redis/v1.18.2 : Apache License 2.0 -google-cloud-go retail/v1.19.2 : Apache License 2.0 +google-cloud-go resourcemanager/v1.10.6 : Apache License 2.0 -google-cloud-go run/v1.8.1 : Apache License 2.0 +google-cloud-go resourcesettings/v1.8.3 : Apache License 2.0 -google-cloud-go scheduler/v1.11.3 : Apache License 2.0 +google-cloud-go retail/v1.20.0 : Apache License 2.0 -google-cloud-go secretmanager/v1.14.3 : Apache License 2.0 +google-cloud-go run/v1.9.3 : Apache License 2.0 -google-cloud-go securitycenter/v1.35.3 : Apache License 2.0 +google-cloud-go scheduler/v1.11.7 : Apache License 2.0 -google-cloud-go security/v1.18.3 : Apache License 2.0 +google-cloud-go secretmanager/v1.14.7 : Apache License 2.0 -google-cloud-go servicedirectory/v1.12.3 : Apache License 2.0 +google-cloud-go securitycenter/v1.36.2 : Apache License 2.0 -google-cloud-go shell/v1.8.3 : Apache License 2.0 +google-cloud-go security/v1.18.5 : Apache License 2.0 -google-cloud-go spanner/v1.73.0 : Apache License 2.0 +google-cloud-go servicedirectory/v1.12.6 : Apache License 2.0 -google-cloud-go speech/v1.26.0 : Apache License 2.0 +google-cloud-go shell/v1.8.6 : Apache License 2.0 -google-cloud-go storagetransfer/v1.12.1 : Apache License 2.0 +google-cloud-go spanner/v1.80.0 : Apache License 2.0 -google-cloud-go storage/v1.50.0 : Apache License 2.0 +google-cloud-go speech/v1.27.1 : Apache License 2.0 -google-cloud-go talent/v1.8.0 : Apache License 2.0 +google-cloud-go storagetransfer/v1.12.4 : Apache License 2.0 -google-cloud-go texttospeech/v1.11.0 : Apache License 2.0 +google-cloud-go storage/v1.52.0 : Apache License 2.0 -google-cloud-go tpu/v1.8.0 : Apache License 2.0 +google-cloud-go talent/v1.8.3 : Apache License 2.0 -google-cloud-go trace/v1.11.3 : Apache License 2.0 +google-cloud-go texttospeech/v1.12.1 : Apache License 2.0 -google-cloud-go translate/v1.12.3 : Apache License 2.0 +google-cloud-go tpu/v1.8.3 : Apache License 2.0 -google-cloud-go v0.118.1 : Apache License 2.0 +google-cloud-go trace/v1.11.6 : Apache License 2.0 -google-cloud-go videointelligence/v1.12.3 : Apache License 2.0 +google-cloud-go translate/v1.12.5 : Apache License 2.0 -google-cloud-go video/v1.23.3 : Apache License 2.0 +google-cloud-go v0.121.0 : Apache License 2.0 -google-cloud-go vision/v2.9.3 : Apache License 2.0 +google-cloud-go videointelligence/v1.12.6 : Apache License 2.0 -google-cloud-go vmmigration/v1.8.3 : Apache License 2.0 +google-cloud-go video/v1.23.5 : Apache License 2.0 -google-cloud-go vmwareengine/v1.3.3 : Apache License 2.0 +google-cloud-go vision/v2.9.5 : Apache License 2.0 -google-cloud-go vpcaccess/v1.8.3 : Apache License 2.0 +google-cloud-go vmmigration/v1.8.6 : Apache License 2.0 -google-cloud-go webrisk/v1.10.3 : Apache License 2.0 +google-cloud-go vmwareengine/v1.3.5 : Apache License 2.0 -google-cloud-go websecurityscanner/v1.7.3 : Apache License 2.0 +google-cloud-go vpcaccess/v1.8.6 : Apache License 2.0 -google-cloud-go workflows/v1.13.3 : Apache License 2.0 +google-cloud-go webrisk/v1.11.1 : Apache License 2.0 -GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.27.0 : Apache License 2.0 +google-cloud-go websecurityscanner/v1.7.6 : Apache License 2.0 -GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.49.0 : Apache License 2.0 +google-cloud-go workflows/v1.14.2 : Apache License 2.0 -GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.49.0 : Apache License 2.0 +GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.27.0 : Apache License 2.0 -GoogleCloudPlatform/osconfig 20241004.00 : Apache License 2.0 +GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.51.0 : Apache License 2.0 -google/gnostic-models v0.6.9 : Apache License 2.0 +GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.51.0 : Apache License 2.0 -google/go-cmp v0.7.0 : BSD 3-clause "New" or "Revised" License +GoogleCloudPlatform/osconfig 20241004.00 : Apache License 2.0 -google-gofuzz v1.2.0 : Apache License 2.0 +google/gnostic-models v0.7.0 : Apache License 2.0 -google/go-pkcs11 v0.3.0 : Apache License 2.0 +google/go-cmp v0.7.0 : BSD 3-clause "New" or "Revised" License -google/pprof 20241029-snapshot-d1b30feb : Apache License 2.0 +google-gofuzz v1.2.0 : Apache License 2.0 -google/s2a-go v0.1.9 : Apache License 2.0 +google/go-pkcs11 v0.3.0 : Apache License 2.0 -Googleuuid v1.6.0 : BSD 3-clause "New" or "Revised" License +google/pprof 20241029-snapshot-d1b30feb : Apache License 2.0 -go-openapi/analysis v0.23.0 : Apache License 2.0 +google/s2a-go v0.1.9 : Apache License 2.0 -go-openapi/errors v0.22.0 : Apache License 2.0 +Googleuuid v1.6.0 : BSD 3-clause "New" or "Revised" License -go-openapi/jsonpointer v0.21.0 : Apache License 2.0 +go-openapi/analysis v0.23.0 : Apache License 2.0 -go-openapi/loads v0.22.0 : Apache License 2.0 +go-openapi/errors v0.22.1 : Apache License 2.0 -go-openapi/runtime v0.28.0 : Apache License 2.0 +go-openapi/jsonpointer v0.21.1 : Apache License 2.0 -go-openapi/spec v0.21.0 : Apache License 2.0 +go-openapi/loads v0.22.0 : Apache License 2.0 -go-openapi/validate v0.24.0 : Apache License 2.0 +go-openapi/runtime v0.28.0 : Apache License 2.0 -go.opentelemetry.io/proto otlp/v1.4.0 : Apache License 2.0 +go-openapi/spec v0.21.0 : Apache License 2.0 -go-plist v1.0.1 : (BSD 3-clause "New" or "Revised" License OR BSD 2-Clause with views sentence) +go-openapi/validate v0.24.0 : Apache License 2.0 -go-restful v3.11.0 : MIT License +go.opentelemetry.io/proto otlp/v1.5.0 : Apache License 2.0 -gorilla/mux v1.8.1 : BSD 3-clause "New" or "Revised" License +go-plist v1.0.1 : (BSD 3-clause "New" or "Revised" License AND BSD 2-Clause with views sentence) -gorilla/websocket 20250226-snapshot-e064f32e : BSD 2-clause "Simplified" License +go-restful v3.12.2 : MIT License -gorilla/websocket 20250408-snapshot : BSD 2-clause "Simplified" License +gorilla/mux v1.8.1 : BSD 3-clause "New" or "Revised" License -go-spew 20180930-snapshot-d8f796af : ISC License +gorilla/websocket 20250226-snapshot-e064f32e : BSD 2-clause "Simplified" License -go-systemd 20190321-snapshot-95778dfb : Apache License 2.0 +go-spew 20180930-snapshot-d8f796af : ISC License -go-systemd v22.5.0 : Apache License 2.0 +go-systemd 20191104-snapshot-d3cd4ed1 : Apache License 2.0 -go-task/slim-sprig v3.0.0 : MIT License +go-systemd v22.5.0 : Apache License 2.0 -Go Testify v1.10.0 : MIT License +go-task/slim-sprig v3.0.0 : MIT License -go.uber.org/goleak v1.3.0 : MIT License +Go Testify 20250907-snapshot : MIT License -go.uber.org/multierr v1.11.0 : MIT License +Go Testify v1.11.1 : MIT License -govalidator 20230301-snapshot-a9d515a0 : MIT License +go.uber.org/goleak v1.3.0 : MIT License -go-zap v1.27.0 : MIT License +go.uber.org/multierr v1.11.0 : MIT License -Grafana 10.2.6 : GNU Affero General Public License v3.0 +govalidator 20230301-snapshot-a9d515a0 : MIT License -gregjones/httpcache 20190611-snapshot-901d9072 : MIT License +go.yaml.in/yaml/v2 v2.4.2 : MIT License -groupcache 20210331-snapshot-41bb18bf : Apache License 2.0 +go.yaml.in/yaml/v2 v3.0.4 : MIT License -grpc-ecosystem/go-grpc-middleware v1.3.0 : Apache License 2.0 +go-zap v1.27.0 : MIT License -grpc-ecosystem/go-grpc-prometheus v1.2.0 : Apache License 2.0 +gregjones/httpcache 20190611-snapshot-901d9072 : MIT License -grpc-gateway v1.16.0 : BSD 3-clause "New" or "Revised" License +groupcache 20210331-snapshot-41bb18bf : Apache License 2.0 -grpc-gateway v2.24.0 : BSD 3-clause "New" or "Revised" License +grpc-ecosystem/go-grpc-middleware providers/prometheus/v1.0.1 : Apache License 2.0 -grpc-go 20250607-snapshot : Apache License 2.0 +grpc-ecosystem/go-grpc-middleware v2.3.0 : Apache License 2.0 -grpc-go 20250610-snapshot : Apache License 2.0 +grpc-ecosystem/go-grpc-prometheus v1.2.0 : Apache License 2.0 -grpc-go v1.73.0 : Apache License 2.0 +grpc-gateway v2.26.3 : BSD 3-clause "New" or "Revised" License -helm/helm v3.18.1 : Apache License 2.0 +grpc-go 20250721-snapshot : Apache License 2.0 -inconshreveable/mousetrap v1.1.0 : Apache License 2.0 +grpc-go v1.73.0 : Apache License 2.0 -jarcoal/httpmock v1.3.1 : MIT License +helm/helm v3.19.0 : Apache License 2.0 -jmespath-go-jmespath v0.4.0 : Apache License 2.0 +inconshreveable/mousetrap v1.1.0 : Apache License 2.0 -jonboulle-clockwork v0.4.0 : Apache License 2.0 +jarcoal/httpmock v1.4.0 : MIT License -josharian/intern v1.0.0 : MIT License +jonboulle-clockwork v0.5.0 : Apache License 2.0 -jpillora-backoff 1.0.0 : MIT License +josharian/intern v1.0.0 : MIT License -jsoniter-go v1.1.12 : MIT License +jpillora-backoff 1.0.0 : MIT License -jsonreference v0.21.0 : Apache License 2.0 +jsoniter-go v1.1.12 : MIT License -julienschmidt/httprouter v1.3.0 : BSD 3-clause "New" or "Revised" License +jsonreference v0.21.0 : Apache License 2.0 -k8s.io/code-generator v0.33.1 : Apache License 2.0 +julienschmidt/httprouter v1.3.0 : BSD 3-clause "New" or "Revised" License -k8s.io/klog 2.130.1 : Apache License 2.0 +k8s.io/code-generator v0.34.1 : Apache License 2.0 -k8s.io/kube-openapi 20250318-snapshot-c8a335a9 : Apache License 2.0 +k8s.io/klog 2.130.1 : Apache License 2.0 -k8s.io/utils 20241210-snapshot-24370bea : Apache License 2.0 +k8s.io/kube-openapi 20250710-snapshot-f3f2b991 : Apache License 2.0 -k8s.io/utils 20250526-snapshot : Apache License 2.0 +k8s.io/kube-openapi 20250910-snapshot : Apache License 2.0 -kisielk-gotool v1.0.0 : (MIT License AND BSD 3-clause "New" or "Revised" License) +k8s.io/utils 20250324-snapshot-4c0f3b24 : Apache License 2.0 -klauspost-compress v1.18.0 : BSD 3-clause "New" or "Revised" License +k8s.io/utils 20250918-snapshot : Apache License 2.0 -kr/pretty v0.3.1 : MIT License +keybase/go-keychain 20231219-snapshot-57a3676c : MIT License -Kubernetes v1.33.0-alpha.1 : Apache License 2.0 +kisielk-gotool v1.0.0 : (MIT License AND BSD 3-clause "New" or "Revised" License) -Kubernetes v1.33.0-rc.1 : Apache License 2.0 +klauspost-compress v1.18.0 : BSD 3-clause "New" or "Revised" License -kubernetes/api 20250603-snapshot : Apache License 2.0 +kr/pretty v0.3.1 : MIT License -kubernetes/api v0.33.1 : Apache License 2.0 +Kubernetes v1.34.0-alpha.1 : Apache License 2.0 -kubernetes/apiextensions-apiserver 20250507-snapshot : Apache License 2.0 +Kubernetes v1.34.0-rc.1 : Apache License 2.0 -kubernetes/apiextensions-apiserver v0.33.1 : Apache License 2.0 +kubernetes/api 20250901-snapshot : Apache License 2.0 -kubernetes/apimachinery v0.33.1 : Apache License 2.0 +kubernetes/api v0.34.1 : Apache License 2.0 -kubernetes/apiserver v0.33.1 : Apache License 2.0 +kubernetes/apiextensions-apiserver 20250919-snapshot : Apache License 2.0 -kubernetes/component-base v0.33.1 : Apache License 2.0 +kubernetes/apiextensions-apiserver v0.34.1 : Apache License 2.0 -kubernetes-csi/csi-proxy client/v1.1.3 : Apache License 2.0 +kubernetes/apimachinery 20250908-snapshot : Apache License 2.0 -kubernetes-csi/external-snapshotter 20250130-snapshot : Apache License 2.0 +kubernetes/apimachinery 20250919-snapshot : Apache License 2.0 -kubernetes-csi/external-snapshotter client/v8.2.0 : Apache License 2.0 +kubernetes/apimachinery v0.34.1 : Apache License 2.0 -kubernetes/kms v0.33.1 : Apache License 2.0 +kubernetes/apiserver v0.34.1 : Apache License 2.0 -kubernetes/mount-utils v0.32.1 : Apache License 2.0 +kubernetes/component-base v0.34.1 : Apache License 2.0 -kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.2 : Apache License 2.0 +kubernetes-csi/csi-proxy client/v1.2.1 : Apache License 2.0 -kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.0.50 : Apache License 2.0 +kubernetes-csi/external-snapshotter 20250130-snapshot : Apache License 2.0 -kubernetes-sigs/structured-merge-diff v4.6.0 : Apache License 2.0 +kubernetes-csi/external-snapshotter 20250707-snapshot : Apache License 2.0 -mailru/easyjson v0.7.7 : MIT License +kubernetes-csi/external-snapshotter client/v8.2.0 : Apache License 2.0 -mapstructure v1.5.0 : MIT License +kubernetes/kms v0.34.1 : Apache License 2.0 -martian v3.3.3 : Apache License 2.0 +kubernetes/mount-utils v0.32.1 : Apache License 2.0 -mattn-go-runewidth v0.0.10 : MIT License +kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.2 : Apache License 2.0 -matttproud-golang_protobuf_extensions v1.0.4 : Apache License 2.0 +kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.0.50 : Apache License 2.0 -mendersoftware/mendertesting 0.0~git20200227.1396c95 : Apache License 2.0 +kubernetes-sigs/structured-merge-diff 20250925-snapshot : Apache License 2.0 -Microsoft-go-winio v0.6.0 : MIT License +kubernetes-sigs/structured-merge-diff v4.4.2 : Apache License 2.0 -mitchellh-copystructure v1.2.0 : MIT License +kubernetes-sigs/structured-merge-diff v6.3.0 : Apache License 2.0 -mitchellh-hashstructure v2.0.2 : MIT License +mailru/easyjson v0.9.0 : MIT License -mitchellh-reflectwalk v1.0.2 : MIT License +mapstructure v1.5.0 : MIT License -moby/sys mountinfo/v0.7.2 : Apache License 2.0 +martian v3.3.3 : Apache License 2.0 -moby/sys userns/v0.1.0 : Apache License 2.0 +mattn-go-runewidth v0.0.10 : MIT License -modern-go/concurrent 20180305-snapshot-bacd9c7e : Apache License 2.0 +matttproud-golang_protobuf_extensions v1.0.4 : Apache License 2.0 -modern-go/reflect2 v1.0.2 : Apache License 2.0 +mendersoftware/mendertesting 0.0~git20200227.1396c95 : Apache License 2.0 -mongodb/mongo-go-driver v1.14.0 : Apache License 2.0 +Microsoft-go-winio v0.6.0 : MIT License -mschoch/smat v0.2.0 : Apache License 2.0 +mitchellh-hashstructure v2.0.2 : MIT License -mwitkow/go-conntrack 20190716-snapshot-2f068394 : Apache License 2.0 +moby/sys mountinfo/v0.7.2 : Apache License 2.0 -natefinch/lumberjack v2.2.1 : MIT License +moby/sys userns/v0.1.0 : Apache License 2.0 -NetApp/trident 20250601-snapshot-5a39b5b5 : Apache License 2.0 +modern-go/concurrent 20180305-snapshot-bacd9c7e : Apache License 2.0 -NetApp/trident v25.02.0 : Apache License 2.0 +modern-go/reflect2 20250322-snapshot-35a7c28c : Apache License 2.0 -niemeyer/pretty 20200227-snapshot-a10e7cae : MIT License +mongodb/mongo-go-driver v1.17.4 : Apache License 2.0 -NYTimes-gziphandler v1.1.1 : Apache License 2.0 +mschoch/smat v0.2.0 : Apache License 2.0 -oklog/ulid v1.3.1 : Apache License 2.0 +mwitkow/go-conntrack 20190716-snapshot-2f068394 : Apache License 2.0 -olekukonko-tablewriter 20230925-snapshot-df64c4bb : MIT License +natefinch/lumberjack v2.2.1 : MIT License -onsi/ginkgo 2.21.0 : MIT License +NetApp/trident 20251006-snapshot-47d3bc71 : Apache License 2.0 -OpenCensus 0.2.1 : Apache License 2.0 +NetApp/trident v24.10.0 : Apache License 2.0 -opencontainers/go-digest 1.0.0 : Apache License 2.0 +niemeyer/pretty 20200227-snapshot-a10e7cae : MIT License -open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.33.0 : Apache License 2.0 +NYTimes-gziphandler v1.1.1 : Apache License 2.0 -open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.33.0 : Apache License 2.0 +oklog/ulid v1.3.1 : Apache License 2.0 -open-telemetry/opentelemetry-go metric/v1.35.0 : Apache License 2.0 +olekukonko-tablewriter v0.0.5 : MIT License -open-telemetry/opentelemetry-go sdk/metric/v1.35.0 : Apache License 2.0 +onsi/ginkgo 2.21.0 : MIT License -open-telemetry/opentelemetry-go sdk/v1.35.0 : Apache License 2.0 +OpenCensus 0.2.1 : Apache License 2.0 -open-telemetry/opentelemetry-go trace/v1.35.0 : Apache License 2.0 +opencontainers/go-digest 1.0.0 : Apache License 2.0 -open-telemetry/opentelemetry-go v1.35.0 : Apache License 2.0 +openshift/api 20250530-snapshot-e041b5ef : Apache License 2.0 -open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.35.0 : Apache License 2.0 +open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.34.0 : Apache License 2.0 -open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.58.0 : Apache License 2.0 +open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.34.0 : Apache License 2.0 -open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.58.0 : Apache License 2.0 +open-telemetry/opentelemetry-go metric/v1.35.0 : Apache License 2.0 -open-telemetry/opentelemetry-go-instrumentation sdk/v1.1.0 : Apache License 2.0 +open-telemetry/opentelemetry-go sdk/metric/v1.35.0 : Apache License 2.0 -opentracing-opentracing-go v1.2.0 : Apache License 2.0 +open-telemetry/opentelemetry-go sdk/v1.35.0 : Apache License 2.0 -osbuild-osbuild-composer 126 : Apache License 2.0 +open-telemetry/opentelemetry-go trace/v1.35.0 : Apache License 2.0 -pkg/browser 20240102-snapshot-5ac0b6a4 : BSD 2-clause "Simplified" License +open-telemetry/opentelemetry-go v1.35.0 : Apache License 2.0 -pkg/errors v0.9.1 : BSD 2-clause "Simplified" License +open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.35.0 : Apache License 2.0 -pmezard-go-difflib 20190219-snapshot-5d4384ee : Apache License 2.0 +open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.60.0 : Apache License 2.0 -podman 5.2.1 : (MIT License AND BSD 2-clause "Simplified" License AND ISC License AND Apache License 2.0 AND BSD 3-clause "New" or "Revised" License AND Mozilla Public License 2.0) +open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.60.0 : Apache License 2.0 -prometheus-client_model v0.6.1 : Apache License 2.0 +open-telemetry/opentelemetry-go-instrumentation sdk/v1.1.0 : Apache License 2.0 -prometheus-common v0.62.0 : Apache License 2.0 +opentracing-opentracing-go v1.2.0 : Apache License 2.0 -prometheus-procfs v0.15.1 : Apache License 2.0 +osbuild-osbuild-composer 126 : Apache License 2.0 -RoaringBitmap-roaring v2.4.2 : Apache License 2.0 +osbuild-osbuild-composer 143 : Apache License 2.0 -rogpeppe/go-internal v1.13.1 : BSD 3-clause "New" or "Revised" License +pkg/browser 20240102-snapshot-5ac0b6a4 : BSD 2-clause "Simplified" License -rs-xid v1.6.0 : MIT License +pkg/errors v0.9.1 : BSD 2-clause "Simplified" License -secureheader v0.2.0 : MIT License +pmezard-go-difflib 20190219-snapshot-5d4384ee : Apache License 2.0 -sigs.k8s.io/json 20241010-snapshot-9aa6b5e7 : Apache License 2.0 +prometheus-client_model v0.6.1 : Apache License 2.0 -sigs.k8s.io/randfill v1.0.0 : Apache License 2.0 +prometheus-common v0.62.0 : Apache License 2.0 -sigs.k8s.io/yaml v1.4.0 : Apache License 2.0 +prometheus-procfs v0.16.1 : Apache License 2.0 -Sirupsen/logrus v1.9.3 : MIT License +RoaringBitmap-roaring v2.5.0 : Apache License 2.0 -soheilhy/cmux v0.1.5 : Apache License 2.0 +rogpeppe/go-internal v1.14.1 : BSD 3-clause "New" or "Revised" License -spf13-afero 20250326-snapshot : Apache License 2.0 +rs-xid v1.6.0 : MIT License -spf13-afero v1.14.0 : Apache License 2.0 +runc v1.3.0 : Apache License 2.0 -spf13-cobra 1.8.1 : Apache License 2.0 +securego/gosec v2.22.7 : Apache License 2.0 -spiffe/go-spiffe v2.5.0 : Apache License 2.0 +secureheader v0.2.0 : MIT License -stoewer/go-strcase v1.3.0 : MIT License +sigs.k8s.io/json 20241014-snapshot-cfa47c3a : Apache License 2.0 -stretchr/objx v0.5.2 : MIT License +sigs.k8s.io/randfill v1.0.0 : Apache License 2.0 -strfmt v0.23.0 : Apache License 2.0 +sigs.k8s.io/yaml v1.6.0 : Apache License 2.0 -swag v0.23.0 : Apache License 2.0 +Sirupsen/logrus v1.9.3 : MIT License -tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3 : MIT License +soheilhy/cmux v0.1.5 : Apache License 2.0 -uber-go/mock v0.5.0-patch : Apache License 2.0 +spf13-afero 20250922-snapshot : Apache License 2.0 -VictoriaMetrics v1.111.0 : Apache License 2.0 +spf13-afero v1.15.0 : Apache License 2.0 -vishvananda-netlink v1.3.0 : Apache License 2.0 +spf13-cobra 1.9.1 : Apache License 2.0 -vishvananda-netns v0.0.5 : Apache License 2.0 +spiffe/go-spiffe v2.5.0 : Apache License 2.0 -x448/float16 v0.8.4 : MIT License +stoewer/go-strcase v1.3.0 : MIT License -xdg-go/scram v1.1.2 : Apache License 2.0 +stretchr/objx v0.5.2 : MIT License -xdg-go/stringprep v1.0.4 : Apache License 2.0 +strfmt v0.23.0 : Apache License 2.0 -xhit/go-str2duration v2.1.0 : BSD 3-clause "New" or "Revised" License +swag v0.23.1 : Apache License 2.0 -xiang90-probing 20221125-snapshot-a49e3df8 : MIT License +tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3 : MIT License -yaml for Go 20141213-snapshot-9f9df343 : (MIT License AND Apache License 2.0) +VictoriaMetrics v1.111.0 : Apache License 2.0 -yaml for Go v2.4.0 : Apache License 2.0 +VictoriaMetrics v1.112.0 : Apache License 2.0 -yaml for Go v3.0.1 : (MIT License AND Apache License 2.0) +vishvananda-netlink v1.3.1 : Apache License 2.0 -youmark/pkcs8 20181117-snapshot-1be2e3e5 : MIT License +vishvananda-netns v0.0.5 : Apache License 2.0 -yuin/goldmark v1.4.13 : MIT License +x448/float16 v0.8.4 : MIT License -zcalusic/sysinfo v1.1.3 : MIT License +xdg-go/scram v1.1.2 : Apache License 2.0 -zeebo/errs v1.4.0 : MIT License +xdg-go/stringprep v1.0.4 : Apache License 2.0 +xhit/go-str2duration v2.1.0 : BSD 3-clause "New" or "Revised" License -Licenses: + +xiang90-probing 20221125-snapshot-a49e3df8 : MIT License + + +yaml for Go 20141213-snapshot-9f9df343 : (MIT License AND Apache License 2.0) + + +yaml for Go v2.4.0 : Apache License 2.0 + + +yaml for Go v3.0.1 : (MIT License AND Apache License 2.0) + + +youmark/pkcs8 20240726-snapshot-a2c0da24 : MIT License + + +yuin/goldmark v1.4.13 : MIT License + + +zcalusic/sysinfo 20250716-snapshot : MIT License + + +zcalusic/sysinfo v1.1.3 : MIT License + + +zeebo/errs v1.4.0 : MIT License + + + +Licenses: Apache License 2.0 -(aws/aws-sdk-go-v2 20250128-snapshot, aws/aws-sdk-go-v2 config/v1.29.2, aws/aws-sdk-go-v2 credentials/v1.17.55, aws/aws-sdk-go-v2 feature/ec2/imds/v1.16.25, aws/aws-sdk-go-v2 internal/configsources/v1.3.29, aws/aws-sdk-go-v2 internal/endpoints/v2.6.29, aws/aws-sdk-go-v2 internal/ini/v1.8.2, aws/aws-sdk-go-v2 service/fsx/v1.51.6, aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.12.2, aws/aws-sdk-go-v2 service/internal/presigned-url/v1.12.10, aws/aws-sdk-go-v2 service/secretsmanager/v1.34.14, aws/aws-sdk-go-v2 service/sso/v1.24.12, aws/aws-sdk-go-v2 service/ssooidc/v1.28.11, aws/aws-sdk-go-v2 service/sts/v1.33.10, aws/aws-sdk-go-v2 v1.34.0, Azure/azure-sdk-for-go v2.0.0-beta, Azure/azure-sdk-for-go v3.0.0-beta, btree v1.1.3, census-instrumentation/opencensus-go v0.24.0, client-go v0.33.1, client_golang 20250429-snapshot, client_golang v1.22.0, cncf/udpa 20201120-snapshot-5459f2c9, container-storage-interface/spec v1.9.0, containerd/containerd v2.0.5, containerd/containerd v2.1.0, CoreOS v0.3.1, docker-go-plugins-helpers 20240701-snapshot-45e24314, docker-go-units v0.5.0, docker/buildx v0.18.0, elastic/go-sysinfo 20250425-snapshot, elastic/go-windows v1.0.2, envoyproxy/go-control-plane envoy/v1.32.4, envoyproxy/go-control-plane ratelimit/v0.1.0, envoyproxy/go-control-plane v0.13.4, gengo 20250207-snapshot-1244d319, github.com/aws/smithy-go 20250514-snapshot, github.com/aws/smithy-go v1.22.2, github.com/cncf/xds 20250326-snapshot-ae57f3c0, github.com/distribution/reference v0.6.0, github.com/google/cel-spec v0.23.0, github.com/kubernetes-csi/csi-lib-utils v0.16.0, github.com/mattermost/xml-roundtrip-validator 20230502-snapshot-3079e7b8, github.com/moby/spdystream v0.5.0, github.com/xdg-go/pbkdf2 1.0.0, go-etcd api/v3.5.21, go-etcd client/pkg/v3.5.21, go-etcd client/v2.305.21, go-etcd client/v3.5.21, go-etcd pkg/v3.5.21, go-etcd raft/v3.5.21, go-etcd server/v3.5.21, go-jose 4.0.5, go-logr/logr v1.4.2, go-logr/stdr v1.2.2, go-openapi/analysis v0.23.0, go-openapi/errors v0.22.0, go-openapi/jsonpointer v0.21.0, go-openapi/loads v0.22.0, go-openapi/runtime v0.28.0, go-openapi/spec v0.21.0, go-openapi/validate v0.24.0, go-systemd 20190321-snapshot-95778dfb, go-systemd v22.5.0, go.opentelemetry.io/proto otlp/v1.4.0, godebug v1.1.0, golang-github-docker-go-connections-dev 0.4.0, golang-mock v1.6.0, golang/appengine v1.6.8, golang/glog v1.2.4, google-cloud-go accessapproval/v1.8.3, google-cloud-go accesscontextmanager/v1.9.3, google-cloud-go aiplatform/v1.70.0, google-cloud-go analytics/v0.25.3, google-cloud-go apigateway/v1.7.3, google-cloud-go apigeeconnect/v1.7.3, google-cloud-go apigeeregistry/v0.9.3, google-cloud-go appengine/v1.9.3, google-cloud-go area120/v0.9.3, google-cloud-go artifactregistry/v1.16.1, google-cloud-go asset/v1.20.4, google-cloud-go assuredworkloads/v1.12.3, google-cloud-go auth/oauth2adapt/v0.2.7, google-cloud-go auth/v0.14.0, google-cloud-go automl/v1.14.4, google-cloud-go baremetalsolution/v1.3.3, google-cloud-go batch/v1.11.5, google-cloud-go beyondcorp/v1.1.3, google-cloud-go bigquery/v1.66.0, google-cloud-go bigtable/v1.34.0, google-cloud-go billing/v1.20.1, google-cloud-go binaryauthorization/v1.9.3, google-cloud-go certificatemanager/v1.9.3, google-cloud-go channel/v1.19.2, google-cloud-go cloudbuild/v1.20.0, google-cloud-go clouddms/v1.8.3, google-cloud-go cloudtasks/v1.13.3, google-cloud-go compute/metadata/v0.6.0, google-cloud-go compute/v1.33.0, google-cloud-go contactcenterinsights/v1.17.1, google-cloud-go container/v1.42.1, google-cloud-go containeranalysis/v0.13.3, google-cloud-go datacatalog/v1.24.3, google-cloud-go dataflow/v0.10.3, google-cloud-go dataform/v0.10.3, google-cloud-go datafusion/v1.8.3, google-cloud-go datalabeling/v0.9.3, google-cloud-go dataplex/v1.21.0, google-cloud-go dataproc/v2.10.1, google-cloud-go dataqna/v0.9.3, google-cloud-go datastore/v1.20.0, google-cloud-go datastream/v1.12.1, google-cloud-go deploy/v1.26.1, google-cloud-go dialogflow/v1.64.1, google-cloud-go dlp/v1.20.1, google-cloud-go documentai/v1.35.1, google-cloud-go domains/v0.10.3, google-cloud-go edgecontainer/v1.4.1, google-cloud-go errorreporting/v0.3.2, google-cloud-go essentialcontacts/v1.7.3, google-cloud-go eventarc/v1.15.1, google-cloud-go filestore/v1.9.3, google-cloud-go firestore/v1.18.0, google-cloud-go functions/v1.19.3, google-cloud-go gkebackup/v1.6.3, google-cloud-go gkeconnect/v0.12.1, google-cloud-go gkehub/v0.15.3, google-cloud-go gkemulticloud/v1.5.1, google-cloud-go gsuiteaddons/v1.7.3, google-cloud-go iam/v1.3.1, google-cloud-go iap/v1.10.3, google-cloud-go ids/v1.5.3, google-cloud-go iot/v1.8.3, google-cloud-go kms/v1.20.5, google-cloud-go language/v1.14.3, google-cloud-go lifesciences/v0.10.3, google-cloud-go logging/v1.13.0, google-cloud-go longrunning/v0.6.4, google-cloud-go managedidentities/v1.7.3, google-cloud-go maps/v1.17.1, google-cloud-go mediatranslation/v0.9.3, google-cloud-go memcache/v1.11.3, google-cloud-go metastore/v1.14.3, google-cloud-go monitoring/v1.23.0, google-cloud-go netapp/v1.6.0, google-cloud-go networkconnectivity/v1.16.1, google-cloud-go networkmanagement/v1.18.0, google-cloud-go networksecurity/v0.10.3, google-cloud-go notebooks/v1.12.3, google-cloud-go optimization/v1.7.3, google-cloud-go orchestration/v1.11.4, google-cloud-go orgpolicy/v1.14.2, google-cloud-go osconfig/v1.14.3, google-cloud-go oslogin/v1.14.3, google-cloud-go phishingprotection/v0.9.3, google-cloud-go policytroubleshooter/v1.11.3, google-cloud-go privatecatalog/v0.10.4, google-cloud-go pubsub/v1.45.3, google-cloud-go pubsublite/v1.8.2, google-cloud-go recaptchaenterprise/v2.19.4, google-cloud-go recommendationengine/v0.9.3, google-cloud-go recommender/v1.13.3, google-cloud-go redis/v1.17.3, google-cloud-go resourcemanager/v1.10.3, google-cloud-go resourcesettings/v1.8.3, google-cloud-go retail/v1.19.2, google-cloud-go run/v1.8.1, google-cloud-go scheduler/v1.11.3, google-cloud-go secretmanager/v1.14.3, google-cloud-go security/v1.18.3, google-cloud-go securitycenter/v1.35.3, google-cloud-go servicedirectory/v1.12.3, google-cloud-go shell/v1.8.3, google-cloud-go spanner/v1.73.0, google-cloud-go speech/v1.26.0, google-cloud-go storage/v1.50.0, google-cloud-go storagetransfer/v1.12.1, google-cloud-go talent/v1.8.0, google-cloud-go texttospeech/v1.11.0, google-cloud-go tpu/v1.8.0, google-cloud-go trace/v1.11.3, google-cloud-go translate/v1.12.3, google-cloud-go v0.118.1, google-cloud-go video/v1.23.3, google-cloud-go videointelligence/v1.12.3, google-cloud-go vision/v2.9.3, google-cloud-go vmmigration/v1.8.3, google-cloud-go vmwareengine/v1.3.3, google-cloud-go vpcaccess/v1.8.3, google-cloud-go webrisk/v1.10.3, google-cloud-go websecurityscanner/v1.7.3, google-cloud-go workflows/v1.13.3, google-gofuzz v1.2.0, google/cel-go v0.23.2, google/gnostic-models v0.6.9, google/go-pkcs11 v0.3.0, google/pprof 20241029-snapshot-d1b30feb, google/s2a-go v0.1.9, googleapis/enterprise-certificate-proxy 0.3.4, googleapis/go-genproto 20250115-snapshot-1a7da9e5, googleapis/go-genproto 20250122-snapshot-138b5a5a, googleapis/go-genproto 20250324-snapshot-b45e905d, googleapis/go-genproto 20250603-snapshot-513f2392, googleapis/go-genproto 20250604-snapshot, GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.27.0, GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.49.0, GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.49.0, GoogleCloudPlatform/osconfig 20241004.00, groupcache 20210331-snapshot-41bb18bf, grpc-ecosystem/go-grpc-middleware v1.3.0, grpc-ecosystem/go-grpc-prometheus v1.2.0, grpc-go 20250607-snapshot, grpc-go 20250610-snapshot, grpc-go v1.73.0, helm/helm v3.18.1, inconshreveable/mousetrap v1.1.0, jmespath-go-jmespath v0.4.0, jonboulle-clockwork v0.4.0, jsonreference v0.21.0, k8s.io/code-generator v0.33.1, k8s.io/klog 2.130.1, k8s.io/kube-openapi 20250318-snapshot-c8a335a9, k8s.io/utils 20241210-snapshot-24370bea, k8s.io/utils 20250526-snapshot, Kubernetes v1.33.0-alpha.1, Kubernetes v1.33.0-rc.1, kubernetes-csi/csi-proxy client/v1.1.3, kubernetes-csi/external-snapshotter 20250130-snapshot, kubernetes-csi/external-snapshotter client/v8.2.0, kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.2, kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.0.50, kubernetes-sigs/structured-merge-diff v4.6.0, kubernetes/api 20250603-snapshot, kubernetes/api v0.33.1, kubernetes/apiextensions-apiserver 20250507-snapshot, kubernetes/apiextensions-apiserver v0.33.1, kubernetes/apimachinery v0.33.1, kubernetes/apiserver v0.33.1, kubernetes/component-base v0.33.1, kubernetes/kms v0.33.1, kubernetes/mount-utils v0.32.1, martian v3.3.3, matttproud-golang_protobuf_extensions v1.0.4, mendersoftware/mendertesting 0.0~git20200227.1396c95, moby/sys mountinfo/v0.7.2, moby/sys userns/v0.1.0, modern-go/concurrent 20180305-snapshot-bacd9c7e, modern-go/reflect2 v1.0.2, mongodb/mongo-go-driver v1.14.0, mschoch/smat v0.2.0, mwitkow/go-conntrack 20190716-snapshot-2f068394, NetApp/trident 20250601-snapshot-5a39b5b5, NetApp/trident v25.02.0, NYTimes-gziphandler v1.1.1, oklog/ulid v1.3.1, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.33.0, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.33.0, open-telemetry/opentelemetry-go metric/v1.35.0, open-telemetry/opentelemetry-go sdk/metric/v1.35.0, open-telemetry/opentelemetry-go sdk/v1.35.0, open-telemetry/opentelemetry-go trace/v1.35.0, open-telemetry/opentelemetry-go v1.35.0, open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.35.0, open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.58.0, open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.58.0, open-telemetry/opentelemetry-go-instrumentation sdk/v1.1.0, OpenCensus 0.2.1, opencontainers/go-digest 1.0.0, opentracing-opentracing-go v1.2.0, osbuild-osbuild-composer 126, pmezard-go-difflib 20190219-snapshot-5d4384ee, podman 5.2.1, prometheus-client_model v0.6.1, prometheus-common v0.62.0, prometheus-procfs v0.15.1, RoaringBitmap-roaring v2.4.2, sigs.k8s.io/json 20241010-snapshot-9aa6b5e7, sigs.k8s.io/randfill v1.0.0, sigs.k8s.io/yaml v1.4.0, soheilhy/cmux v0.1.5, spf13-afero 20250326-snapshot, spf13-afero v1.14.0, spf13-cobra 1.8.1, spiffe/go-spiffe v2.5.0, strfmt v0.23.0, swag v0.23.0, uber-go/mock v0.5.0-patch, VictoriaMetrics v1.111.0, vishvananda-netlink v1.3.0, vishvananda-netns v0.0.5, xdg-go/scram v1.1.2, xdg-go/stringprep v1.0.4, yaml for Go 20141213-snapshot-9f9df343, yaml for Go v2.4.0, yaml for Go v3.0.1) +(aws/aws-sdk-go-v2 config/v1.29.2, aws/aws-sdk-go-v2 credentials/v1.17.55, aws/aws-sdk-go-v2 feature/ec2/imds/v1.16.25, aws/aws-sdk-go-v2 internal/configsources/v1.3.32, aws/aws-sdk-go-v2 internal/endpoints/v2.6.32, aws/aws-sdk-go-v2 internal/ini/v1.8.2, aws/aws-sdk-go-v2 service/fsx/v1.52.0, aws/aws-sdk-go-v2 service/internal/accept-encoding/v1.12.2, aws/aws-sdk-go-v2 service/internal/presigned-url/v1.12.10, aws/aws-sdk-go-v2 service/secretsmanager/v1.34.14, aws/aws-sdk-go-v2 service/sso/v1.24.12, aws/aws-sdk-go-v2 service/ssooidc/v1.28.11, aws/aws-sdk-go-v2 service/sts/v1.33.10, aws/aws-sdk-go-v2 v1.36.1, Azure/azure-sdk-for-go 20241215-snapshot, Azure/azure-sdk-for-go v2.0.0-beta, Azure/azure-sdk-for-go v3.0.0-beta, brunoga/deep 20250830-snapshot, brunoga/deep v1.2.4, btree v1.1.3, census-instrumentation/opencensus-go v0.24.0, client-go v0.34.1, client_golang 20250429-snapshot, client_golang v1.22.0, cncf/udpa 20201120-snapshot-5459f2c9, container-storage-interface/spec v1.11.0, containerd/containerd v2.0.5, containerd/containerd v2.1.3, CoreOS v0.3.1, coreos/ignition 2.21.0, coreos/ignition 2.22.0, docker-go-plugins-helpers 20240701-snapshot-45e24314, docker-go-units v0.5.0, docker/buildx v0.18.0, elastic/go-sysinfo 184688adcb6ddaa744fe787e6e6a47a95f8b5e44, elastic/go-sysinfo 20250922-snapshot, elastic/go-windows v1.0.2, envoyproxy/go-control-plane envoy/v1.32.4, envoyproxy/go-control-plane ratelimit/v0.1.0, envoyproxy/go-control-plane v0.13.4, envoyproxy/protoc-gen-validate 1.2.1, etcd-io/raft v3.6.0, gengo 20250604-snapshot-85fd79db, github.com/aws/smithy-go 20250514-snapshot, github.com/aws/smithy-go v1.22.2, github.com/cncf/xds 20250326-snapshot-ae57f3c0, github.com/distribution/reference v0.6.0, github.com/google/cel-spec v0.24.0, github.com/kubernetes-csi/csi-lib-utils v0.16.0, github.com/mattermost/xml-roundtrip-validator 20230502-snapshot-3079e7b8, github.com/moby/spdystream v0.5.0, github.com/xdg-go/pbkdf2 1.0.0, go-etcd api/v3.6.4, go-etcd client/pkg/v3.6.4, go-etcd client/v3.6.4, go-etcd pkg/v3.6.4, go-etcd server/v3.6.4, go-jose 4.0.5, go-logr/logr v1.4.2, go-logr/stdr v1.2.2, go-openapi/analysis v0.23.0, go-openapi/errors v0.22.1, go-openapi/jsonpointer v0.21.1, go-openapi/loads v0.22.0, go-openapi/runtime v0.28.0, go-openapi/spec v0.21.0, go-openapi/validate v0.24.0, go-systemd 20191104-snapshot-d3cd4ed1, go-systemd v22.5.0, go.opentelemetry.io/proto otlp/v1.5.0, godebug v1.1.0, golang-github-docker-go-connections-dev 0.4.0, golang-mock v1.6.0, golang/appengine v1.6.8, golang/glog v1.2.4, google-cloud-go accessapproval/v1.8.6, google-cloud-go accesscontextmanager/v1.9.6, google-cloud-go aiplatform/v1.85.0, google-cloud-go analytics/v0.28.0, google-cloud-go apigateway/v1.7.6, google-cloud-go apigeeconnect/v1.7.6, google-cloud-go apigeeregistry/v0.9.6, google-cloud-go appengine/v1.9.6, google-cloud-go area120/v0.9.6, google-cloud-go artifactregistry/v1.17.1, google-cloud-go asset/v1.21.0, google-cloud-go assuredworkloads/v1.12.6, google-cloud-go auth/oauth2adapt/v0.2.8, google-cloud-go auth/v0.16.1, google-cloud-go automl/v1.14.7, google-cloud-go baremetalsolution/v1.3.6, google-cloud-go batch/v1.12.2, google-cloud-go beyondcorp/v1.1.6, google-cloud-go bigquery/v1.67.0, google-cloud-go bigtable/v1.37.0, google-cloud-go billing/v1.20.4, google-cloud-go binaryauthorization/v1.9.5, google-cloud-go certificatemanager/v1.9.5, google-cloud-go channel/v1.19.5, google-cloud-go cloudbuild/v1.22.2, google-cloud-go clouddms/v1.8.7, google-cloud-go cloudtasks/v1.13.6, google-cloud-go compute/metadata/v0.7.0, google-cloud-go compute/v1.38.0, google-cloud-go contactcenterinsights/v1.17.3, google-cloud-go container/v1.42.4, google-cloud-go containeranalysis/v0.14.1, google-cloud-go datacatalog/v1.26.0, google-cloud-go dataflow/v0.10.6, google-cloud-go dataform/v0.11.2, google-cloud-go datafusion/v1.8.6, google-cloud-go datalabeling/v0.9.6, google-cloud-go dataplex/v1.25.2, google-cloud-go dataproc/v2.11.2, google-cloud-go dataqna/v0.9.6, google-cloud-go datastore/v1.20.0, google-cloud-go datastream/v1.14.1, google-cloud-go deploy/v1.27.1, google-cloud-go dialogflow/v1.68.2, google-cloud-go dlp/v1.22.1, google-cloud-go documentai/v1.37.0, google-cloud-go domains/v0.10.6, google-cloud-go edgecontainer/v1.4.3, google-cloud-go errorreporting/v0.3.2, google-cloud-go essentialcontacts/v1.7.6, google-cloud-go eventarc/v1.15.5, google-cloud-go filestore/v1.10.2, google-cloud-go firestore/v1.18.0, google-cloud-go functions/v1.19.6, google-cloud-go gkebackup/v1.7.0, google-cloud-go gkeconnect/v0.12.4, google-cloud-go gkehub/v0.15.6, google-cloud-go gkemulticloud/v1.5.3, google-cloud-go gsuiteaddons/v1.7.7, google-cloud-go iam/v1.5.2, google-cloud-go iap/v1.11.1, google-cloud-go ids/v1.5.6, google-cloud-go iot/v1.8.6, google-cloud-go kms/v1.21.2, google-cloud-go language/v1.14.5, google-cloud-go lifesciences/v0.10.6, google-cloud-go logging/v1.13.0, google-cloud-go longrunning/v0.6.7, google-cloud-go managedidentities/v1.7.6, google-cloud-go maps/v1.20.4, google-cloud-go mediatranslation/v0.9.6, google-cloud-go memcache/v1.11.6, google-cloud-go metastore/v1.14.6, google-cloud-go monitoring/v1.24.2, google-cloud-go netapp/v1.9.0, google-cloud-go networkconnectivity/v1.17.1, google-cloud-go networkmanagement/v1.19.1, google-cloud-go networksecurity/v0.10.6, google-cloud-go notebooks/v1.12.6, google-cloud-go optimization/v1.7.6, google-cloud-go orchestration/v1.11.9, google-cloud-go orgpolicy/v1.15.0, google-cloud-go osconfig/v1.14.5, google-cloud-go oslogin/v1.14.6, google-cloud-go phishingprotection/v0.9.6, google-cloud-go policytroubleshooter/v1.11.6, google-cloud-go privatecatalog/v0.10.7, google-cloud-go pubsub/v1.49.0, google-cloud-go pubsublite/v1.8.2, google-cloud-go recaptchaenterprise/v2.20.4, google-cloud-go recommendationengine/v0.9.6, google-cloud-go recommender/v1.13.5, google-cloud-go redis/v1.18.2, google-cloud-go resourcemanager/v1.10.6, google-cloud-go resourcesettings/v1.8.3, google-cloud-go retail/v1.20.0, google-cloud-go run/v1.9.3, google-cloud-go scheduler/v1.11.7, google-cloud-go secretmanager/v1.14.7, google-cloud-go security/v1.18.5, google-cloud-go securitycenter/v1.36.2, google-cloud-go servicedirectory/v1.12.6, google-cloud-go shell/v1.8.6, google-cloud-go spanner/v1.80.0, google-cloud-go speech/v1.27.1, google-cloud-go storage/v1.52.0, google-cloud-go storagetransfer/v1.12.4, google-cloud-go talent/v1.8.3, google-cloud-go texttospeech/v1.12.1, google-cloud-go tpu/v1.8.3, google-cloud-go trace/v1.11.6, google-cloud-go translate/v1.12.5, google-cloud-go v0.121.0, google-cloud-go video/v1.23.5, google-cloud-go videointelligence/v1.12.6, google-cloud-go vision/v2.9.5, google-cloud-go vmmigration/v1.8.6, google-cloud-go vmwareengine/v1.3.5, google-cloud-go vpcaccess/v1.8.6, google-cloud-go webrisk/v1.11.1, google-cloud-go websecurityscanner/v1.7.6, google-cloud-go workflows/v1.14.2, google-gofuzz v1.2.0, google/cel-go v0.26.0, google/gnostic-models v0.7.0, google/go-pkcs11 v0.3.0, google/pprof 20241029-snapshot-d1b30feb, google/s2a-go v0.1.9, googleapis/enterprise-certificate-proxy v0.3.6, googleapis/go-genproto 20250505-snapshot-f936aa4a, googleapis/go-genproto 20250512-snapshot-5a2f75b7, googleapis/go-genproto 20250603-snapshot-513f2392, GoogleCloudPlatform/opentelemetry-operations-go detectors/gcp/v1.27.0, GoogleCloudPlatform/opentelemetry-operations-go exporter/metric/v0.51.0, GoogleCloudPlatform/opentelemetry-operations-go internal/resourcemapping/v0.51.0, GoogleCloudPlatform/osconfig 20241004.00, groupcache 20210331-snapshot-41bb18bf, grpc-ecosystem/go-grpc-middleware providers/prometheus/v1.0.1, grpc-ecosystem/go-grpc-middleware v2.3.0, grpc-ecosystem/go-grpc-prometheus v1.2.0, grpc-go 20250721-snapshot, grpc-go v1.73.0, helm/helm v3.19.0, inconshreveable/mousetrap v1.1.0, jonboulle-clockwork v0.5.0, jsonreference v0.21.0, k8s.io/code-generator v0.34.1, k8s.io/klog 2.130.1, k8s.io/kube-openapi 20250710-snapshot-f3f2b991, k8s.io/kube-openapi 20250910-snapshot, k8s.io/utils 20250324-snapshot-4c0f3b24, k8s.io/utils 20250918-snapshot, Kubernetes v1.34.0-alpha.1, Kubernetes v1.34.0-rc.1, kubernetes-csi/csi-proxy client/v1.2.1, kubernetes-csi/external-snapshotter 20250130-snapshot, kubernetes-csi/external-snapshotter 20250707-snapshot, kubernetes-csi/external-snapshotter client/v8.2.0, kubernetes-sigs/apiserver-network-proxy konnectivity-client/v0.31.2, kubernetes-sigs/cloud-provider-azure pkg/azclient/v0.0.50, kubernetes-sigs/structured-merge-diff 20250925-snapshot, kubernetes-sigs/structured-merge-diff v4.4.2, kubernetes-sigs/structured-merge-diff v6.3.0, kubernetes/api 20250901-snapshot, kubernetes/api v0.34.1, kubernetes/apiextensions-apiserver 20250919-snapshot, kubernetes/apiextensions-apiserver v0.34.1, kubernetes/apimachinery 20250908-snapshot, kubernetes/apimachinery 20250919-snapshot, kubernetes/apimachinery v0.34.1, kubernetes/apiserver v0.34.1, kubernetes/component-base v0.34.1, kubernetes/kms v0.34.1, kubernetes/mount-utils v0.32.1, martian v3.3.3, matttproud-golang_protobuf_extensions v1.0.4, mendersoftware/mendertesting 0.0~git20200227.1396c95, moby/sys mountinfo/v0.7.2, moby/sys userns/v0.1.0, modern-go/concurrent 20180305-snapshot-bacd9c7e, modern-go/reflect2 20250322-snapshot-35a7c28c, mongodb/mongo-go-driver v1.17.4, mschoch/smat v0.2.0, mwitkow/go-conntrack 20190716-snapshot-2f068394, NetApp/trident 20251006-snapshot-47d3bc71, NetApp/trident v24.10.0, NYTimes-gziphandler v1.1.1, oklog/ulid v1.3.1, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/otlptracegrpc/v1.34.0, open-telemetry/opentelemetry-go exporters/otlp/otlptrace/v1.34.0, open-telemetry/opentelemetry-go metric/v1.35.0, open-telemetry/opentelemetry-go sdk/metric/v1.35.0, open-telemetry/opentelemetry-go sdk/v1.35.0, open-telemetry/opentelemetry-go trace/v1.35.0, open-telemetry/opentelemetry-go v1.35.0, open-telemetry/opentelemetry-go-contrib detectors/gcp/v1.35.0, open-telemetry/opentelemetry-go-contrib instrumentation/google.golang.org/grpc/otelgrpc/v0.60.0, open-telemetry/opentelemetry-go-contrib instrumentation/net/http/otelhttp/v0.60.0, open-telemetry/opentelemetry-go-instrumentation sdk/v1.1.0, OpenCensus 0.2.1, opencontainers/go-digest 1.0.0, openshift/api 20250530-snapshot-e041b5ef, opentracing-opentracing-go v1.2.0, osbuild-osbuild-composer 126, osbuild-osbuild-composer 143, pmezard-go-difflib 20190219-snapshot-5d4384ee, prometheus-client_model v0.6.1, prometheus-common v0.62.0, prometheus-procfs v0.16.1, RoaringBitmap-roaring v2.5.0, runc v1.3.0, securego/gosec v2.22.7, sigs.k8s.io/json 20241014-snapshot-cfa47c3a, sigs.k8s.io/randfill v1.0.0, sigs.k8s.io/yaml v1.6.0, soheilhy/cmux v0.1.5, spf13-afero 20250922-snapshot, spf13-afero v1.15.0, spf13-cobra 1.9.1, spiffe/go-spiffe v2.5.0, strfmt v0.23.0, swag v0.23.1, VictoriaMetrics v1.111.0, VictoriaMetrics v1.112.0, vishvananda-netlink v1.3.1, vishvananda-netns v0.0.5, xdg-go/scram v1.1.2, xdg-go/stringprep v1.0.4, yaml for Go 20141213-snapshot-9f9df343, yaml for Go v2.4.0, yaml for Go v3.0.1) Apache License @@ -1709,7 +1739,7 @@ third-party archives. KIND, either express or implied. See the License for the specific language - governing permissions and limitations under the License. + governing permissions and limitations under the License. --- @@ -1773,7 +1803,7 @@ The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the copyright holders or contributors. +either expressed or implied, of the copyright holders or contributors. --- @@ -1825,13 +1855,13 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- BSD 2-clause "Simplified" License -(containerd/containerd v2.0.5, containerd/containerd v2.1.0, dnaeon/go-vcr v3.2.0, go-check-check 20201130-snapshot-10cb9826, gorilla/websocket 20250226-snapshot-e064f32e, gorilla/websocket 20250408-snapshot, pkg/browser 20240102-snapshot-5ac0b6a4, podman 5.2.1) +(containerd/containerd v2.0.5, containerd/containerd v2.1.3, dnaeon/go-vcr v3.2.0, github.com/redis/go-redis v9.7.0, go-check-check 20201130-snapshot-10cb9826, gorilla/websocket 20250226-snapshot-e064f32e, pkg/browser 20240102-snapshot-5ac0b6a4) BSD Two Clause License @@ -1877,7 +1907,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. +DAMAGE. --- @@ -1931,7 +1961,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -1943,7 +1973,7 @@ Copyright (c) 2009 The Go Authors. All rights reserved. - + @@ -1995,7 +2025,7 @@ Redistribution and use in source and binary forms, with or without (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2057,7 +2087,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2121,67 +2151,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - ---- - -BSD 3-clause "New" or "Revised" License - -(grpc-gateway v1.16.0) - -Copyright (c) 2015, Gengo, Inc. - -All rights reserved. - - - -Redistribution and use in source and binary forms, with or without modification, - -are permitted provided that the following conditions are met: - - - - * Redistributions of source code must retain the above copyright notice, - - this list of conditions and the following disclaimer. - - - - * Redistributions in binary form must reproduce the above copyright notice, - - this list of conditions and the following disclaimer in the documentation - - and/or other materials provided with the distribution. - - - - * Neither the name of Gengo, Inc. nor the names of its - - contributors may be used to endorse or promote products derived from this - - software without specific prior written permission. - - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2245,7 +2215,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2307,69 +2277,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - ---- - -BSD 3-clause "New" or "Revised" License - -(golang-github-spf13-pflag-dev v1.0.5) - -Copyright (c) 2012 Alex Ogier. All rights reserved. - -Copyright (c) 2012 The Go Authors. All rights reserved. - - - -Redistribution and use in source and binary forms, with or without - -modification, are permitted provided that the following conditions are - -met: - - - - * Redistributions of source code must retain the above copyright - -notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - -copyright notice, this list of conditions and the following disclaimer - -in the documentation and/or other materials provided with the - -distribution. - - * Neither the name of Google Inc. nor the names of its - -contributors may be used to endorse or promote products derived from - -this software without specific prior written permission. - - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2441,7 +2349,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2455,7 +2363,7 @@ All rights reserved. -Redistribution and use in source and binary forms, with or without +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -2471,33 +2379,33 @@ modification, are permitted provided that the following conditions are met: and/or other materials provided with the distribution. -* Neither the name of the Evan Phoenix nor the names of its contributors +* Neither the name of the Evan Phoenix nor the names of its contributors - may be used to endorse or promote products derived from this software + may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- @@ -2557,95 +2465,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - ---- - -BSD 3-clause "New" or "Revised" License - -(go-flags v1.4.0) - -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ - - - -Files: * - -Copyright: Copyright (c) 2012 Jesse van den Kieboom - -License: BSD-3-Clause - - - -Files: debian/* - -Copyright: Copyright (C) 2013 Canonical, Ltd. - -License: BSD-3-Clause - - - -License: BSD-3-Clause - - Copyright (c) 2012 Jesse van den Kieboom. All rights reserved. - - - -Redistribution and use in source and binary forms, with or without - - modification, are permitted provided that the following conditions are - - met: - - . - - * Redistributions of source code must retain the above copyright - - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - - copyright notice, this list of conditions and the following disclaimer - - in the documentation and/or other materials provided with the - - distribution. - - * Neither the name of Google Inc. nor the names of its - - contributors may be used to endorse or promote products derived from - - this software without specific prior written permission. - - . - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE --- BSD 3-clause "New" or "Revised" License -(7-Zip 24.09, bits-and-blooms/bitset 20241228-snapshot, bits-and-blooms/bitset v1.20.0, containerd/containerd v2.0.5, containerd/containerd v2.1.0, evanphx/json-patch v5.9.0, exp 20240711-snapshot-8a7402ab, fsnotify-fsnotify v1.7.0, github.com/antlr4-go/antlr v4.13.0, github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61, github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b, go-plist v1.0.1, Golang Protobuf 20240812-snapshot, Golang Protobuf v1.36.6, Golang Protobuf v1.5.4, golang-github-googleapis-gax-go-dev 2.13.0, golang.org/x/crypto v0.39.0, golang.org/x/lint 20190308-snapshot-d0100b6b, golang.org/x/mod v0.25.0, golang.org/x/net 20250404-snapshot, golang.org/x/net 20250607-snapshot, golang.org/x/net v0.41.0, golang.org/x/oauth2 v0.28.0, golang.org/x/sys 20250608-snapshot, golang.org/x/sys v0.33.0, golang.org/x/term v0.32.0, golang.org/x/time v0.9.0, golang.org/x/tools 20250611-snapshot, golang.org/x/tools v0.33.0, golang.org/x/xerrors 20200804-snapshot-5ec99f83, golang/sync 20250607-snapshot, golang/sync v0.15.0, golang/telemetry 20240517-snapshot-bda55230, golang/text 20240806-snapshot, golang/text v0.26.0, golang/tools v0.33.0, google/go-cmp v0.7.0, googleapis/gax-go v2.14.1, googleapis/google-api-go-client 20250505-snapshot, googleapis/google-api-go-client v0.218.0, Googleuuid v1.6.0, gorilla/mux v1.8.1, grpc-gateway v2.24.0, klauspost-compress v1.18.0, podman 5.2.1, rogpeppe/go-internal v1.13.1, xhit/go-str2duration v2.1.0) +(bits-and-blooms/bitset 20250923-snapshot, bits-and-blooms/bitset v1.20.0, containerd/containerd v2.0.5, containerd/containerd v2.1.3, evanphx/json-patch v5.9.11, exp 20250718-snapshot-645b1fa8, fsnotify-fsnotify v1.9.0, github.com/antlr4-go/antlr v4.13.0, github.com/munnerz/goautoneg 20191010-snapshot-a7dc8b61, github.com/planetscale/vtprotobuf 20240319-snapshot-0393e58b, go-flags v1.6.1, go-plist v1.0.1, Golang Protobuf 20250505-snapshot, Golang Protobuf v1.36.6, Golang Protobuf v1.5.4, golang-github-googleapis-gax-go-dev 2.13.0, golang-github-spf13-pflag-dev v1.0.7, golang.org/x/crypto v0.40.0, golang.org/x/lint 20190308-snapshot-d0100b6b, golang.org/x/mod v0.26.0, golang.org/x/net 20250827-snapshot, golang.org/x/net v0.42.0, golang.org/x/oauth2 v0.30.0, golang.org/x/sys 20250923-snapshot, golang.org/x/sys v0.37.0, golang.org/x/term v0.33.0, golang.org/x/time v0.11.0, golang.org/x/tools v0.35.0, golang.org/x/xerrors 20200804-snapshot-5ec99f83, golang/sync v0.16.0, golang/telemetry 20250709-snapshot-8d8967af, golang/text 20240806-snapshot, golang/text v0.28.0, google/go-cmp v0.7.0, googleapis/gax-go v2.14.2, googleapis/google-api-go-client 20250917-snapshot, googleapis/google-api-go-client v0.234.0, Googleuuid v1.6.0, gorilla/mux v1.8.1, grpc-gateway v2.26.3, klauspost-compress v1.18.0, rogpeppe/go-internal v1.14.1, xhit/go-str2duration v2.1.0) Copyright (c) , @@ -2701,1385 +2527,1093 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --- -Expat License +GNU Lesser General Public License v2.1 or later -(fxamacker/cbor 2.7.0) +(7-Zip 25.01) -Expat License +GNU Lesser General Public License -============= +================================= -Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd +Version 2.1, February 1999 - and Clark Cooper -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. + Copyright (C) 1991, 1999 Free Software Foundation, Inc. -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in the + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -Software without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the -Software, and to permit persons to whom the Software is furnished to do so, + Everyone is permitted to copy and distribute verbatim copies -subject to the following conditions: + of this license document, but changing it is not allowed. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + [This is the first released version of the Lesser GPL. It also counts -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + as the successor of the GNU Library Public License, version 2, hence -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + the version number 2.1.] -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---- -GNU Affero General Public License v3.0 -(Grafana 10.2.6) -GNU AFFERO GENERAL PUBLIC LICENSE +Preamble -================================= +-------- -Version 3, 19 November 2007 +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the -Copyright (C) 2007 Free Software Foundation, Inc. +software is free for all its users. -Everyone is permitted to copy and distribute verbatim copies of this license -document, but changing it is not allowed. +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software +Foundation and other authors who decide to use it. You can use it too, but we +suggest you first think carefully about whether this license or the ordinary -Preamble +General Public License is the better strategy to use in any particular case, +based on the explanations below. -The GNU Affero General Public License is a free, copyleft license for software -and other kinds of works, specifically designed to ensure cooperation with the +When we speak of free software, we are referring to freedom of use, not price. -community in the case of network server software. +Our General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you wish); +that you receive source code or can get it if you want it; that you can change -The licenses for most software and other practical works are designed to take +the software and use pieces of it in new free programs; and that you are informed -away your freedom to share and change the works. By contrast, our General Public +that you can do these things. -Licenses are intended to guarantee your freedom to share and change all versions -of a program--to make sure it remains free software for all its users. +To protect your rights, we need to make restrictions that forbid distributors to +deny you these rights or to ask you to surrender these rights. These restrictions -When we speak of free software, we are referring to freedom, not price. Our +translate to certain responsibilities for you if you distribute copies of the -General Public Licenses are designed to make sure that you have the freedom to +library or if you modify it. -distribute copies of free software (and charge for them if you wish), that you -receive source code or can get it if you want it, that you can change the -software or use pieces of it in new free programs, and that you know you can do +For example, if you distribute copies of the library, whether gratis or for a -these things. +fee, you must give the recipients all the rights that we gave you. You must make +sure that they, too, receive or can get the source code. If you link other code +with the library, you must provide complete object files to the recipients, so -Developers that use our General Public Licenses protect your rights with two +that they can relink them with the library after making changes to the library -steps: (1) assert copyright on the software, and (2) offer you this License which +and recompiling it. And you must show them these terms so they know their rights. -gives you legal permission to copy, distribute and/or modify the software. +We protect your rights with a two-step method: (1) we copyright the library, and -A secondary benefit of defending all users' freedom is that improvements made in +(2) we offer you this license, which gives you legal permission to copy, -alternate versions of the program, if they receive widespread use, become +distribute and/or modify the library. -available for other developers to incorporate. Many developers of free software -are heartened and encouraged by the resulting cooperation. However, in the case -of software used on network servers, this result may fail to come about. The GNU +To protect each distributor, we want to make it very clear that there is no -General Public License permits making a modified version and letting the public +warranty for the free library. Also, if the library is modified by someone else -access it on a server without ever releasing its source code to the public. +and passed on, the recipients should know that what they have is not the original +version, so that the original author's reputation will not be affected by +problems that might be introduced by others. -The GNU Affero General Public License is designed specifically to ensure that, in -such cases, the modified source code becomes available to the community. It -requires the operator of a network server to provide the source code of the +Finally, software patents pose a constant threat to the existence of any free -modified version running there to the users of that server. Therefore, public use +program. We wish to make sure that a company cannot effectively restrict the -of a modified version, on a publicly accessible server, gives the public access +users of a free program by obtaining a restrictive license from a patent holder. -to the source code of the modified version. +Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this +license. -An older license, called the Affero General Public License and published by -Affero, was designed to accomplish similar goals. This is a different license, -not a version of the Affero GPL, but Affero has released a new version of the +Most GNU software, including some libraries, is covered by the ordinary GNU -Affero GPL which permits relicensing under this license. +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the ordinary +General Public License. We use this license for certain libraries in order to -The precise terms and conditions for copying, distribution and modification +permit linking those libraries into non-free programs. -follow. +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License therefore -TERMS AND CONDITIONS +permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other +code with the library. -0. Definitions. +We call this license the "Lesser" General Public License because it does Less to -"This License" refers to version 3 of the GNU Affero General Public License. +protect the user's freedom than the ordinary General Public License. It also +provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary General -"Copyright" also means copyright-like laws that apply to other kinds of works, +Public License for many libraries. However, the Lesser license provides -such as semiconductor masks. +advantages in certain special circumstances. -"The Program" refers to any copyrightable work licensed under this License. Each +For example, on rare occasions, there may be a special need to encourage the -licensee is addressed as "you". "Licensees" and "recipients" may be individuals +widest possible use of a certain library, so that it becomes a de-facto standard. -or organizations. +To achieve this, non-free programs must be allowed to use the library. A more +frequent case is that a free library does the same job as widely used non-free +libraries. In this case, there is little to gain by limiting the free library to -To "modify" a work means to copy from or adapt all or part of the work in a +free software only, so we use the Lesser General Public License. -fashion requiring copyright permission, other than the making of an exact copy. -The resulting work is called a "modified version" of the earlier work or a work -"based on" the earlier work. +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables many -A "covered work" means either the unmodified Program or a work based on the +more people to use the whole GNU operating system, as well as its variant, the -Program. +GNU/Linux operating system. -To "propagate" a work means to do anything with it that, without permission, +Although the Lesser General Public License is Less protective of the users' -would make you directly or secondarily liable for infringement under applicable +freedom, it does ensure that the user of a program that is linked with the -copyright law, except executing it on a computer or modifying a private copy. +Library has the freedom and the wherewithal to run that program using a modified -Propagation includes copying, distribution (with or without modification), making +version of the Library. -available to the public, and in some countries other activities as well. +The precise terms and conditions for copying, distribution and modification -To "convey" a work means any kind of propagation that enables other parties to +follow. Pay close attention to the difference between a "work based on the -make or receive copies. Mere interaction with a user through a computer network, +library" and a "work that uses the library". The former contains code derived -with no transfer of a copy, is not conveying. +from the library, whereas the latter must be combined with the library in order +to run. -An interactive user interface displays "Appropriate Legal Notices" to the extent -that it includes a convenient and prominently visible feature that (1) displays -an appropriate copyright notice, and (2) tells the user that there is no warranty -for the work (except to the extent that warranties are provided), that licensees +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -may convey the work under this License, and how to view a copy of this License. +--------------------------------------------------------------- -If the interface presents a list of user commands or options, such as a menu, a -prominent item in the list meets this criterion. +0. This License Agreement applies to any software library or other program which +contains a notice placed by the copyright holder or other authorized party saying -1. Source Code. +it may be distributed under the terms of this Lesser General Public License (also +called "this License"). Each licensee is addressed as "you". -The "source code" for a work means the preferred form of the work for making -modifications to it. "Object code" means any non-source form of a work. +A "library" means a collection of software functions and/or data prepared so as +to be conveniently linked with application programs (which use some of those +functions and data) to form executables. -A "Standard Interface" means an interface that either is an official standard -defined by a recognized standards body, or, in the case of interfaces specified -for a particular programming language, one that is widely used among developers +The "Library", below, refers to any such software library or work which has been -working in that language. +distributed under these terms. A "work based on the Library" means either the +Library or any derivative work under copyright law: that is to say, a work +containing the Library or a portion of it, either verbatim or with modifications -The "System Libraries" of an executable work include anything, other than the +and/or translated straightforwardly into another language. (Hereinafter, -work as a whole, that (a) is included in the normal form of packaging a Major +translation is included without limitation in the term "modification".) -Component, but which is not part of that Major Component, and (b) serves only to -enable use of the work with that Major Component, or to implement a Standard -Interface for which an implementation is available to the public in source code +"Source code" for a work means the preferred form of the work for making -form. A "Major Component", in this context, means a major essential component +modifications to it. For a library, complete source code means all the source -(kernel, window system, and so on) of the specific operating system (if any) on +code for all modules it contains, plus any associated interface definition files, -which the executable work runs, or a compiler used to produce the work, or an +plus the scripts used to control compilation and installation of the library. -object code interpreter used to run it. +Activities other than copying, distribution and modification are not covered by -The "Corresponding Source" for a work in object code form means all the source +this License; they are outside its scope. The act of running a program using the -code needed to generate, install, and (for an executable work) run the object +Library is not restricted, and output from such a program is covered only if its -code and to modify the work, including scripts to control those activities. +contents constitute a work based on the Library (independent of the use of the -However, it does not include the work's System Libraries, or general-purpose +Library in a tool for writing it). Whether that is true depends on what the -tools or generally available free programs which are used unmodified in +Library does and what the program that uses the Library does. -performing those activities but which are not part of the work. For example, -Corresponding Source includes interface definition files associated with source -files for the work, and the source code for shared libraries and dynamically +1. You may copy and distribute verbatim copies of the Library's complete source -linked subprograms that the work is specifically designed to require, such as by +code as you receive it, in any medium, provided that you conspicuously and -intimate data communication or control flow between those subprograms and other +appropriately publish on each copy an appropriate copyright notice and disclaimer -parts of the work. +of warranty; keep intact all the notices that refer to this License and to the +absence of any warranty; and distribute a copy of this License along with the +Library. -The Corresponding Source need not include anything that users can regenerate -automatically from other parts of the Corresponding Source. +You may charge a fee for the physical act of transferring a copy, and you may at +your option offer warranty protection in exchange for a fee. -The Corresponding Source for a work in source code form is that same work. +2. You may modify your copy or copies of the Library or any portion of it, thus -2. Basic Permissions. +forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: -All rights granted under this License are granted for the term of copyright on -the Program, and are irrevocable provided the stated conditions are met. This -License explicitly affirms your unlimited permission to run the unmodified + a) The modified work must itself be a software library. -Program. The output from running a covered work is covered by this License only -if the output, given its content, constitutes a covered work. This License -acknowledges your rights of fair use or other equivalent, as provided by + b) You must cause the files modified to carry prominent notices stating -copyright law. + that you changed the files and the date of any change. -You may make, run and propagate covered works that you do not convey, without + c) You must cause the whole of the work to be licensed at no charge to all -conditions so long as your license otherwise remains in force. You may convey + third parties under the terms of this License. -covered works to others for the sole purpose of having them make modifications -exclusively for you, or provide you with facilities for running those works, -provided that you comply with the terms of this License in conveying all material + d) If a facility in the modified Library refers to a function or a table of -for which you do not control copyright. Those thus making or running the covered + data to be supplied by an application program that uses the facility, other -works for you must do so exclusively on your behalf, under your direction and + than as an argument passed when the facility is invoked, then you must make -control, on terms that prohibit them from making any copies of your copyrighted + a good faith effort to ensure that, in the event an application does not -material outside their relationship with you. + supply such function or table, the facility still operates, and performs + whatever part of its purpose remains meaningful. -Conveying under any other circumstances is permitted solely under the conditions -stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + (For example, a function in a library to compute square roots has a purpose + that is entirely well-defined independent of the application. Therefore, + Subsection 2d requires that any application-supplied function or table used -3. Protecting Users' Legal Rights From Anti-Circumvention Law. + by this function must be optional: if the application does not supply it, + the square root function must still compute square roots.) -No covered work shall be deemed part of an effective technological measure under -any applicable law fulfilling obligations under article 11 of the WIPO copyright + These requirements apply to the modified work as a whole. If identifiable -treaty adopted on 20 December 1996, or similar laws prohibiting or restricting + sections of that work are not derived from the Library, and can be -circumvention of such measures. + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same -When you convey a covered work, you waive any legal power to forbid circumvention + sections as part of a whole which is a work based on the Library, the -of technological measures to the extent such circumvention is effected by + distribution of the whole must be on the terms of this License, whose -exercising rights under this License with respect to the covered work, and you + permissions for other licensees extend to the entire whole, and thus to -disclaim any intention to limit operation or modification of the work as a means + each and every part regardless of who wrote it. -of enforcing, against the work's users, your or third parties' legal rights to -forbid circumvention of technological measures. + Thus, it is not the intent of this section to claim rights or contest your + rights to work written entirely by you; rather, the intent is to exercise -4. Conveying Verbatim Copies. + the right to control the distribution of derivative or collective works + based on the Library. -You may convey verbatim copies of the Program's source code as you receive it, in -any medium, provided that you conspicuously and appropriately publish on each + In addition, mere aggregation of another work not based on the Library with -copy an appropriate copyright notice; keep intact all notices stating that this + the Library (or with a work based on the Library) on a volume of a storage -License and any non-permissive terms added in accord with section 7 apply to the + or distribution medium does not bring the other work under the scope of -code; keep intact all notices of the absence of any warranty; and give all + this License. -recipients a copy of this License along with the Program. +3. You may opt to apply the terms of the ordinary GNU General Public License -You may charge any price or no price for each copy that you convey, and you may +instead of this License to a given copy of the Library. To do this, you must -offer support or warranty protection for a fee. +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. (If a +newer version than version 2 of the ordinary GNU General Public License has -5. Conveying Modified Source Versions. +appeared, then you can specify that version instead if you wish.) Do not make any +other change in these notices. -You may convey a work based on the Program, or the modifications to produce it -from the Program, in the form of source code under the terms of section 4, +Once this change is made in a given copy, it is irreversible for that copy, so -provided that you also meet all of these conditions: +the ordinary GNU General Public License applies to all subsequent copies and +derivative works made from that copy. - * a) The work must carry prominent notices stating that you modified it, and - giving a relevant date. +This option is useful when you wish to copy part of the code of the Library into +a program that is not a library. - * b) The work must carry prominent notices stating that it is released under - this License and any conditions added under section 7. This requirement +4. You may copy and distribute the Library (or a portion or derivative of it, - modifies the requirement in section 4 to "keep intact all notices". +under Section 2) in object code or executable form under the terms of Sections 1 +and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of - * c) You must license the entire work, as a whole, under this License to anyone +Sections 1 and 2 above on a medium customarily used for software interchange. - who comes into possession of a copy. This License will therefore apply, along - with any applicable section 7 additional terms, to the whole of the work, and - all its parts, regardless of how they are packaged. This License gives no +If distribution of object code is made by offering access to copy from a - permission to license the work in any other way, but it does not invalidate +designated place, then offering equivalent access to copy the source code from - such permission if you have separately received it. +the same place satisfies the requirement to distribute the source code, even +though third parties are not compelled to copy the source along with the object +code. - * d) If the work has interactive user interfaces, each must display Appropriate - Legal Notices; however, if the Program has interactive interfaces that do not - display Appropriate Legal Notices, your work need not make them do so. +5. A program that contains no derivative of any portion of the Library, but is +designed to work with the Library by being compiled or linked with it, is called +a "work that uses the Library". Such a work, in isolation, is not a derivative -A compilation of a covered work with other separate and independent works, which +work of the Library, and therefore falls outside the scope of this License. -are not by their nature extensions of the covered work, and which are not -combined with it such as to form a larger program, in or on a volume of a storage -or distribution medium, is called an "aggregate" if the compilation and its +However, linking a "work that uses the Library" with the Library creates an -resulting copyright are not used to limit the access or legal rights of the +executable that is a derivative of the Library (because it contains portions of -compilation's users beyond what the individual works permit. Inclusion of a +the Library), rather than a "work that uses the library". The executable is -covered work in an aggregate does not cause this License to apply to the other +therefore covered by this License. Section 6 states terms for distribution of -parts of the aggregate. +such executables. -6. Conveying Non-Source Forms. +When a "work that uses the Library" uses material from a header file that is part +of the Library, the object code for the work may be a derivative work of the +Library even though the source code is not. Whether this is true is especially -You may convey a covered work in object code form under the terms of sections 4 +significant if the work can be linked without the Library, or if the work is -and 5, provided that you also convey the machine-readable Corresponding Source +itself a library. The threshold for this to be true is not precisely defined by -under the terms of this License, in one of these ways: +law. - * a) Convey the object code in, or embodied in, a physical product (including a +If such an object file uses only numerical parameters, data structure layouts and - physical distribution medium), accompanied by the Corresponding Source fixed +accessors, and small macros and small inline functions (ten lines or less in - on a durable physical medium customarily used for software interchange. +length), then the use of the object file is unrestricted, regardless of whether +it is legally a derivative work. (Executables containing this object code plus +portions of the Library will still fall under Section 6.) - * b) Convey the object code in, or embodied in, a physical product (including a - physical distribution medium), accompanied by a written offer, valid for at - least three years and valid for as long as you offer spare parts or customer +Otherwise, if the work is a derivative of the Library, you may distribute the - support for that product model, to give anyone who possesses the object code +object code for the work under the terms of Section 6. Any executables containing - either (1) a copy of the Corresponding Source for all the software in the +that work also fall under Section 6, whether or not they are linked directly with - product that is covered by this License, on a durable physical medium +the Library itself. - customarily used for software interchange, for a price no more than your - reasonable cost of physically performing this conveying of source, or (2) - access to copy the Corresponding Source from a network server at no charge. +6. As an exception to the Sections above, you may also combine or link a "work +that uses the Library" with the Library to produce a work containing portions of +the Library, and distribute that work under terms of your choice, provided that - * c) Convey individual copies of the object code with a copy of the written +the terms permit modification of the work for the customer's own use and reverse - offer to provide the Corresponding Source. This alternative is allowed only +engineering for debugging such modifications. - occasionally and noncommercially, and only if you received the object code - with such an offer, in accord with subsection 6b. +You must give prominent notice with each copy of the work that the Library is +used in it and that the Library and its use are covered by this License. You must - * d) Convey the object code by offering access from a designated place (gratis +supply a copy of this License. If the work during execution displays copyright - or for a charge), and offer equivalent access to the Corresponding Source in +notices, you must include the copyright notice for the Library among them, as - the same way through the same place at no further charge. You need not +well as a reference directing the user to the copy of this License. Also, you - require recipients to copy the Corresponding Source along with the object +must do one of these things: - code. If the place to copy the object code is a network server, the - Corresponding Source may be on a different server (operated by you or a third - party) that supports equivalent copying facilities, provided you maintain + a) Accompany the work with the complete corresponding machine-readable - clear directions next to the object code saying where to find the + source code for the Library including whatever changes were used in the - Corresponding Source. Regardless of what server hosts the Corresponding + work (which must be distributed under Sections 1 and 2 above); and, if the - Source, you remain obligated to ensure that it is available for as long as + work is an executable linked with the Library, with the complete - needed to satisfy these requirements. + machine-readable "work that uses the Library", as object code and/or source + code, so that the user can modify the Library and then relink to produce a + modified executable containing the modified Library. (It is understood that - * e) Convey the object code using peer-to-peer transmission, provided you + the user who changes the contents of definitions files in the Library will - inform other peers where the object code and Corresponding Source of the work + not necessarily be able to recompile the application to use the modified - are being offered to the general public at no charge under subsection 6d. + definitions.) -A separable portion of the object code, whose source code is excluded from the + b) Use a suitable shared library mechanism for linking with the Library. A -Corresponding Source as a System Library, need not be included in conveying the + suitable mechanism is one that (1) uses at run time a copy of the library -object code work. + already present on the user's computer system, rather than copying library + functions into the executable, and (2) will operate properly with a + modified version of the library, if the user installs one, as long as the -A "User Product" is either (1) a "consumer product", which means any tangible + modified version is interface-compatible with the version that the work was -personal property which is normally used for personal, family, or household + made with. -purposes, or (2) anything designed or sold for incorporation into a dwelling. In -determining whether a product is a consumer product, doubtful cases shall be -resolved in favor of coverage. For a particular product received by a particular + c) Accompany the work with a written offer, valid for at least three years, -user, "normally used" refers to a typical or common use of that class of product, + to give the same user the materials specified in Subsection 6a, above, for -regardless of the status of the particular user or of the way in which the + a charge no more than the cost of performing this distribution. -particular user actually uses, or expects or is expected to use, the product. A -product is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent the only + d) If distribution of the work is made by offering access to copy from a -significant mode of use of the product. + designated place, offer equivalent access to copy the above specified + materials from the same place. -"Installation Information" for a User Product means any methods, procedures, -authorization keys, or other information required to install and execute modified + e) Verify that the user has already received a copy of these materials or -versions of a covered work in that User Product from a modified version of its + that you have already sent this user a copy. -Corresponding Source. The information must suffice to ensure that the continued -functioning of the modified object code is in no case prevented or interfered -with solely because modification has been made. +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable from +it. However, as a special exception, the materials to be distributed need not -If you convey an object code work under this section in, or with, or specifically +include anything that is normally distributed (in either source or binary form) -for use in, a User Product, and the conveying occurs as part of a transaction in +with the major components (compiler, kernel, and so on) of the operating system -which the right of possession and use of the User Product is transferred to the +on which the executable runs, unless that component itself accompanies the -recipient in perpetuity or for a fixed term (regardless of how the transaction is +executable. -characterized), the Corresponding Source conveyed under this section must be -accompanied by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install modified object +It may happen that this requirement contradicts the license restrictions of other -code on the User Product (for example, the work has been installed in ROM). +proprietary libraries that do not normally accompany the operating system. Such a +contradiction means you cannot use both them and the Library together in an +executable that you distribute. -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates for a -work that has been modified or installed by the recipient, or for the User +7. You may place library facilities that are a work based on the Library -Product in which it has been modified or installed. Access to a network may be +side-by-side in a single library together with other library facilities not -denied when the modification itself materially and adversely affects the +covered by this License, and distribute such a combined library, provided that -operation of the network or violates the rules and protocols for communication +the separate distribution of the work based on the Library and of the other -across the network. +library facilities is otherwise permitted, and provided that you do these two +things: -Corresponding Source conveyed, and Installation Information provided, in accord -with this section must be in a format that is publicly documented (and with an + a) Accompany the combined library with a copy of the same work based on the -implementation available to the public in source code form), and must require no + Library, uncombined with any other library facilities. This must be -special password or key for unpacking, reading or copying. + distributed under the terms of the Sections above. -7. Additional Terms. + b) Give prominent notice with the combined library of the fact that part of + it is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. -"Additional permissions" are terms that supplement the terms of this License by -making exceptions from one or more of its conditions. Additional permissions that -are applicable to the entire Program shall be treated as though they were +8. You may not copy, modify, sublicense, link with, or distribute the Library -included in this License, to the extent that they are valid under applicable law. +except as expressly provided under this License. Any attempt otherwise to copy, -If additional permissions apply only to part of the Program, that part may be +modify, sublicense, link with, or distribute the Library is void, and will -used separately under those permissions, but the entire Program remains governed +automatically terminate your rights under this License. However, parties who have -by this License without regard to the additional permissions. +received copies, or rights, from you under this License will not have their +licenses terminated so long as such parties remain in full compliance. -When you convey a copy of a covered work, you may at your option remove any -additional permissions from that copy, or from any part of it. (Additional +9. You are not required to accept this License, since you have not signed it. -permissions may be written to require their own removal in certain cases when you +However, nothing else grants you permission to modify or distribute the Library -modify the work.) You may place additional permissions on material, added by you +or its derivative works. These actions are prohibited by law if you do not accept -to a covered work, for which you have or can give appropriate copyright +this License. Therefore, by modifying or distributing the Library (or any work -permission. +based on the Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying the Library +or works based on it. -Notwithstanding any other provision of this License, for material you add to a -covered work, you may (if authorized by the copyright holders of that material) -supplement the terms of this License with terms: +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor to +copy, distribute, link with or modify the Library subject to these terms and - * a) Disclaiming warranty or limiting liability differently from the terms of +conditions. You may not impose any further restrictions on the recipients' - sections 15 and 16 of this License; or +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties with this License. - * b) Requiring preservation of specified reasonable legal notices or author - attributions in that material or in the Appropriate Legal Notices displayed +11. If, as a consequence of a court judgment or allegation of patent infringement - by works containing it; or +or for any other reason (not limited to patent issues), conditions are imposed on +you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of this - * c) Prohibiting misrepresentation of the origin of that material, or requiring +License. If you cannot distribute so as to satisfy simultaneously your - that modified versions of such material be marked in reasonable ways as +obligations under this License and any other pertinent obligations, then as a - different from the original version; or +consequence you may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by all those +who receive copies directly or indirectly through you, then the only way you - * d) Limiting the use for publicity purposes of names of licensors or authors +could satisfy both it and this License would be to refrain entirely from - of the material; or +distribution of the Library. - * e) Declining to grant rights under trademark law for use of some trade names, +If any portion of this section is held invalid or unenforceable under any - trademarks, or service marks; or +particular circumstance, the balance of the section is intended to apply, and the +section as a whole is intended to apply in other circumstances. - * f) Requiring indemnification of licensors and authors of that material by - anyone who conveys the material (or modified versions of it) with contractual +It is not the purpose of this section to induce you to infringe any patents or - assumptions of liability to the recipient, for any liability that these +other property right claims or to contest validity of any such claims; this - contractual assumptions directly impose on those licensors and authors. +section has the sole purpose of protecting the integrity of the free software +distribution system which is implemented by public license practices. Many people +have made generous contributions to the wide range of software distributed -All other non-permissive additional terms are considered "further restrictions" +through that system in reliance on consistent application of that system; it is -within the meaning of section 10. If the Program as you received it, or any part +up to the author/donor to decide if he or she is willing to distribute software -of it, contains a notice stating that it is governed by this License along with a +through any other system and a licensee cannot impose that choice. -term that is a further restriction, you may remove that term. If a license -document contains a further restriction but permits relicensing or conveying -under this License, you may add to a covered work material governed by the terms +This section is intended to make thoroughly clear what is believed to be a -of that license document, provided that the further restriction does not survive +consequence of the rest of this License. -such relicensing or conveying. +12. If the distribution and/or use of the Library is restricted in certain -If you add terms to a covered work in accord with this section, you must place, +countries either by patents or by copyrighted interfaces, the original copyright -in the relevant source files, a statement of the additional terms that apply to +holder who places the Library under this License may add an explicit geographical -those files, or a notice indicating where to find the applicable terms. +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. -Additional terms, permissive or non-permissive, may be stated in the form of a -separately written license, or stated as exceptions; the above requirements apply -either way. +13. The Free Software Foundation may publish revised and/or new versions of the +Lesser General Public License from time to time. Such new versions will be +similar in spirit to the present version, but may differ in detail to address new -8. Termination. +problems or concerns. -You may not propagate or modify a covered work except as expressly provided under +Each version is given a distinguishing version number. If the Library specifies a -this License. Any attempt otherwise to propagate or modify it is void, and will +version number of this License which applies to it and "any later version", you -automatically terminate your rights under this License (including any patent +have the option of following the terms and conditions either of that version or -licenses granted under the third paragraph of section 11). +of any later version published by the Free Software Foundation. If the Library +does not specify a license version number, you may choose any version ever +published by the Free Software Foundation. -However, if you cease all violation of this License, then your license from a -particular copyright holder is reinstated (a) provisionally, unless and until the -copyright holder explicitly and finally terminates your license, and (b) +14. If you wish to incorporate parts of the Library into other free programs -permanently, if the copyright holder fails to notify you of the violation by some +whose distribution conditions are incompatible with these, write to the author to -reasonable means prior to 60 days after the cessation. +ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free -Moreover, your license from a particular copyright holder is reinstated +status of all derivatives of our free software and of promoting the sharing and -permanently if the copyright holder notifies you of the violation by some +reuse of software generally. -reasonable means, this is the first time you have received notice of violation of -this License (for any work) from that copyright holder, and you cure the -violation prior to 30 days after your receipt of the notice. +NO WARRANTY -Termination of your rights under this section does not terminate the licenses of +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE -parties who have received copies or rights from you under this License. If your +LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED -rights have been terminated and not permanently reinstated, you do not qualify to +IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" -receive new licenses for the same material under section 10. +WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -9. Acceptance Not Required for Having Copies. +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -You are not required to accept this License in order to receive or run a copy of -the Program. Ancillary propagation of a covered work occurring solely as a +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL -consequence of using peer-to-peer transmission to receive a copy likewise does +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE -not require acceptance. However, nothing other than this License grants you +LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, -permission to propagate or modify any covered work. These actions infringe +SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY -copyright if you do not accept this License. Therefore, by modifying or +TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -propagating a covered work, you indicate your acceptance of this License to do +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF -so. +THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -10. Automatic Licensing of Downstream Recipients. -Each time you convey a covered work, the recipient automatically receives a +END OF TERMS AND CONDITIONS -license from the original licensors, to run, modify and propagate that work, -subject to this License. You are not responsible for enforcing compliance by -third parties with this License. +How to Apply These Terms to Your New Libraries -An "entity transaction" is a transaction transferring control of an organization, +---------------------------------------------- -or substantially all assets of one, or subdividing an organization, or merging -organizations. If propagation of a covered work results from an entity -transaction, each party to that transaction who receives a copy of the work also +If you develop a new library, and you want it to be of the greatest possible use -receives whatever licenses to the work the party's predecessor in interest had or +to the public, we recommend making it free software that everyone can -could give under the previous paragraph, plus a right to possession of the +redistribute and change. You can do so by permitting redistribution under these -Corresponding Source of the work from the predecessor in interest, if the +terms (or, alternatively, under the terms of the ordinary General Public -predecessor has it or can get it with reasonable efforts. +License). -You may not impose any further restrictions on the exercise of the rights granted +To apply these terms, attach the following notices to the library. It is safest -or affirmed under this License. For example, you may not impose a license fee, +to attach them to the start of each source file to most effectively convey the -royalty, or other charge for exercise of rights granted under this License, and +exclusion of warranty; and each file should have at least the "copyright" line -you may not initiate litigation (including a cross-claim or counterclaim in a +and a pointer to where the full notice is found. -lawsuit) alleging that any patent claim is infringed by making, using, selling, -offering for sale, or importing the Program or any portion of it. + one line to give the library's name and an idea of what it does. -11. Patents. + Copyright (C) year name of author -A "contributor" is a copyright holder who authorizes use under this License of -the Program or a work on which the Program is based. The work thus licensed is + This library is free software; you can redistribute it and/or -called the contributor's "contributor version". + modify it under the terms of the GNU Lesser General Public -A contributor's "essential patent claims" are all patent claims owned or -controlled by the contributor, whether already acquired or hereafter acquired, -that would be infringed by some manner, permitted by this License, of making, + License as published by the Free Software Foundation; either -using, or selling its contributor version, but do not include claims that would -be infringed only as a consequence of further modification of the contributor -version. For purposes of this definition, "control" includes the right to grant + version 2.1 of the License, or (at your option) any later version. -patent sublicenses in a manner consistent with the requirements of this License. + This library is distributed in the hope that it will be useful, -Each contributor grants you a non-exclusive, worldwide, royalty-free patent -license under the contributor's essential patent claims, to make, use, sell, -offer for sale, import and otherwise run, modify and propagate the contents of + but WITHOUT ANY WARRANTY; without even the implied warranty of -its contributor version. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -In the following three paragraphs, a "patent license" is any express agreement or -commitment, however denominated, not to enforce a patent (such as an express -permission to practice a patent or covenant not to sue for patent infringement). + Lesser General Public License for more details. -To "grant" such a patent license to a party means to make such an agreement or -commitment not to enforce a patent against the party. + You should have received a copy of the GNU Lesser General Public -If you convey a covered work, knowingly relying on a patent license, and the -Corresponding Source of the work is not available for anyone to copy, free of + License along with this library; if not, write to the Free Software -charge and under the terms of this License, through a publicly available network -server or other readily accessible means, then you must either (1) cause the -Corresponding Source to be so available, or (2) arrange to deprive yourself of + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -the benefit of the patent license for this particular work, or (3) arrange, in a -manner consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have actual +Also add information on how to contact you by electronic and paper mail. -knowledge that, but for the patent license, your conveying the covered work in a -country, or your recipient's use of the covered work in a country, would infringe -one or more identifiable patents in that country that you have reason to believe +You should also get your employer (if you work as a programmer) or your school, -are valid. +if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a +sample; alter the names: -If, pursuant to or in connection with a single transaction or arrangement, you -convey, or propagate by procuring conveyance of, a covered work, and grant a + Yoyodyne, Inc., hereby disclaims all copyright interest in -patent license to some of the parties receiving the covered work authorizing them -to use, propagate, modify or convey a specific copy of the covered work, then the -patent license you grant is automatically extended to all recipients of the + the library `Frob' (a library for tweaking knobs) written -covered work and works based on it. + by James Random Hacker. -A patent license is "discriminatory" if it does not include within the scope of -its coverage, prohibits the exercise of, or is conditioned on the non-exercise of -one or more of the rights that are specifically granted under this License. You + signature of Ty Coon, 1 April 1990 -may not convey a covered work if you are a party to an arrangement with a third -party that is in the business of distributing software, under which you make -payment to the third party based on the extent of your activity of conveying the + Ty Coon, President of Vice -work, and under which the third party grants, to any of the parties who would -receive the covered work from you, a discriminatory patent license (a) in -connection with copies of the covered work conveyed by you (or copies made from +That's all there is to it! -those copies), or (b) primarily for and in connection with specific products or +--- -compilations that contain the covered work, unless you entered into that +ISC License -arrangement, or that patent license was granted, prior to 28 March 2007. +(containerd/containerd v2.0.5, containerd/containerd v2.1.3) +ISC License (ISCL) +================== -Nothing in this License shall be construed as excluding or limiting any implied -license or other defenses to infringement that may otherwise be available to you -under applicable patent law. +Copyright (c) 4-digit year, Company or Person's Name -12. No Surrender of Others' Freedom. +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. -If conditions are imposed on you (whether by court order, agreement or otherwise) -that contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot convey a covered work so as to satisfy +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -simultaneously your obligations under this License and any other pertinent +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -obligations, then as a consequence you may not convey it at all. For example, if +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -you agree to terms that obligate you to collect a royalty for further conveying +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -from those to whom you convey the Program, the only way you could satisfy both +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -those terms and this License would be to refrain entirely from conveying the +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -Program. +THIS SOFTWARE. +--- +ISC License -13. Remote Network Interaction; Use with the GNU General Public License. +(go-spew 20180930-snapshot-d8f796af) +ISC License -Notwithstanding any other provision of this License, if you modify the Program, -your modified version must prominently offer all users interacting with it +Copyright (c) 2012-2016 Dave Collins -remotely through a computer network (if your version supports such interaction) -an opportunity to receive the Corresponding Source of your version by providing -access to the Corresponding Source from a network server at no charge, through +Permission to use, copy, modify, and/or distribute this software for any -some standard or customary means of facilitating copying of software. This +purpose with or without fee is hereby granted, provided that the above -Corresponding Source shall include the Corresponding Source for any work covered +copyright notice and this permission notice appear in all copies. -by version 3 of the GNU General Public License that is incorporated pursuant to -the following paragraph. +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -Notwithstanding any other provision of this License, you have permission to link +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -or combine any covered work with a work licensed under version 3 of the GNU +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -General Public License into a single combined work, and to convey the resulting +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -work. The terms of this License will continue to apply to the part which is the +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -covered work, but the work with which it is combined will remain governed by +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE -version 3 of the GNU General Public License. +--- +MIT License +(josharian/intern v1.0.0) -14. Revised Versions of this License. +MIT License -The Free Software Foundation may publish revised and/or new versions of the GNU +Copyright (c) 2019 Josh Bleecher Snyder -Affero General Public License from time to time. Such new versions will be -similar in spirit to the present version, but may differ in detail to address new -problems or concerns. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights -Each version is given a distinguishing version number. If the Program specifies +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -that a certain numbered version of the GNU Affero General Public License "or any +copies of the Software, and to permit persons to whom the Software is -later version" applies to it, you have the option of following the terms and +furnished to do so, subject to the following conditions: -conditions either of that numbered version or of any later version published by -the Free Software Foundation. If the Program does not specify a version number of -the GNU Affero General Public License, you may choose any version ever published - -by the Free Software Foundation. - - - -If the Program specifies that a proxy can decide which future versions of the GNU - -Affero General Public License can be used, that proxy's public statement of - -acceptance of a version permanently authorizes you to choose that version for the - -Program. - - - -Later license versions may give you additional or different permissions. However, - -no additional obligations are imposed on any author or copyright holder as a - -result of your choosing to follow a later version. - - - -15. Disclaimer of Warranty. - - - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. - -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER - -PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER - -EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE - -QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE - -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - - -16. Limitation of Liability. - - - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY - -COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS - -PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, - -INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE - -THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED - -INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE - -PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY - -HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - - -17. Interpretation of Sections 15 and 16. - - - -If the disclaimer of warranty and limitation of liability provided above cannot - -be given local legal effect according to their terms, reviewing courts shall - -apply local law that most closely approximates an absolute waiver of all civil - -liability in connection with the Program, unless a warranty or assumption of - -liability accompanies a copy of the Program in return for a fee. - - - -END OF TERMS AND CONDITIONS - - - - - -How to Apply These Terms to Your New Programs - - - -If you develop a new program, and you want it to be of the greatest possible use - -to the public, the best way to achieve this is to make it free software which - -everyone can redistribute and change under these terms. - - - -To do so, attach the following notices to the program. It is safest to attach - -them to the start of each source file to most effectively state the exclusion of - -warranty; and each file should have at least the "copyright" line and a pointer - -to where the full notice is found. - - - - - - - - Copyright (C) - - - - This program is free software: you can redistribute it and/or modify - - it under the terms of the GNU Affero General Public License as - - published by the Free Software Foundation, either version 3 of the - - License, or (at your option) any later version. - - - - This program is distributed in the hope that it will be useful, - - but WITHOUT ANY WARRANTY; without even the implied warranty of - - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - - GNU Affero General Public License for more details. - - - - You should have received a copy of the GNU Affero General Public License - - along with this program. If not, see . - - - -Also add information on how to contact you by electronic and paper mail. - - - -If your software can interact with users remotely through a computer network, you - -should also make sure that it provides a way for users to get its source. For - -example, if your program is a web application, its interface could display a - -"Source" link that leads users to an archive of the code. There are many ways you - -could offer source, and different solutions will be better for different - -programs; see section 13 for the specific requirements. - - - -You should also get your employer (if you work as a programmer) or school, if - -any, to sign a "copyright disclaimer" for the program, if necessary. For more - -information on this, and how to apply and follow the GNU AGPL, see - -. - ---- - -ISC License - -(containerd/containerd v2.0.5, containerd/containerd v2.1.0, podman 5.2.1) - -ISC License (ISCL) - -================== - - - -Copyright (c) 4-digit year, Company or Person's Name - - - -Permission to use, copy, modify, and/or distribute this software for any purpose - -with or without fee is hereby granted, provided that the above copyright notice - -and this permission notice appear in all copies. - - - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - -THIS SOFTWARE. - ---- - -ISC License - -(go-spew 20180930-snapshot-d8f796af) - -ISC License - - - -Copyright (c) 2012-2016 Dave Collins - - - -Permission to use, copy, modify, and/or distribute this software for any - -purpose with or without fee is hereby granted, provided that the above - -copyright notice and this permission notice appear in all copies. - - - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE - ---- - -MIT License - -(josharian/intern v1.0.0) - -MIT License - - - -Copyright (c) 2019 Josh Bleecher Snyder - - - -Permission is hereby granted, free of charge, to any person obtaining a copy - -of this software and associated documentation files (the "Software"), to deal - -in the Software without restriction, including without limitation the rights - -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - -copies of the Software, and to permit persons to whom the Software is - -furnished to do so, subject to the following conditions: - - - -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. @@ -4097,7 +3631,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +SOFTWARE --- @@ -4145,27 +3679,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE - ---- - -MIT License - -(mailru/easyjson v0.7.7) - -Copyright (c) 2016 Mail.Ru Group - - - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +SOFTWARE --- @@ -4211,7 +3725,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --- @@ -4259,7 +3773,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +SOFTWARE --- @@ -4279,7 +3793,7 @@ The above copyright notice and this permission notice shall be included in all c -THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --- @@ -4325,7 +3839,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --- @@ -4375,7 +3889,7 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE +OTHER DEALINGS IN THE SOFTWARE --- @@ -4423,7 +3937,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +SOFTWARE --- @@ -4467,7 +3981,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE +THE SOFTWARE --- @@ -4527,7 +4041,7 @@ Permission is hereby granted, free of charge, to any person obtaining a copy OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE + SOFTWARE --- @@ -4571,7 +4085,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +SOFTWARE --- @@ -4595,53 +4109,97 @@ The above copyright notice and this permission notice shall be included in all c -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --- MIT License -(GoDoc Text v0.2.0) +(olekukonko-tablewriter v0.0.5) -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Copyright (C) 2014 by Oleku Konko -Upstream-Name: github.com/kr/text -Source: https://github.com/kr/text/ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal -Files: * +in the Software without restriction, including without limitation the rights -Copyright: 2013 Keith Rarick +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -License: Expat +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Files: debian/* -Copyright: 2013 Tonnerre Lombard +The above copyright notice and this permission notice shall be included in -License: Expat +all copies or substantial portions of the Software. -License: Expat +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -Permission is hereby granted, free of charge, to any person obtaining a copy +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - of this software and associated documentation files (the "Software"), to deal +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - in the Software without restriction, including without limitation the rights +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +THE SOFTWARE - copies of the Software, and to permit persons to whom the Software is +--- - furnished to do so, subject to the following conditions: +MIT License + +(GoDoc Text v0.2.0) + +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Upstream-Name: github.com/kr/text + +Source: https://github.com/kr/text/ + + + +Files: * + +Copyright: 2013 Keith Rarick + +License: Expat + + + +Files: debian/* + +Copyright: 2013 Tonnerre Lombard + +License: Expat + + + +License: Expat + + + +Permission is hereby granted, free of charge, to any person obtaining a copy + + of this software and associated documentation files (the "Software"), to deal + + in the Software without restriction, including without limitation the rights + + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + + copies of the Software, and to permit persons to whom the Software is + + furnished to do so, subject to the following conditions: . @@ -4663,13 +4221,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE + THE SOFTWARE --- MIT License -(alecthomas-kingpin v2.4.0, alecthomas-units 20240927-snapshot-0f3dac36, Azure/azure-sdk-for-go 20240522-snapshot, Azure/azure-sdk-for-go 20250120-snapshot, Azure/azure-sdk-for-go sdk/azcore/v1.17.0, Azure/azure-sdk-for-go sdk/azidentity/v1.7.0, Azure/azure-sdk-for-go sdk/internal/v1.10.0, Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/compute/armcompute/v5.7.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerservice/armcontainerservice/v4.8.0, Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.4.0, Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0, Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.6.0, AzureAD/microsoft-authentication-library-for-go v1.2.2, cenkalti/backoff v4.3.0, cespare/xxhash v2.3.0, containerd/containerd v2.0.5, containerd/containerd v2.1.0, dominikh/go-tools 20190523-snapshot-ea95bdfd, felixge/httpsnoop v1.0.4, go humanize 20250512-snapshot-b48bc01a, Go Testify v1.10.0, go-restful v3.11.0, go-task/slim-sprig v3.0.0, go-zap v1.27.0, go.etcd.io/bbolt v1.3.11, go.uber.org/goleak v1.3.0, go.uber.org/multierr v1.11.0, golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd, golang-github-ghodss-yaml-dev 20240620-snapshot, golang-jwt/jwt v4.5.2, golang-jwt/jwt v5.2.2, golang-set 20250321-snapshot, golang-stats v0.7.0, gomega v1.35.1, govalidator 20230301-snapshot-a9d515a0, jarcoal/httpmock v1.3.1, kr/pretty v0.3.1, mapstructure v1.5.0, Microsoft-go-winio v0.6.0, mitchellh-hashstructure v2.0.2, natefinch/lumberjack v2.2.1, niemeyer/pretty 20200227-snapshot-a10e7cae, olekukonko-tablewriter 20230925-snapshot-df64c4bb, onsi/ginkgo 2.21.0, podman 5.2.1, rs-xid v1.6.0, secureheader v0.2.0, Sirupsen/logrus v1.9.3, stoewer/go-strcase v1.3.0, stretchr/objx v0.5.2, tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3, xiang90-probing 20221125-snapshot-a49e3df8, yaml for Go v3.0.1, youmark/pkcs8 20181117-snapshot-1be2e3e5, yuin/goldmark v1.4.13, zcalusic/sysinfo v1.1.3, zeebo/errs v1.4.0) +(alecthomas-kingpin v2.4.0, alecthomas-units 20240927-snapshot-0f3dac36, AstroProfundis/sysinfo 20211201-snapshot-9f959380, Azure/azure-sdk-for-go 20240522-snapshot, Azure/azure-sdk-for-go sdk/azcore/v1.18.0, Azure/azure-sdk-for-go sdk/azidentity/v1.8.2, Azure/azure-sdk-for-go sdk/internal/v1.11.0, Azure/azure-sdk-for-go sdk/resourcemanager/authorization/armauthorization/v2.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/compute/armcompute/v5.7.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerregistry/armcontainerregistry/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/containerservice/armcontainerservice/v4.8.0, Azure/azure-sdk-for-go sdk/resourcemanager/keyvault/armkeyvault/v1.4.0, Azure/azure-sdk-for-go sdk/resourcemanager/managementgroups/armmanagementgroups/v1.0.0, Azure/azure-sdk-for-go sdk/resourcemanager/privatedns/armprivatedns/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resourcegraph/armresourcegraph/v0.9.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armfeatures/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/resources/armresources/v1.2.0, Azure/azure-sdk-for-go sdk/resourcemanager/storage/armstorage/v1.6.0, AzureAD/microsoft-authentication-library-for-go 20250410-snapshot, AzureAD/microsoft-authentication-library-for-go v1.4.2, cenkalti/backoff v4.3.0, cespare/xxhash v2.3.0, cli/cli v2.76.1, containerd/containerd v2.0.5, containerd/containerd v2.1.3, dgryski/go-rendezvous 20200823-snapshot-9f7001d1, dominikh/go-tools 20190523-snapshot-ea95bdfd, felixge/httpsnoop v1.0.4, fxamacker/cbor v2.9.0, go humanize 20250512-snapshot-b48bc01a, Go Testify 20250907-snapshot, Go Testify v1.11.1, go-restful v3.12.2, go-task/slim-sprig v3.0.0, go-zap v1.27.0, go.uber.org/goleak v1.3.0, go.uber.org/multierr v1.11.0, go.yaml.in/yaml/v2 v2.4.2, go.yaml.in/yaml/v2 v3.0.4, golang-github-ghodss-yaml-dev 20210413-snapshot-d8423dcd, golang-github-ghodss-yaml-dev 20240620-snapshot, golang-jwt/jwt v5.2.2, golang-set 20250321-snapshot, golang-set v2.8.0, golang-stats v0.7.1, gomega v1.35.1, govalidator 20230301-snapshot-a9d515a0, jarcoal/httpmock v1.4.0, keybase/go-keychain 20231219-snapshot-57a3676c, kr/pretty v0.3.1, mailru/easyjson v0.9.0, mapstructure v1.5.0, Microsoft-go-winio v0.6.0, mitchellh-hashstructure v2.0.2, natefinch/lumberjack v2.2.1, niemeyer/pretty 20200227-snapshot-a10e7cae, onsi/ginkgo 2.21.0, rs-xid v1.6.0, secureheader v0.2.0, Sirupsen/logrus v1.9.3, stoewer/go-strcase v1.3.0, stretchr/objx v0.5.2, tmc/grpc-websocket-proxy 20220101-snapshot-673ab2c3, xiang90-probing 20221125-snapshot-a49e3df8, yaml for Go v3.0.1, youmark/pkcs8 20240726-snapshot-a2c0da24, yuin/goldmark v1.4.13, zcalusic/sysinfo 20250716-snapshot, zcalusic/sysinfo v1.1.3, zeebo/errs v1.4.0) The MIT License @@ -4711,7 +4269,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER I AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- @@ -4757,7 +4315,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE --- @@ -4805,7 +4363,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE +SOFTWARE --- @@ -4853,55 +4411,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE - ---- - -MIT License - -(mitchellh-reflectwalk v1.0.2) - -The MIT License (MIT) - - - -Copyright (c) 2013 Mitchell Hashimoto - - - -Permission is hereby granted, free of charge, to any person obtaining a copy - -of this software and associated documentation files (the "Software"), to deal - -in the Software without restriction, including without limitation the rights - -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - -copies of the Software, and to permit persons to whom the Software is - -furnished to do so, subject to the following conditions: - - - -The above copyright notice and this permission notice shall be included in - -all copies or substantial portions of the Software. - - - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - -THE SOFTWARE +THE SOFTWARE --- @@ -4949,55 +4459,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE - ---- - -MIT License - -(mitchellh-copystructure v1.2.0) - -The MIT License (MIT) - - - -Copyright (c) 2014 Mitchell Hashimoto - - - -Permission is hereby granted, free of charge, to any person obtaining a copy - -of this software and associated documentation files (the "Software"), to deal - -in the Software without restriction, including without limitation the rights - -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - -copies of the Software, and to permit persons to whom the Software is - -furnished to do so, subject to the following conditions: - - - -The above copyright notice and this permission notice shall be included in - -all copies or substantial portions of the Software. - - - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - -THE SOFTWARE +SOFTWARE --- @@ -5045,828 +4507,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE - ---- - -Mozilla Public License 2.0 - -(podman 5.2.1) - -Mozilla Public License - -Version 2.0 - -====================== - - - - - -1. Definitions - --------------- - - - - 1.1. "Contributor" - - - - means each individual or legal entity that creates, contributes to the creation - - of, or owns Covered Software. - - - - 1.2. "Contributor Version" - - - - means the combination of the Contributions of others (if any) used by a - - Contributor and that particular Contributor's Contribution. - - - - 1.3. "Contribution" - - - - means Covered Software of a particular Contributor. - - - - 1.4. "Covered Software" - - - - means Source Code Form to which the initial Contributor has attached the notice - - in Exhibit A, the Executable Form of such Source Code Form, and Modifications - - of such Source Code Form, in each case including portions thereof. - - - - 1.5. "Incompatible With Secondary Licenses" - - - - means - - - - a. - - - - that the initial Contributor has attached the notice described in Exhibit B - - to the Covered Software; or - - - - b. - - - - that the Covered Software was made available under the terms of version 1.1 - - or earlier of the License, but not also under the terms of a Secondary - - License. - - - - 1.6. "Executable Form" - - - - means any form of the work other than Source Code Form. - - - - 1.7. "Larger Work" - - - - means a work that combines Covered Software with other material, in a separate - - file or files, that is not Covered Software. - - - - 1.8. "License" - - - - means this document. - - - - 1.9. "Licensable" - - - - means having the right to grant, to the maximum extent possible, whether at the - - time of the initial grant or subsequently, any and all of the rights conveyed - - by this License. - - - - 1.10. "Modifications" - - - - means any of the following: - - - - a. - - - - any file in Source Code Form that results from an addition to, deletion - - from, or modification of the contents of Covered Software; or - - - - b. - - - - any new file in Source Code Form that contains any Covered Software. - - - - 1.11. "Patent Claims" of a Contributor - - - - means any patent claim(s), including without limitation, method, process, and - - apparatus claims, in any patent Licensable by such Contributor that would be - - infringed, but for the grant of the License, by the making, using, selling, - - offering for sale, having made, import, or transfer of either its Contributions - - or its Contributor Version. - - - - 1.12. "Secondary License" - - - - means either the GNU General Public License, Version 2.0, the GNU Lesser - - General Public License, Version 2.1, the GNU Affero General Public License, - - Version 3.0, or any later versions of those licenses. - - - - 1.13. "Source Code Form" - - - - means the form of the work preferred for making modifications. - - - - 1.14. "You" (or "Your") - - - - means an individual or a legal entity exercising rights under this License. For - - legal entities, "You" includes any entity that controls, is controlled by, or - - is under common control with You. For purposes of this definition, "control" - - means (a) the power, direct or indirect, to cause the direction or management - - of such entity, whether by contract or otherwise, or (b) ownership of more than - - fifty percent (50%) of the outstanding shares or beneficial ownership of such - - entity. - - - - - -2. License Grants and Conditions - --------------------------------- - - - - - - 2.1. Grants - - - - Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive - - license: - - - - a. - - - - under intellectual property rights (other than patent or trademark) - - Licensable by such Contributor to use, reproduce, make available, modify, - - display, perform, distribute, and otherwise exploit its Contributions, - - either on an unmodified basis, with Modifications, or as part of a Larger - - Work; and - - - - b. - - - - under Patent Claims of such Contributor to make, use, sell, offer for sale, - - have made, import, and otherwise transfer either its Contributions or its - - Contributor Version. - - - - - - 2.2. Effective Date - - - - The licenses granted in Section2.1 with respect to any Contribution become - - effective for each Contribution on the date the Contributor first distributes - - such Contribution. - - - - - - 2.3. Limitations on Grant Scope - - - - The licenses granted in this Section2 are the only rights granted under this - - License. No additional rights or licenses will be implied from the distribution - - or licensing of Covered Software under this License. Notwithstanding - - Section2.1(b) above, no patent license is granted by a Contributor: - - - - a. - - - - for any code that a Contributor has removed from Covered Software; or - - - - b. - - - - for infringements caused by: (i) Your and any other third party's - - modifications of Covered Software, or (ii) the combination of its - - Contributions with other software (except as part of its Contributor - - Version); or - - - - c. - - - - under Patent Claims infringed by Covered Software in the absence of its - - Contributions. - - - - This License does not grant any rights in the trademarks, service marks, or - - logos of any Contributor (except as may be necessary to comply with the notice - - requirements in Section3.4). - - - - - - 2.4. Subsequent Licenses - - - - No Contributor makes additional grants as a result of Your choice to distribute - - the Covered Software under a subsequent version of this License (see - - Section10.2) or under the terms of a Secondary License (if permitted under the - - terms of Section3.3). - - - - - - 2.5. Representation - - - - Each Contributor represents that the Contributor believes its Contributions are - - its original creation(s) or it has sufficient rights to grant the rights to its - - Contributions conveyed by this License. - - - - - - 2.6. Fair Use - - - - This License is not intended to limit any rights You have under applicable - - copyright doctrines of fair use, fair dealing, or other equivalents. - - - - - - 2.7. Conditions - - - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - - Section2.1. - - - - - -3. Responsibilities - -------------------- - - - - - - 3.1. Distribution of Source Form - - - - All distribution of Covered Software in Source Code Form, including any - - Modifications that You create or to which You contribute, must be under the - - terms of this License. You must inform recipients that the Source Code Form of - - the Covered Software is governed by the terms of this License, and how they can - - obtain a copy of this License. You may not attempt to alter or restrict the - - recipients' rights in the Source Code Form. - - - - - - 3.2. Distribution of Executable Form - - - - If You distribute Covered Software in Executable Form then: - - - - a. - - - - such Covered Software must also be made available in Source Code Form, as - - described in Section3.1, and You must inform recipients of the Executable - - Form how they can obtain a copy of such Source Code Form by reasonable - - means in a timely manner, at a charge no more than the cost of distribution - - to the recipient; and - - - - b. - - - - You may distribute such Executable Form under the terms of this License, or - - sublicense it under different terms, provided that the license for the - - Executable Form does not attempt to limit or alter the recipients' rights - - in the Source Code Form under this License. - - - - - - 3.3. Distribution of a Larger Work - - - - You may create and distribute a Larger Work under terms of Your choice, - - provided that You also comply with the requirements of this License for the - - Covered Software. If the Larger Work is a combination of Covered Software with - - a work governed by one or more Secondary Licenses, and the Covered Software is - - not Incompatible With Secondary Licenses, this License permits You to - - additionally distribute such Covered Software under the terms of such Secondary - - License(s), so that the recipient of the Larger Work may, at their option, - - further distribute the Covered Software under the terms of either this License - - or such Secondary License(s). - - - - - - 3.4. Notices - - - - You may not remove or alter the substance of any license notices (including - - copyright notices, patent notices, disclaimers of warranty, or limitations of - - liability) contained within the Source Code Form of the Covered Software, - - except that You may alter any license notices to the extent required to remedy - - known factual inaccuracies. - - - - - - 3.5. Application of Additional Terms - - - - You may choose to offer, and to charge a fee for, warranty, support, indemnity - - or liability obligations to one or more recipients of Covered Software. - - However, You may do so only on Your own behalf, and not on behalf of any - - Contributor. You must make it absolutely clear that any such warranty, support, - - indemnity, or liability obligation is offered by You alone, and You hereby - - agree to indemnify every Contributor for any liability incurred by such - - Contributor as a result of warranty, support, indemnity or liability terms You - - offer. You may include additional disclaimers of warranty and limitations of - - liability specific to any jurisdiction. - - - - - -4. Inability to Comply Due to Statute or Regulation - ---------------------------------------------------- - - - -If it is impossible for You to comply with any of the terms of this License with - -respect to some or all of the Covered Software due to statute, judicial order, or - -regulation then You must: (a) comply with the terms of this License to the - -maximum extent possible; and (b) describe the limitations and the code they - -affect. Such description must be placed in a text file included with all - -distributions of the Covered Software under this License. Except to the extent - -prohibited by statute or regulation, such description must be sufficiently - -detailed for a recipient of ordinary skill to be able to understand it. - - - - - -5. Termination - --------------- - - - - 5.1. The rights granted under this License will terminate automatically if You - - fail to comply with any of its terms. However, if You become compliant, then - - the rights granted under this License from a particular Contributor are - - reinstated (a) provisionally, unless and until such Contributor explicitly and - - finally terminates Your grants, and (b) on an ongoing basis, if such - - Contributor fails to notify You of the non-compliance by some reasonable means - - prior to 60 days after You have come back into compliance. Moreover, Your - - grants from a particular Contributor are reinstated on an ongoing basis if such - - Contributor notifies You of the non-compliance by some reasonable means, this - - is the first time You have received notice of non-compliance with this License - - from such Contributor, and You become compliant prior to 30 days after Your - - receipt of the notice. - - - - 5.2. If You initiate litigation against any entity by asserting a patent - - infringement claim (excluding declaratory judgment actions, counter-claims, and - - cross-claims) alleging that a Contributor Version directly or indirectly - - infringes any patent, then the rights granted to You by any and all - - Contributors for the Covered Software under Section2.1 of this License shall - - terminate. - - - - 5.3. In the event of termination under Sections5.1 or 5.2 above, all end user - - license agreements (excluding distributors and resellers) which have been - - validly granted by You or Your distributors under this License prior to - - termination shall survive termination. - - - - - -6. Disclaimer of Warranty - -------------------------- - - - -Covered Software is provided under this License on an "as is" basis, without - -warranty of any kind, either expressed, implied, or statutory, including, without - -limitation, warranties that the Covered Software is free of defects, - -merchantable, fit for a particular purpose or non-infringing. The entire risk as - -to the quality and performance of the Covered Software is with You. Should any - -Covered Software prove defective in any respect, You (not any Contributor) assume - -the cost of any necessary servicing, repair, or correction. This disclaimer of - -warranty constitutes an essential part of this License. No use of any Covered - -Software is authorized under this License except under this disclaimer. - - - - - -7. Limitation of Liability - --------------------------- - - - -Under no circumstances and under no legal theory, whether tort (including - -negligence), contract, or otherwise, shall any Contributor, or anyone who - -distributes Covered Software as permitted above, be liable to You for any direct, - -indirect, special, incidental, or consequential damages of any character - -including, without limitation, damages for lost profits, loss of goodwill, work - -stoppage, computer failure or malfunction, or any and all other commercial - -damages or losses, even if such party shall have been informed of the possibility - -of such damages. This limitation of liability shall not apply to liability for - -death or personal injury resulting from such party's negligence to the extent - -applicable law prohibits such limitation. Some jurisdictions do not allow the - -exclusion or limitation of incidental or consequential damages, so this exclusion - -and limitation may not apply to You. - - - - - -8. Litigation - -------------- - - - -Any litigation relating to this License may be brought only in the courts of a - -jurisdiction where the defendant maintains its principal place of business and - -such litigation shall be governed by laws of that jurisdiction, without reference - -to its conflict-of-law provisions. Nothing in this Section shall prevent a - -party's ability to bring cross-claims or counter-claims. - - - - - -9. Miscellaneous - ----------------- - - - -This License represents the complete agreement concerning the subject matter - -hereof. If any provision of this License is held to be unenforceable, such - -provision shall be reformed only to the extent necessary to make it enforceable. - -Any law or regulation which provides that the language of a contract shall be - -construed against the drafter shall not be used to construe this License against - -a Contributor. - - - - - -10. Versions of the License - ---------------------------- - - - - - - 10.1. New Versions - - - - Mozilla Foundation is the license steward. Except as provided in Section10.3, - - no one other than the license steward has the right to modify or publish new - - versions of this License. Each version will be given a distinguishing version - - number. - - - - - - 10.2. Effect of New Versions - - - - You may distribute the Covered Software under the terms of the version of the - - License under which You originally received the Covered Software, or under the - - terms of any subsequent version published by the license steward. - - - - - - 10.3. Modified Versions - - - - If you create software not governed by this License, and you want to create a - - new license for such software, you may create and use a modified version of - - this License if you rename the license and remove any references to the name of - - the license steward (except to note that such modified license differs from - - this License). - - - - - - 10.4. Distributing Source Code Form that is Incompatible With Secondary - - Licenses - - - - If You choose to distribute Source Code Form that is Incompatible With - - Secondary Licenses under the terms of this version of the License, the notice - - described in Exhibit B of this License must be attached. - - - - - -Exhibit A - Source Code Form License Notice - -------------------------------------------- - - - - This Source Code Form is subject to the terms of the Mozilla Public License, - - v. 2.0. If a copy of the MPL was not distributed with this file, You can - - obtain one at http://mozilla.org/MPL/2.0/. - - - -If it is not possible or desirable to put the notice in a particular file, then - -You may include the notice in a location (such as a LICENSE file in a relevant - -directory) where a recipient would be likely to look for such a notice. - - - -You may add additional accurate notices of copyright ownership. - - - - - -Exhibit B - "Incompatible With Secondary Licenses" Notice - ---------------------------------------------------------- - - - - This Source Code Form is "Incompatible With Secondary Licenses", as defined - - by the Mozilla Public License, v. 2.0. +THE SOFTWARE --- \ No newline at end of file From d771e94901e481fe996db611bf8b1b9404118734 Mon Sep 17 00:00:00 2001 From: VinayKumarHavanur <54576364+VinayKumarHavanur@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:03:26 +0530 Subject: [PATCH 16/30] Implement unique subsystem names for regular ONTAP NVMe driver --- storage_drivers/ontap/api/abstraction_rest.go | 29 +++++++- .../ontap/api/abstraction_rest_test.go | 74 +++++++++++++++++++ storage_drivers/ontap/ontap_common.go | 3 + storage_drivers/ontap/ontap_common_test.go | 20 +++++ storage_drivers/ontap/ontap_san_nvme.go | 35 +++++---- storage_drivers/ontap/ontap_san_nvme_test.go | 36 ++------- 6 files changed, 151 insertions(+), 46 deletions(-) diff --git a/storage_drivers/ontap/api/abstraction_rest.go b/storage_drivers/ontap/api/abstraction_rest.go index fec275ea9..8e03e48a5 100644 --- a/storage_drivers/ontap/api/abstraction_rest.go +++ b/storage_drivers/ontap/api/abstraction_rest.go @@ -3520,6 +3520,7 @@ func (d OntapAPIREST) NVMeEnsureNamespaceMapped(ctx context.Context, subsystemUU // NVMeEnsureNamespaceUnmapped first checks if a namespace is mapped to the subsystem and if it is mapped: // a) removes the namespace from the subsystem // b) deletes the subsystem if no more namespaces are attached to it +// c) Handles the case where multiple hosts are mapped to single common subsystem arising out of lengthy node names. // If namespace is not mapped to subsystem, it is treated as success // The function also returns a bool value along with error. A true value denotes the subsystem is deleted // successfully and Published info can be removed for the NVMe volume @@ -3549,8 +3550,15 @@ func (d OntapAPIREST) NVMeEnsureNamespaceUnmapped(ctx context.Context, hostNQN, } } - // In case of multiple hosts attached to a subsystem (e.g. in RWX case), do not delete the namespace, - // subsystem or the published info + // Below are the cases where multiple hosts are attached to a subsystem: + // case 1: RWX case - Multiple hosts are intentionally sharing the same namespace for ReadWriteMany access. + // In this scenario, do not delete the namespace mapping, as other hosts may still be using it. + // case 2: Common subsystem due to lengthy host names - When host names exceed the allowed length, + // multiple hosts may be mapped to a single "common" subsystem. In this scenario, the subsystem + // is not truly shared for RWX purposes, but is a result of host name truncation. Therefore, + // it is appropriate to remove the namespace mapping when detaching a host, as the mapping is + // not required for other hosts. This differs from the RWX case, where the namespace mapping + // must be preserved for legitimate multi-host access. if len(subsystemHosts) > 1 { if hostFound { Logc(ctx).Infof("Multiple hosts are attached to this subsystem %v. Do not delete namespace or subsystem", @@ -3560,6 +3568,21 @@ func (d OntapAPIREST) NVMeEnsureNamespaceUnmapped(ctx context.Context, hostNQN, return false, err } } + + // Check if there are multiple namespaces attached to the subsystem, + // This indicates case 2, so, remove the namespace only. + count, err := d.api.NVMeNamespaceCount(ctx, subsystemUUID) + if err != nil { + return false, fmt.Errorf("error getting namespace count for subsystem %s; %v", subsystemUUID, err) + } + + if count > 1 { + err = d.api.NVMeSubsystemRemoveNamespace(ctx, subsystemUUID, namespaceUUID) + if err != nil { + return false, fmt.Errorf("error removing namespace %s from subsystem %s; %v", namespaceUUID, subsystemUUID, err) + } + } + return false, nil } @@ -3583,7 +3606,7 @@ func (d OntapAPIREST) NVMeEnsureNamespaceUnmapped(ctx context.Context, hostNQN, return false, fmt.Errorf("error getting namespace count for subsystem %s; %v", subsystemUUID, err) } - // Delete the subsystem if no. of namespaces is 0 + // Delete the subsystem if the namespace count for this subsystem is 0 after removal. if count == 0 { if err := d.api.NVMeSubsystemDelete(ctx, subsystemUUID); err != nil { return false, fmt.Errorf("error deleting subsystem %s; %v", subsystemUUID, err) diff --git a/storage_drivers/ontap/api/abstraction_rest_test.go b/storage_drivers/ontap/api/abstraction_rest_test.go index 91613c001..e10e7f8fb 100644 --- a/storage_drivers/ontap/api/abstraction_rest_test.go +++ b/storage_drivers/ontap/api/abstraction_rest_test.go @@ -964,6 +964,7 @@ func TestNVMeNamespaceUnmapped(t *testing.T) { mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1, host2}, nil).Times(1) mock.EXPECT().NVMeRemoveHostFromSubsystem(ctx, hostNQN, subsystemUUID).Return(nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(1), nil).Times(1) removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, hostNQN, subsystemUUID, nsUUID) @@ -1035,11 +1036,84 @@ func TestNVMeNamespaceUnmapped(t *testing.T) { mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1, host2}, nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(1), nil).Times(1) removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, nonExistentHostNQN, subsystemUUID, nsUUID) assert.Equal(t, false, removePublishInfo, "nqn is unmapped") assert.NoError(t, err) + + // case 13: Multiple hosts with error getting namespace count + mock.EXPECT().ClientConfig().Return(clientConfig).AnyTimes() + mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) + mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1, host2}, nil).Times(1) + mock.EXPECT().NVMeRemoveHostFromSubsystem(ctx, hostNQN, subsystemUUID).Return(nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(0), errors.New("Error getting namespace count")).Times(1) + + removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, hostNQN, subsystemUUID, nsUUID) + + assert.Equal(t, false, removePublishInfo, "subsystem removed") + assert.Error(t, err) + + // case 14: Multiple hosts with namespace count > 1, error removing namespace + mock.EXPECT().ClientConfig().Return(clientConfig).AnyTimes() + mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) + mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1, host2}, nil).Times(1) + mock.EXPECT().NVMeRemoveHostFromSubsystem(ctx, hostNQN, subsystemUUID).Return(nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(2), nil).Times(1) + mock.EXPECT().NVMeSubsystemRemoveNamespace(ctx, subsystemUUID, nsUUID).Return(errors.New("Error removing namespace")).Times(1) + + removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, hostNQN, subsystemUUID, nsUUID) + + assert.Equal(t, false, removePublishInfo, "subsystem removed") + assert.Error(t, err) + + // case 15: Multiple hosts with namespace count > 1, success removing namespace + mock.EXPECT().ClientConfig().Return(clientConfig).AnyTimes() + mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) + mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1, host2}, nil).Times(1) + mock.EXPECT().NVMeRemoveHostFromSubsystem(ctx, hostNQN, subsystemUUID).Return(nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(3), nil).Times(1) + mock.EXPECT().NVMeSubsystemRemoveNamespace(ctx, subsystemUUID, nsUUID).Return(nil).Times(1) + + removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, hostNQN, subsystemUUID, nsUUID) + + assert.Equal(t, false, removePublishInfo, "subsystem is not removed due to multiple namespaces") + assert.NoError(t, err) + + // case 16: Multiple hosts with namespace count = 1 (RWX case), no removal of namespace or subsystem + mock.EXPECT().ClientConfig().Return(clientConfig).AnyTimes() + mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) + mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1, host2}, nil).Times(1) + mock.EXPECT().NVMeRemoveHostFromSubsystem(ctx, hostNQN, subsystemUUID).Return(nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(1), nil).Times(1) + + removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, hostNQN, subsystemUUID, nsUUID) + + assert.Equal(t, false, removePublishInfo, "subsystem is not removed in RWX case") + assert.NoError(t, err) + + // case 17: Single host but different from requested host (nonExistentHostNQN), subsystem and namespace not removed + mock.EXPECT().ClientConfig().Return(clientConfig).AnyTimes() + mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) + mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1}, nil).Times(1) + + removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, nonExistentHostNQN, subsystemUUID, nsUUID) + + assert.Equal(t, false, removePublishInfo, "subsystem is not removed when host doesn't match") + assert.NoError(t, err) + + // case 18: Namespace count > 0 after removal, subsystem not deleted + mock.EXPECT().ClientConfig().Return(clientConfig).AnyTimes() + mock.EXPECT().NVMeIsNamespaceMapped(ctx, subsystemUUID, nsUUID).Return(true, nil).Times(1) + mock.EXPECT().NVMeGetHostsOfSubsystem(ctx, subsystemUUID).Return([]*models.NvmeSubsystemHost{host1}, nil).Times(1) + mock.EXPECT().NVMeSubsystemRemoveNamespace(ctx, subsystemUUID, nsUUID).Return(nil).Times(1) + mock.EXPECT().NVMeNamespaceCount(ctx, subsystemUUID).Return(int64(2), nil).Times(1) + + removePublishInfo, err = oapi.NVMeEnsureNamespaceUnmapped(ctx, hostNQN, subsystemUUID, nsUUID) + + assert.Equal(t, true, removePublishInfo, "subsystem has remaining namespaces") + assert.NoError(t, err) } func TestNVMeIsNamespaceMapped(t *testing.T) { diff --git a/storage_drivers/ontap/ontap_common.go b/storage_drivers/ontap/ontap_common.go index c929b7dfd..0c8ff58c8 100644 --- a/storage_drivers/ontap/ontap_common.go +++ b/storage_drivers/ontap/ontap_common.go @@ -5438,6 +5438,8 @@ func getUniqueNodeSpecificSubsystemName( // Construct the subsystem name completeSSName := fmt.Sprintf("%s_%s_%s", prefix, nodeName, tridentUUID) + // Skip any underscores at the beginning + completeSSName = strings.TrimLeft(completeSSName, "_") finalSSName := completeSSName // Ensure the final name does not exceed the maximum length @@ -5451,6 +5453,7 @@ func getUniqueNodeSpecificSubsystemName( base64Str := base64.StdEncoding.EncodeToString(u[:]) finalSSName = fmt.Sprintf("%s_%s_%s", prefix, nodeName, base64Str) + finalSSName = strings.TrimLeft(finalSSName, "_") if len(finalSSName) > maxSubsystemLength { // If even after Trident UUID reduction, the length is more than max length, diff --git a/storage_drivers/ontap/ontap_common_test.go b/storage_drivers/ontap/ontap_common_test.go index 0fabf9611..43bb7eace 100644 --- a/storage_drivers/ontap/ontap_common_test.go +++ b/storage_drivers/ontap/ontap_common_test.go @@ -9824,6 +9824,26 @@ func TestGetUniqueNodeSpecificSubsystemName(t *testing.T) { expectedFinal: "", expectError: true, }, + { + description: "Valid input, with no prefix passed", + nodeName: "node1", + tridentUUID: tridentUUID, + prefix: "", + maxSubsystemLength: 64, + expectedBestCase: fmt.Sprintf("node1_%s", tridentUUID), + expectedFinal: fmt.Sprintf("node1_%s", tridentUUID), + expectError: false, + }, + { + description: "Even hash is more than limit, truncation needed with no prefix", + nodeName: "averylongnodenameexceedingthelimit", + tridentUUID: tridentUUID, + prefix: "", + maxSubsystemLength: 32, + expectedBestCase: fmt.Sprintf("averylongnodenameexceedingthelimit_%s", tridentUUID), + expectedFinal: fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("averylongnodenameexceedingthelimit_%s", tridentUUID))))[:32], + expectError: false, + }, } for _, test := range tests { diff --git a/storage_drivers/ontap/ontap_san_nvme.go b/storage_drivers/ontap/ontap_san_nvme.go index 0e45a14a4..f30d7c210 100644 --- a/storage_drivers/ontap/ontap_san_nvme.go +++ b/storage_drivers/ontap/ontap_san_nvme.go @@ -58,6 +58,8 @@ const ( defaultNamespaceBlockSize = 4096 // maximumSubsystemNameLength represent the max length of subsystem name maximumSubsystemNameLength = 64 + // nvmeSubsystemPrefix Subsystem prefix for ONTAP NVMe driver (empty for legacy compatibility). + nvmeSubsystemPrefix = "" ) // Namespace attributes stored in its comment field. These fields are useful for docker context. @@ -923,6 +925,7 @@ func (d *NVMeStorageDriver) Publish( // When FS type is RAW, we create a new subsystem per namespace, // else we use the subsystem created for that particular node var ssName string + var completeSSName string if volConfig.FileSystem == filesystem.Raw { ssName = getNamespaceSpecificSubsystemName(name, pvName) } else { @@ -931,13 +934,24 @@ func (d *NVMeStorageDriver) Publish( if tridentconfig.CurrentDriverContext == tridentconfig.ContextDocker { ssName = d.getStoragePrefixSubsystemName() } else { - ssName = d.getNodeSpecificSubsystemName(publishInfo.HostName, publishInfo.TridentUUID) + if completeSSName, ssName, err = getUniqueNodeSpecificSubsystemName( + publishInfo.HostName, publishInfo.TridentUUID, nvmeSubsystemPrefix, maximumSubsystemNameLength); err != nil { + return fmt.Errorf("failed to create node specific subsystem name: %w", err) + } } } + // Update the subsystem comment + var ssComment string + if completeSSName == "" { + ssComment = ssName + } else { + ssComment = completeSSName + } + // If 2 concurrent requests try to create the same subsystem, one will succeed and ONTAP is guaranteed to return // "already exists" error code for the other. So no need for locking around subsystem creation. - subsystem, err := d.createOrGetSubsystem(ctx, ssName) + subsystem, err := d.createOrGetSubsystem(ctx, ssName, ssComment) if err != nil { return err } @@ -1177,9 +1191,11 @@ func (d *NVMeStorageDriver) getStoragePoolAttributes(ctx context.Context) map[st } } -func (d *NVMeStorageDriver) createOrGetSubsystem(ctx context.Context, ssName string) (*api.NVMeSubsystem, error) { +func (d *NVMeStorageDriver) createOrGetSubsystem( + ctx context.Context, ssName, comment string, +) (*api.NVMeSubsystem, error) { // This checks if subsystem exists and creates it if not. - ss, err := d.API.NVMeSubsystemCreate(ctx, ssName, ssName) + ss, err := d.API.NVMeSubsystemCreate(ctx, ssName, comment) if err != nil { Logc(ctx).Errorf("subsystem create failed, %v", err) return nil, err @@ -1743,17 +1759,6 @@ func (d *NVMeStorageDriver) ParseNVMeNamespaceCommentString(_ context.Context, c return nil, fmt.Errorf("nsAttrs field not found in Namespace comment") } -func (d *NVMeStorageDriver) getNodeSpecificSubsystemName(nodeName, tridentUUID string) string { - // For CSI mode, use per-node subsystems - subsystemName := fmt.Sprintf("%s-%s", nodeName, tridentUUID) - if len(subsystemName) > maximumSubsystemNameLength { - // If the new subsystem name is over the subsystem character limit, it means the host name is too long. - subsystemPrefixLength := maximumSubsystemNameLength - len(tridentUUID) - 1 - subsystemName = fmt.Sprintf("%s-%s", nodeName[:subsystemPrefixLength], tridentUUID) - } - return subsystemName -} - // getStoragePrefixSubsystemName generates a stable subsystem name using storage prefix for Docker mode func (d *NVMeStorageDriver) getStoragePrefixSubsystemName() string { // Default for Docker is "netappdvp_", users can customize for isolation/sharing diff --git a/storage_drivers/ontap/ontap_san_nvme_test.go b/storage_drivers/ontap/ontap_san_nvme_test.go index 087f35f68..53ff12437 100644 --- a/storage_drivers/ontap/ontap_san_nvme_test.go +++ b/storage_drivers/ontap/ontap_san_nvme_test.go @@ -1500,26 +1500,6 @@ func TestGetNamespaceSpecificSubsystemName(t *testing.T) { assert.Equal(t, got_name, expected_name) } -func TestGetNodeSpecificSubsystemName(t *testing.T) { - d := newNVMeDriver(nil, nil, nil) - // case 1: subsystem, name is shorter than 64 char - nodeName := "fakeNodeName" - tridentUUID := "fakeUUID" - expected := "fakeNodeName-fakeUUID" - - got := d.getNodeSpecificSubsystemName(nodeName, tridentUUID) - - assert.Equal(t, got, expected) - - // case 2: subsystem name is longer than 64 char - nodeName = "fakeNodeNamefakeNodeNamefakeNodeNamefakeNodeNamefakeNodeNamefakeNodeNamefakeNodeNamefakeNodeName" - expected = "fakeNodeNamefakeNodeNamefakeNodeNamefakeNodeNamefakeNod-fakeUUID" - - got = d.getNodeSpecificSubsystemName(nodeName, tridentUUID) - - assert.Equal(t, got, expected) -} - func TestPublish(t *testing.T) { mockCtrl := gomock.NewController(t) mock := mockapi.NewMockOntapAPI(mockCtrl) @@ -1632,7 +1612,7 @@ func TestPublish(t *testing.T) { publishInfo.LUKSEncryption = "" volConfig.FileSystem = "" mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, errors.New("Error creating subsystem")).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, errors.New("Error creating subsystem")).Times(1) err = d.Publish(ctx, volConfig, publishInfo) @@ -1650,7 +1630,7 @@ func TestPublish(t *testing.T) { // case 8: Error while adding host nqn to subsystem publishInfo.HostNQN = "fakeHostNQN" mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(errors.New("Error adding host nqnq to subsystem")).Times(1) err = d.Publish(ctx, volConfig, publishInfo) @@ -1659,7 +1639,7 @@ func TestPublish(t *testing.T) { // case 9: Error returned by NVMeEnsureNamespaceMapped mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(nil).Times(1) mock.EXPECT().NVMeEnsureNamespaceMapped(ctx, gomock.Any(), gomock.Any()).Return(errors.New("Error returned by NVMeEnsureNamespaceMapped")).Times(1) @@ -1669,7 +1649,7 @@ func TestPublish(t *testing.T) { // case 10: Success mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(nil).Times(1) mock.EXPECT().NVMeEnsureNamespaceMapped(ctx, gomock.Any(), gomock.Any()).Return(nil).Times(1) @@ -1681,7 +1661,7 @@ func TestPublish(t *testing.T) { volConfig.FileSystem = filesystem.Xfs publishInfo.MountOptions = "" mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(nil).Times(1) mock.EXPECT().NVMeEnsureNamespaceMapped(ctx, gomock.Any(), gomock.Any()).Return(nil).Times(1) @@ -1694,7 +1674,7 @@ func TestPublish(t *testing.T) { volConfig.FileSystem = filesystem.Xfs publishInfo.MountOptions = "rw,relatime" mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(nil).Times(1) mock.EXPECT().NVMeEnsureNamespaceMapped(ctx, gomock.Any(), gomock.Any()).Return(nil).Times(1) @@ -1709,7 +1689,7 @@ func TestPublish(t *testing.T) { volConfig.FileSystem = filesystem.Ext4 publishInfo.MountOptions = "" mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(nil).Times(1) mock.EXPECT().NVMeEnsureNamespaceMapped(ctx, gomock.Any(), gomock.Any()).Return(nil).Times(1) @@ -1722,7 +1702,7 @@ func TestPublish(t *testing.T) { volConfig.FileSystem = filesystem.Xfs publishInfo.MountOptions = "rw,nouuid,relatime" mock.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) - mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName-fakeUUID", "fakeHostName-fakeUUID").Return(subsystem, nil).Times(1) + mock.EXPECT().NVMeSubsystemCreate(ctx, "fakeHostName_fakeUUID", "fakeHostName_fakeUUID").Return(subsystem, nil).Times(1) mock.EXPECT().NVMeAddHostToSubsystem(ctx, publishInfo.HostNQN, subsystem.UUID).Return(nil).Times(1) mock.EXPECT().NVMeEnsureNamespaceMapped(ctx, gomock.Any(), gomock.Any()).Return(nil).Times(1) From df231f97fe1c395e549be107afcb8c95784474f7 Mon Sep 17 00:00:00 2001 From: Joe Webster <31218426+jwebster7@users.noreply.github.com> Date: Thu, 6 Nov 2025 17:48:31 -0600 Subject: [PATCH 17/30] Handle stale LUKS mappers for NVMe This commit enhances Trident's CSI node stage and unstage operations to handle stale mapper devices for LUKS-encrypted NVMe devices. --- .../csi/node_helpers/kubernetes/plugin.go | 6 +- frontend/csi/node_server.go | 66 +++-- frontend/csi/node_server_test.go | 61 +++-- frontend/csi/volume_publish_manager.go | 2 +- frontend/csi/volume_publish_manager_test.go | 20 +- .../mock_devices/mock_devices_client.go | 226 ++++++++++-------- .../mock_devices/mock_luks/mock_luks.go | 150 ++++++++++-- utils/devices/devices.go | 48 ++++ utils/devices/devices_test.go | 80 ++++++- utils/devices/luks/luks.go | 83 ++++++- utils/devices/luks/luks_darwin.go | 6 +- utils/devices/luks/luks_linux.go | 90 ++++++- utils/devices/luks/luks_linux_test.go | 164 ++++++++++++- utils/devices/luks/luks_test.go | 24 +- utils/devices/luks/luks_windows.go | 6 +- utils/devices/luks/utils_test.go | 31 --- utils/filesystem/json.go | 2 +- utils/iscsi/iscsi.go | 43 ++-- utils/iscsi/iscsi_test.go | 80 ------- utils/nvme/nvme.go | 12 +- 20 files changed, 864 insertions(+), 336 deletions(-) delete mode 100644 utils/devices/luks/utils_test.go diff --git a/frontend/csi/node_helpers/kubernetes/plugin.go b/frontend/csi/node_helpers/kubernetes/plugin.go index ebc98860d..1a29279a6 100644 --- a/frontend/csi/node_helpers/kubernetes/plugin.go +++ b/frontend/csi/node_helpers/kubernetes/plugin.go @@ -1,4 +1,4 @@ -// Copyright 2022 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package kubernetes @@ -214,13 +214,13 @@ func (h *helper) RemovePublishedPath(ctx context.Context, volumeID, pathToRemove volTrackingInfo, err := h.ReadTrackingInfo(ctx, volumeID) if err != nil { - return fmt.Errorf("failed to read the tracking file; %v", err) + return fmt.Errorf("failed to read the tracking file; %w", err) } delete(volTrackingInfo.PublishedPaths, pathToRemove) if err := h.WriteTrackingInfo(ctx, volumeID, volTrackingInfo); err != nil { - return fmt.Errorf("failed to update the tracking file; %v", err) + return fmt.Errorf("failed to update the tracking file; %w", err) } h.publishedPaths[volumeID] = volTrackingInfo.PublishedPaths diff --git a/frontend/csi/node_server.go b/frontend/csi/node_server.go index 93cc6a9ca..b659e852d 100644 --- a/frontend/csi/node_server.go +++ b/frontend/csi/node_server.go @@ -644,12 +644,12 @@ func (p *Plugin) nodeExpandVolume( devicePath := publishInfo.DevicePath if convert.ToBool(publishInfo.LUKSEncryption) { if !luks.IsLegacyDevicePath(devicePath) { - devicePath, err = p.devices.GetLUKSDeviceForMultipathDevice(devicePath) + devicePath, err = p.devices.GetLUKSDevicePathForVolume(ctx, volumeId) if err != nil { Logc(ctx).WithFields(LogFields{ "volumeId": volumeId, "publishedPath": publishInfo.DevicePath, - }).WithError(err).Error("Failed to get LUKS device path from device path.") + }).WithError(err).Error("Failed to get LUKS device path for volume.") return status.Error(codes.Internal, err.Error()) } } @@ -1468,15 +1468,18 @@ func (p *Plugin) nodeUnstageFCPVolume( publishInfo.DevicePath = dmPath } } else { - // If not using luks legacy device path we need to find the LUKS mapper device - luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(publishInfo.DevicePath) + // If not using luks legacy device path we need to find the LUKS mapper device. + luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) if err != nil { - if !errors.IsNotFoundError(err) { - Logc(ctx).WithFields(fields).WithError(err).Warn( - "Could not determine LUKS device path from multipath device. " + - "Continuing with device removal.") + // If the LUKS device is not found, the functional difference is negligible to unstage. + // But it may be useful to log at different levels for observability. + log := Logc(ctx).WithFields(fields).WithError(err) + if errors.IsNotFoundError(err) { + log.Warn("Failed to get LUKS device path for volume.") + } else { + log.Debug("Could not determine LUKS device path for volume.") } - Logc(ctx).WithFields(fields).Info("No LUKS device path found from multipath device.") + log.Debug("Continuing with device removal.") } } err = p.devices.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksMapperPath) @@ -1519,11 +1522,10 @@ func (p *Plugin) nodeUnstageFCPVolume( "multipathDevice": deviceInfo.MultipathDevice, } - luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(deviceInfo.MultipathDevice) + luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) if err != nil { if !errors.IsNotFoundError(err) { - Logc(ctx).WithFields(fields). - WithError(err).Error("Failed to get LUKS device path from multipath device.") + Logc(ctx).WithFields(fields).WithError(err).Error("Failed to get LUKS device path from multipath device.") return err } Logc(ctx).WithFields(fields).Info("No LUKS device path found from multipath device.") @@ -1995,7 +1997,7 @@ func (p *Plugin) nodeUnstageISCSIVolume( if convert.ToBool(publishInfo.LUKSEncryption) { var err error var luksMapperPath string - fields := LogFields{"device": publishInfo.DevicePath} + fields := LogFields{"device": publishInfo.DevicePath, "volume": req.GetVolumeId()} // Set device path to dm device to correctly verify legacy volumes. if luks.IsLegacyDevicePath(publishInfo.DevicePath) { luksMapperPath = publishInfo.DevicePath @@ -2009,17 +2011,22 @@ func (p *Plugin) nodeUnstageISCSIVolume( publishInfo.DevicePath = dmPath } } else { - // If not using luks legacy device path we need to find the LUKS mapper device - luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(publishInfo.DevicePath) + // Use the volume ID to get the LUKS mapper path. + // This should always work if the mapper is still present. + luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) if err != nil { - if !errors.IsNotFoundError(err) { - Logc(ctx).WithFields(fields).WithError(err).Warn( - "Could not determine LUKS device path from multipath device. " + - "Continuing with device removal.") + // If the LUKS device is not found, the functional difference is negligible to unstage. + // But it may be useful to log at different levels for observability. + log := Logc(ctx).WithFields(fields).WithError(err) + if errors.IsNotFoundError(err) { + log.Warn("Failed to get LUKS device path for volume.") + } else { + log.Debug("Could not determine LUKS device path for volume.") } - Logc(ctx).WithFields(fields).Info("No LUKS device path found from multipath device.") + log.Debug("Continuing with device removal.") } } + err = p.devices.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksMapperPath) if err != nil { Logc(ctx).WithError(err).Debug("Unable to remove LUKS device. Continuing with tracking file removal.") @@ -2070,7 +2077,7 @@ func (p *Plugin) nodeUnstageISCSIVolume( "multipathDevice": deviceInfo.MultipathDevice, } - luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(deviceInfo.MultipathDevice) + luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) if err != nil { if !errors.IsNotFoundError(err) { Logc(ctx).WithFields(fields).WithError(err).Error("Failed to get LUKS device path from multipath device.") @@ -3073,7 +3080,7 @@ func (p *Plugin) nodeUnstageNVMeVolume( return nil, fmt.Errorf("failed to get NVMe device; %v", err) } - var devicePath string + devicePath := publishInfo.DevicePath if nvmeDev != nil { devicePath = nvmeDev.GetPath() } @@ -3086,10 +3093,19 @@ func (p *Plugin) nodeUnstageNVMeVolume( "publishedPath": publishInfo.DevicePath, } - luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(devicePath) + // Use the volume ID to get the LUKS mapper path. + // This should always work if the mapper is still present. + luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) if err != nil { - Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to get LUKS device path from device path. " + - "Device may already be removed.") + // If the LUKS device is not found, the functional difference is negligible to unstage. + // But it may be useful to log at different levels for observability. + log := Logc(ctx).WithFields(fields).WithError(err) + if errors.IsNotFoundError(err) { + log.Warn("Failed to get LUKS device path for volume.") + } else { + log.Debug("Could not determine LUKS device path for volume.") + } + log.Debug("Continuing with device removal.") } if luksMapperPath != "" { diff --git a/frontend/csi/node_server_test.go b/frontend/csi/node_server_test.go index 3afe1bb8e..78b096d9b 100644 --- a/frontend/csi/node_server_test.go +++ b/frontend/csi/node_server_test.go @@ -2131,7 +2131,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), @@ -2178,7 +2178,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) @@ -2223,7 +2223,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(fmt.Errorf("mock error")) return mockDeviceClient @@ -2242,7 +2242,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), @@ -2317,7 +2317,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { return mockDeviceClient }, }, - "SAN: iSCSI unstage: GetLUKSDeviceForMultipathDevice error": { + // mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + "SAN: iSCSI unstage: GetLUKSDevicePathForVolume error": { assertError: assert.Error, request: NewNodeUnstageVolumeRequestBuilder().Build(), publishInfo: NewVolumePublishInfoBuilder(TypeiSCSIVolumePublishInfo).WithLUKSEncryption("true").Build(), @@ -2337,8 +2338,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", fmt.Errorf( - "mock error")) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), + gomock.Any()).Return(mockDevicePath, errors.New("mock error")) return mockDeviceClient }, }, @@ -2358,7 +2359,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) return mockDeviceClient @@ -2392,7 +2393,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) @@ -11936,7 +11937,7 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", errors.New("")) + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return("", errors.New("")) return mockDeviceClient }, expErrCode: codes.Internal, @@ -11984,7 +11985,9 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("x/device-path", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("x/device-path", nil).AnyTimes() return mockDeviceClient }, expErrCode: codes.InvalidArgument, @@ -12032,7 +12035,9 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("x/device-path", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("x/device-path", nil).AnyTimes() return mockDeviceClient }, expErrCode: codes.InvalidArgument, @@ -12080,7 +12085,9 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("x/device-path", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("x/device-path", nil).AnyTimes() return mockDeviceClient }, expErrCode: codes.Internal, @@ -12523,7 +12530,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", errors.New("")).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("", errors.New("")).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() return mockDeviceClient }, @@ -12710,7 +12719,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", errors.New("")).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("", errors.New("")).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() // mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -12758,7 +12769,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("multipath-device", nil).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() @@ -12807,7 +12820,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("multipath-device", nil).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.MaxWaitExceededError("")).AnyTimes() @@ -12855,7 +12870,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("multipath-device", nil).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.MaxWaitExceededError("")).AnyTimes() @@ -12903,7 +12920,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("multipath-device", nil).AnyTimes() mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.MaxWaitExceededError("")).AnyTimes() @@ -12952,7 +12971,9 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( + gomock.Any(), gomock.Any(), + ).Return("multipath-device", nil).AnyTimes() mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() diff --git a/frontend/csi/volume_publish_manager.go b/frontend/csi/volume_publish_manager.go index 456ae0994..410ec5f26 100644 --- a/frontend/csi/volume_publish_manager.go +++ b/frontend/csi/volume_publish_manager.go @@ -151,7 +151,7 @@ func (v *VolumePublishManager) readTrackingInfo( err := jsonRW.ReadJSONFile(ctx, &volumeTrackingInfo, path.Join(v.volumeTrackingInfoPath, filename), "volume tracking info") if err != nil { - return nil, err + return nil, fmt.Errorf("failed to read tracking info for volume %s: %w", volumeID, err) } Logc(ctx).WithField("volumeTrackingInfo", volumeTrackingInfo).Debug("Volume tracking info found.") diff --git a/frontend/csi/volume_publish_manager_test.go b/frontend/csi/volume_publish_manager_test.go index e3b3f0caa..908c1fb8b 100644 --- a/frontend/csi/volume_publish_manager_test.go +++ b/frontend/csi/volume_publish_manager_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package csi @@ -112,10 +112,10 @@ func TestWriteTrackingInfo(t *testing.T) { assert.NoError(t, err, "no error expected when write succeeds") mockJSONUtils.EXPECT().WriteJSONFile(gomock.Any(), trackInfo, "tmp-"+fName, "volume tracking info"). - Return(errors.New("foo")) + Return(errors.InvalidJSONError("foo")) err = v.WriteTrackingInfo(ctx, volId, trackInfo) assert.Error(t, err, "error expected when write tracking info fails") - assert.Equal(t, "foo", err.Error(), "expected actual error we threw") + assert.True(t, errors.IsInvalidJSONError(err), "expected actual error we threw") } func TestReadTrackingInfo(t *testing.T) { @@ -144,15 +144,17 @@ func TestReadTrackingInfo(t *testing.T) { mockJSONUtils.EXPECT().ReadJSONFile(gomock.Any(), emptyTrackInfo, fName, "volume tracking info"). SetArg(1, *trackInfo).Return(nil) trackInfo, err := v.ReadTrackingInfo(context.Background(), volId) + assert.NoError(t, err, "no error expected when write succeed") + assert.NotNil(t, trackInfo, "expected a valid tracking info") assert.Equal(t, fsType, trackInfo.FilesystemType, "tracking file did not have expected value in it") - assert.NoError(t, err, "tracking file should have been written") emptyTrackInfo = &models.VolumeTrackingInfo{} mockJSONUtils.EXPECT().ReadJSONFile(gomock.Any(), emptyTrackInfo, fName, - "volume tracking info").Return(errors.New("foo")) - _, err = v.ReadTrackingInfo(context.Background(), volId) + "volume tracking info").Return(errors.NotFoundError("not found")) + trackInfo, err = v.ReadTrackingInfo(context.Background(), volId) assert.Error(t, err, "expected error when reading the file results in an error") - assert.Equal(t, "foo", err.Error(), "expected the error we threw in the mock") + assert.Nil(t, trackInfo, "expected nil tracking info") + assert.True(t, errors.IsNotFoundError(err), "expected not found error") } func TestListVolumeTrackingInfo_FailsToGetVolumeTrackingFiles(t *testing.T) { @@ -288,10 +290,10 @@ func TestDeleteTrackingInfo(t *testing.T) { err := v.DeleteTrackingInfo(context.Background(), volName) assert.NoError(t, err, "expected no error deleting the tracking info") - mockFilesytesm.EXPECT().DeleteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.New("foo")) + mockFilesytesm.EXPECT().DeleteFile(gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.InvalidJSONError("foo")) err = v.DeleteTrackingInfo(context.Background(), volName) assert.Error(t, err, "expected error if delete tracking info fails") - assert.Equal(t, "foo", err.Error(), "expected the error we threw") + assert.True(t, errors.IsInvalidJSONError(err), "expected the error we threw") } func TestUpgradeVolumeTrackingFile(t *testing.T) { diff --git a/mocks/mock_utils/mock_devices/mock_devices_client.go b/mocks/mock_utils/mock_devices/mock_devices_client.go index 3f60eed53..aa1908cc3 100644 --- a/mocks/mock_utils/mock_devices/mock_devices_client.go +++ b/mocks/mock_utils/mock_devices/mock_devices_client.go @@ -10,19 +10,18 @@ package mock_devices import ( + context "context" reflect "reflect" time "time" models "github.com/netapp/trident/utils/models" gomock "go.uber.org/mock/gomock" - context "golang.org/x/net/context" ) // MockDevices is a mock of Devices interface. type MockDevices struct { ctrl *gomock.Controller recorder *MockDevicesMockRecorder - isgomock struct{} } // MockDevicesMockRecorder is the mock recorder for MockDevices. @@ -43,337 +42,352 @@ func (m *MockDevices) EXPECT() *MockDevicesMockRecorder { } // ClearFormatting mocks base method. -func (m *MockDevices) ClearFormatting(ctx context.Context, devicePath string) error { +func (m *MockDevices) ClearFormatting(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ClearFormatting", ctx, devicePath) + ret := m.ctrl.Call(m, "ClearFormatting", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ClearFormatting indicates an expected call of ClearFormatting. -func (mr *MockDevicesMockRecorder) ClearFormatting(ctx, devicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) ClearFormatting(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearFormatting", reflect.TypeOf((*MockDevices)(nil).ClearFormatting), ctx, devicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearFormatting", reflect.TypeOf((*MockDevices)(nil).ClearFormatting), arg0, arg1) } // CloseLUKSDevice mocks base method. -func (m *MockDevices) CloseLUKSDevice(ctx context.Context, devicePath string) error { +func (m *MockDevices) CloseLUKSDevice(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CloseLUKSDevice", ctx, devicePath) + ret := m.ctrl.Call(m, "CloseLUKSDevice", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // CloseLUKSDevice indicates an expected call of CloseLUKSDevice. -func (mr *MockDevicesMockRecorder) CloseLUKSDevice(ctx, devicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) CloseLUKSDevice(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseLUKSDevice", reflect.TypeOf((*MockDevices)(nil).CloseLUKSDevice), ctx, devicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseLUKSDevice", reflect.TypeOf((*MockDevices)(nil).CloseLUKSDevice), arg0, arg1) } // EnsureDeviceReadable mocks base method. -func (m *MockDevices) EnsureDeviceReadable(ctx context.Context, device string) error { +func (m *MockDevices) EnsureDeviceReadable(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureDeviceReadable", ctx, device) + ret := m.ctrl.Call(m, "EnsureDeviceReadable", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // EnsureDeviceReadable indicates an expected call of EnsureDeviceReadable. -func (mr *MockDevicesMockRecorder) EnsureDeviceReadable(ctx, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) EnsureDeviceReadable(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceReadable", reflect.TypeOf((*MockDevices)(nil).EnsureDeviceReadable), ctx, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceReadable", reflect.TypeOf((*MockDevices)(nil).EnsureDeviceReadable), arg0, arg1) } // EnsureLUKSDeviceClosed mocks base method. -func (m *MockDevices) EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error { +func (m *MockDevices) EnsureLUKSDeviceClosed(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosed", ctx, devicePath) + ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosed", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // EnsureLUKSDeviceClosed indicates an expected call of EnsureLUKSDeviceClosed. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosed(ctx, devicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosed(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosed", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosed), ctx, devicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosed", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosed), arg0, arg1) } // EnsureLUKSDeviceClosedWithMaxWaitLimit mocks base method. -func (m *MockDevices) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { +func (m *MockDevices) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosedWithMaxWaitLimit", ctx, luksDevicePath) + ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosedWithMaxWaitLimit", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // EnsureLUKSDeviceClosedWithMaxWaitLimit indicates an expected call of EnsureLUKSDeviceClosedWithMaxWaitLimit. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksDevicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), ctx, luksDevicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), arg0, arg1) } // FindDevicesForMultipathDevice mocks base method. -func (m *MockDevices) FindDevicesForMultipathDevice(ctx context.Context, device string) []string { +func (m *MockDevices) FindDevicesForMultipathDevice(arg0 context.Context, arg1 string) []string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindDevicesForMultipathDevice", ctx, device) + ret := m.ctrl.Call(m, "FindDevicesForMultipathDevice", arg0, arg1) ret0, _ := ret[0].([]string) return ret0 } // FindDevicesForMultipathDevice indicates an expected call of FindDevicesForMultipathDevice. -func (mr *MockDevicesMockRecorder) FindDevicesForMultipathDevice(ctx, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FindDevicesForMultipathDevice(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDevicesForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).FindDevicesForMultipathDevice), ctx, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDevicesForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).FindDevicesForMultipathDevice), arg0, arg1) } // FindMultipathDeviceForDevice mocks base method. -func (m *MockDevices) FindMultipathDeviceForDevice(ctx context.Context, device string) string { +func (m *MockDevices) FindMultipathDeviceForDevice(arg0 context.Context, arg1 string) string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindMultipathDeviceForDevice", ctx, device) + ret := m.ctrl.Call(m, "FindMultipathDeviceForDevice", arg0, arg1) ret0, _ := ret[0].(string) return ret0 } // FindMultipathDeviceForDevice indicates an expected call of FindMultipathDeviceForDevice. -func (mr *MockDevicesMockRecorder) FindMultipathDeviceForDevice(ctx, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FindMultipathDeviceForDevice(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipathDeviceForDevice", reflect.TypeOf((*MockDevices)(nil).FindMultipathDeviceForDevice), ctx, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipathDeviceForDevice", reflect.TypeOf((*MockDevices)(nil).FindMultipathDeviceForDevice), arg0, arg1) } // FlushDevice mocks base method. -func (m *MockDevices) FlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, force bool) error { +func (m *MockDevices) FlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo, arg2 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlushDevice", ctx, deviceInfo, force) + ret := m.ctrl.Call(m, "FlushDevice", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // FlushDevice indicates an expected call of FlushDevice. -func (mr *MockDevicesMockRecorder) FlushDevice(ctx, deviceInfo, force any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FlushDevice(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDevice", reflect.TypeOf((*MockDevices)(nil).FlushDevice), ctx, deviceInfo, force) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDevice", reflect.TypeOf((*MockDevices)(nil).FlushDevice), arg0, arg1, arg2) } // FlushOneDevice mocks base method. -func (m *MockDevices) FlushOneDevice(ctx context.Context, devicePath string) error { +func (m *MockDevices) FlushOneDevice(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlushOneDevice", ctx, devicePath) + ret := m.ctrl.Call(m, "FlushOneDevice", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // FlushOneDevice indicates an expected call of FlushOneDevice. -func (mr *MockDevicesMockRecorder) FlushOneDevice(ctx, devicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FlushOneDevice(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushOneDevice", reflect.TypeOf((*MockDevices)(nil).FlushOneDevice), ctx, devicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushOneDevice", reflect.TypeOf((*MockDevices)(nil).FlushOneDevice), arg0, arg1) } // GetDeviceFSType mocks base method. -func (m *MockDevices) GetDeviceFSType(ctx context.Context, device string) (string, error) { +func (m *MockDevices) GetDeviceFSType(arg0 context.Context, arg1 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeviceFSType", ctx, device) + ret := m.ctrl.Call(m, "GetDeviceFSType", arg0, arg1) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeviceFSType indicates an expected call of GetDeviceFSType. -func (mr *MockDevicesMockRecorder) GetDeviceFSType(ctx, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetDeviceFSType(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceFSType", reflect.TypeOf((*MockDevices)(nil).GetDeviceFSType), ctx, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceFSType", reflect.TypeOf((*MockDevices)(nil).GetDeviceFSType), arg0, arg1) } // GetDiskSize mocks base method. -func (m *MockDevices) GetDiskSize(ctx context.Context, devicePath string) (int64, error) { +func (m *MockDevices) GetDiskSize(arg0 context.Context, arg1 string) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDiskSize", ctx, devicePath) + ret := m.ctrl.Call(m, "GetDiskSize", arg0, arg1) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDiskSize indicates an expected call of GetDiskSize. -func (mr *MockDevicesMockRecorder) GetDiskSize(ctx, devicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetDiskSize(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskSize", reflect.TypeOf((*MockDevices)(nil).GetDiskSize), ctx, devicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskSize", reflect.TypeOf((*MockDevices)(nil).GetDiskSize), arg0, arg1) } // GetLUKSDeviceForMultipathDevice mocks base method. -func (m *MockDevices) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) { +func (m *MockDevices) GetLUKSDeviceForMultipathDevice(arg0 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLUKSDeviceForMultipathDevice", multipathDevice) + ret := m.ctrl.Call(m, "GetLUKSDeviceForMultipathDevice", arg0) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLUKSDeviceForMultipathDevice indicates an expected call of GetLUKSDeviceForMultipathDevice. -func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(multipathDevice any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), multipathDevice) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), arg0) +} + +// GetLUKSDevicePathForVolume mocks base method. +func (m *MockDevices) GetLUKSDevicePathForVolume(arg0 context.Context, arg1 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLUKSDevicePathForVolume", arg0, arg1) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLUKSDevicePathForVolume indicates an expected call of GetLUKSDevicePathForVolume. +func (mr *MockDevicesMockRecorder) GetLUKSDevicePathForVolume(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDevicePathForVolume", reflect.TypeOf((*MockDevices)(nil).GetLUKSDevicePathForVolume), arg0, arg1) } // GetLunSerial mocks base method. -func (m *MockDevices) GetLunSerial(ctx context.Context, path string) (string, error) { +func (m *MockDevices) GetLunSerial(arg0 context.Context, arg1 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLunSerial", ctx, path) + ret := m.ctrl.Call(m, "GetLunSerial", arg0, arg1) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLunSerial indicates an expected call of GetLunSerial. -func (mr *MockDevicesMockRecorder) GetLunSerial(ctx, path any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetLunSerial(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLunSerial", reflect.TypeOf((*MockDevices)(nil).GetLunSerial), ctx, path) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLunSerial", reflect.TypeOf((*MockDevices)(nil).GetLunSerial), arg0, arg1) } // GetMultipathDeviceBySerial mocks base method. -func (m *MockDevices) GetMultipathDeviceBySerial(ctx context.Context, hexSerial string) (string, error) { +func (m *MockDevices) GetMultipathDeviceBySerial(arg0 context.Context, arg1 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceBySerial", ctx, hexSerial) + ret := m.ctrl.Call(m, "GetMultipathDeviceBySerial", arg0, arg1) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMultipathDeviceBySerial indicates an expected call of GetMultipathDeviceBySerial. -func (mr *MockDevicesMockRecorder) GetMultipathDeviceBySerial(ctx, hexSerial any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetMultipathDeviceBySerial(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceBySerial", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceBySerial), ctx, hexSerial) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceBySerial", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceBySerial), arg0, arg1) } // GetMultipathDeviceUUID mocks base method. -func (m *MockDevices) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) { +func (m *MockDevices) GetMultipathDeviceUUID(arg0 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", multipathDevicePath) + ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", arg0) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMultipathDeviceUUID indicates an expected call of GetMultipathDeviceUUID. -func (mr *MockDevicesMockRecorder) GetMultipathDeviceUUID(multipathDevicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetMultipathDeviceUUID(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceUUID), multipathDevicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceUUID), arg0) } // IsDeviceUnformatted mocks base method. -func (m *MockDevices) IsDeviceUnformatted(ctx context.Context, device string) (bool, error) { +func (m *MockDevices) IsDeviceUnformatted(arg0 context.Context, arg1 string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsDeviceUnformatted", ctx, device) + ret := m.ctrl.Call(m, "IsDeviceUnformatted", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsDeviceUnformatted indicates an expected call of IsDeviceUnformatted. -func (mr *MockDevicesMockRecorder) IsDeviceUnformatted(ctx, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) IsDeviceUnformatted(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDeviceUnformatted", reflect.TypeOf((*MockDevices)(nil).IsDeviceUnformatted), ctx, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDeviceUnformatted", reflect.TypeOf((*MockDevices)(nil).IsDeviceUnformatted), arg0, arg1) } // ListAllDevices mocks base method. -func (m *MockDevices) ListAllDevices(ctx context.Context) { +func (m *MockDevices) ListAllDevices(arg0 context.Context) { m.ctrl.T.Helper() - m.ctrl.Call(m, "ListAllDevices", ctx) + m.ctrl.Call(m, "ListAllDevices", arg0) } // ListAllDevices indicates an expected call of ListAllDevices. -func (mr *MockDevicesMockRecorder) ListAllDevices(ctx any) *gomock.Call { +func (mr *MockDevicesMockRecorder) ListAllDevices(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllDevices", reflect.TypeOf((*MockDevices)(nil).ListAllDevices), ctx) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllDevices", reflect.TypeOf((*MockDevices)(nil).ListAllDevices), arg0) } // MultipathFlushDevice mocks base method. -func (m *MockDevices) MultipathFlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo) error { +func (m *MockDevices) MultipathFlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MultipathFlushDevice", ctx, deviceInfo) + ret := m.ctrl.Call(m, "MultipathFlushDevice", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // MultipathFlushDevice indicates an expected call of MultipathFlushDevice. -func (mr *MockDevicesMockRecorder) MultipathFlushDevice(ctx, deviceInfo any) *gomock.Call { +func (mr *MockDevicesMockRecorder) MultipathFlushDevice(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MultipathFlushDevice", reflect.TypeOf((*MockDevices)(nil).MultipathFlushDevice), ctx, deviceInfo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MultipathFlushDevice", reflect.TypeOf((*MockDevices)(nil).MultipathFlushDevice), arg0, arg1) } // RemoveDevice mocks base method. -func (m *MockDevices) RemoveDevice(ctx context.Context, devices []string, ignoreErrors bool) error { +func (m *MockDevices) RemoveDevice(arg0 context.Context, arg1 []string, arg2 bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveDevice", ctx, devices, ignoreErrors) + ret := m.ctrl.Call(m, "RemoveDevice", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // RemoveDevice indicates an expected call of RemoveDevice. -func (mr *MockDevicesMockRecorder) RemoveDevice(ctx, devices, ignoreErrors any) *gomock.Call { +func (mr *MockDevicesMockRecorder) RemoveDevice(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDevice", reflect.TypeOf((*MockDevices)(nil).RemoveDevice), ctx, devices, ignoreErrors) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDevice", reflect.TypeOf((*MockDevices)(nil).RemoveDevice), arg0, arg1, arg2) } // RemoveMultipathDeviceMapping mocks base method. -func (m *MockDevices) RemoveMultipathDeviceMapping(ctx context.Context, devicePath string) error { +func (m *MockDevices) RemoveMultipathDeviceMapping(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveMultipathDeviceMapping", ctx, devicePath) + ret := m.ctrl.Call(m, "RemoveMultipathDeviceMapping", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // RemoveMultipathDeviceMapping indicates an expected call of RemoveMultipathDeviceMapping. -func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMapping(ctx, devicePath any) *gomock.Call { +func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMapping(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMapping", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMapping), ctx, devicePath) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMapping", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMapping), arg0, arg1) } // RemoveMultipathDeviceMappingWithRetries mocks base method. -func (m *MockDevices) RemoveMultipathDeviceMappingWithRetries(ctx context.Context, devicePath string, retries uint64, sleep time.Duration) error { +func (m *MockDevices) RemoveMultipathDeviceMappingWithRetries(arg0 context.Context, arg1 string, arg2 uint64, arg3 time.Duration) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveMultipathDeviceMappingWithRetries", ctx, devicePath, retries, sleep) + ret := m.ctrl.Call(m, "RemoveMultipathDeviceMappingWithRetries", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // RemoveMultipathDeviceMappingWithRetries indicates an expected call of RemoveMultipathDeviceMappingWithRetries. -func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMappingWithRetries(ctx, devicePath, retries, sleep any) *gomock.Call { +func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMappingWithRetries(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMappingWithRetries", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMappingWithRetries), ctx, devicePath, retries, sleep) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMappingWithRetries", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMappingWithRetries), arg0, arg1, arg2, arg3) } // ScanTargetLUN mocks base method. -func (m *MockDevices) ScanTargetLUN(ctx context.Context, deviceAddresses []models.ScsiDeviceAddress) error { +func (m *MockDevices) ScanTargetLUN(arg0 context.Context, arg1 []models.ScsiDeviceAddress) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ScanTargetLUN", ctx, deviceAddresses) + ret := m.ctrl.Call(m, "ScanTargetLUN", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ScanTargetLUN indicates an expected call of ScanTargetLUN. -func (mr *MockDevicesMockRecorder) ScanTargetLUN(ctx, deviceAddresses any) *gomock.Call { +func (mr *MockDevicesMockRecorder) ScanTargetLUN(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanTargetLUN", reflect.TypeOf((*MockDevices)(nil).ScanTargetLUN), ctx, deviceAddresses) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanTargetLUN", reflect.TypeOf((*MockDevices)(nil).ScanTargetLUN), arg0, arg1) } // VerifyMultipathDevice mocks base method. -func (m *MockDevices) VerifyMultipathDevice(ctx context.Context, publishInfo *models.VolumePublishInfo, allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo) (bool, error) { +func (m *MockDevices) VerifyMultipathDevice(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 []models.VolumePublishInfo, arg3 *models.ScsiDeviceInfo) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VerifyMultipathDevice", ctx, publishInfo, allPublishInfos, deviceInfo) + ret := m.ctrl.Call(m, "VerifyMultipathDevice", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // VerifyMultipathDevice indicates an expected call of VerifyMultipathDevice. -func (mr *MockDevicesMockRecorder) VerifyMultipathDevice(ctx, publishInfo, allPublishInfos, deviceInfo any) *gomock.Call { +func (mr *MockDevicesMockRecorder) VerifyMultipathDevice(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDevice", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDevice), ctx, publishInfo, allPublishInfos, deviceInfo) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDevice", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDevice), arg0, arg1, arg2, arg3) } // VerifyMultipathDeviceSize mocks base method. -func (m *MockDevices) VerifyMultipathDeviceSize(ctx context.Context, multipathDevice, device string) (int64, bool, error) { +func (m *MockDevices) VerifyMultipathDeviceSize(arg0 context.Context, arg1, arg2 string) (int64, bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VerifyMultipathDeviceSize", ctx, multipathDevice, device) + ret := m.ctrl.Call(m, "VerifyMultipathDeviceSize", arg0, arg1, arg2) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(error) @@ -381,35 +395,35 @@ func (m *MockDevices) VerifyMultipathDeviceSize(ctx context.Context, multipathDe } // VerifyMultipathDeviceSize indicates an expected call of VerifyMultipathDeviceSize. -func (mr *MockDevicesMockRecorder) VerifyMultipathDeviceSize(ctx, multipathDevice, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) VerifyMultipathDeviceSize(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDeviceSize", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDeviceSize), ctx, multipathDevice, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDeviceSize", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDeviceSize), arg0, arg1, arg2) } // WaitForDevice mocks base method. -func (m *MockDevices) WaitForDevice(ctx context.Context, device string) error { +func (m *MockDevices) WaitForDevice(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForDevice", ctx, device) + ret := m.ctrl.Call(m, "WaitForDevice", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // WaitForDevice indicates an expected call of WaitForDevice. -func (mr *MockDevicesMockRecorder) WaitForDevice(ctx, device any) *gomock.Call { +func (mr *MockDevicesMockRecorder) WaitForDevice(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevice", reflect.TypeOf((*MockDevices)(nil).WaitForDevice), ctx, device) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevice", reflect.TypeOf((*MockDevices)(nil).WaitForDevice), arg0, arg1) } // WaitForDevicesRemoval mocks base method. -func (m *MockDevices) WaitForDevicesRemoval(ctx context.Context, devicePathPrefix string, deviceNames []string, maxWaitTime time.Duration) error { +func (m *MockDevices) WaitForDevicesRemoval(arg0 context.Context, arg1 string, arg2 []string, arg3 time.Duration) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForDevicesRemoval", ctx, devicePathPrefix, deviceNames, maxWaitTime) + ret := m.ctrl.Call(m, "WaitForDevicesRemoval", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // WaitForDevicesRemoval indicates an expected call of WaitForDevicesRemoval. -func (mr *MockDevicesMockRecorder) WaitForDevicesRemoval(ctx, devicePathPrefix, deviceNames, maxWaitTime any) *gomock.Call { +func (mr *MockDevicesMockRecorder) WaitForDevicesRemoval(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevicesRemoval", reflect.TypeOf((*MockDevices)(nil).WaitForDevicesRemoval), ctx, devicePathPrefix, deviceNames, maxWaitTime) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevicesRemoval", reflect.TypeOf((*MockDevices)(nil).WaitForDevicesRemoval), arg0, arg1, arg2, arg3) } diff --git a/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go b/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go index a3b9fdce7..3cc3710f4 100644 --- a/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go +++ b/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/netapp/trident/utils/devices/luks (interfaces: Device) +// Source: github.com/netapp/trident/utils/devices/luks (interfaces: OS,Device) // // Generated by this command: // -// mockgen -destination=../../../mocks/mock_utils/mock_devices/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/devices/luks Device +// mockgen -destination=../../../mocks/mock_utils/mock_devices/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/devices/luks OS,Device // // Package mock_luks is a generated GoMock package. @@ -11,16 +11,114 @@ package mock_luks import ( context "context" + fs "io/fs" reflect "reflect" gomock "go.uber.org/mock/gomock" ) +// MockOS is a mock of OS interface. +type MockOS struct { + ctrl *gomock.Controller + recorder *MockOSMockRecorder +} + +// MockOSMockRecorder is the mock recorder for MockOS. +type MockOSMockRecorder struct { + mock *MockOS +} + +// NewMockOS creates a new mock instance. +func NewMockOS(ctrl *gomock.Controller) *MockOS { + mock := &MockOS{ctrl: ctrl} + mock.recorder = &MockOSMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOS) EXPECT() *MockOSMockRecorder { + return m.recorder +} + +// Glob mocks base method. +func (m *MockOS) Glob(arg0 string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Glob", arg0) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Glob indicates an expected call of Glob. +func (mr *MockOSMockRecorder) Glob(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Glob", reflect.TypeOf((*MockOS)(nil).Glob), arg0) +} + +// ReadDir mocks base method. +func (m *MockOS) ReadDir(arg0 string) ([]fs.FileInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadDir", arg0) + ret0, _ := ret[0].([]fs.FileInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDir indicates an expected call of ReadDir. +func (mr *MockOSMockRecorder) ReadDir(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDir", reflect.TypeOf((*MockOS)(nil).ReadDir), arg0) +} + +// ReadFile mocks base method. +func (m *MockOS) ReadFile(arg0 string) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadFile", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadFile indicates an expected call of ReadFile. +func (mr *MockOSMockRecorder) ReadFile(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFile", reflect.TypeOf((*MockOS)(nil).ReadFile), arg0) +} + +// ReadlinkIfPossible mocks base method. +func (m *MockOS) ReadlinkIfPossible(arg0 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadlinkIfPossible", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadlinkIfPossible indicates an expected call of ReadlinkIfPossible. +func (mr *MockOSMockRecorder) ReadlinkIfPossible(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadlinkIfPossible", reflect.TypeOf((*MockOS)(nil).ReadlinkIfPossible), arg0) +} + +// Stat mocks base method. +func (m *MockOS) Stat(arg0 string) (fs.FileInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Stat", arg0) + ret0, _ := ret[0].(fs.FileInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Stat indicates an expected call of Stat. +func (mr *MockOSMockRecorder) Stat(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockOS)(nil).Stat), arg0) +} + // MockDevice is a mock of Device interface. type MockDevice struct { ctrl *gomock.Controller recorder *MockDeviceMockRecorder - isgomock struct{} } // MockDeviceMockRecorder is the mock recorder for MockDevice. @@ -41,48 +139,62 @@ func (m *MockDevice) EXPECT() *MockDeviceMockRecorder { } // CheckPassphrase mocks base method. -func (m *MockDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { +func (m *MockDevice) CheckPassphrase(arg0 context.Context, arg1 string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckPassphrase", ctx, luksPassphrase) + ret := m.ctrl.Call(m, "CheckPassphrase", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // CheckPassphrase indicates an expected call of CheckPassphrase. -func (mr *MockDeviceMockRecorder) CheckPassphrase(ctx, luksPassphrase any) *gomock.Call { +func (mr *MockDeviceMockRecorder) CheckPassphrase(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPassphrase", reflect.TypeOf((*MockDevice)(nil).CheckPassphrase), ctx, luksPassphrase) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPassphrase", reflect.TypeOf((*MockDevice)(nil).CheckPassphrase), arg0, arg1) } // EnsureDeviceMappedOnHost mocks base method. -func (m *MockDevice) EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) { +func (m *MockDevice) EnsureDeviceMappedOnHost(arg0 context.Context, arg1 string, arg2 map[string]string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureDeviceMappedOnHost", ctx, name, secrets) + ret := m.ctrl.Call(m, "EnsureDeviceMappedOnHost", arg0, arg1, arg2) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // EnsureDeviceMappedOnHost indicates an expected call of EnsureDeviceMappedOnHost. -func (mr *MockDeviceMockRecorder) EnsureDeviceMappedOnHost(ctx, name, secrets any) *gomock.Call { +func (mr *MockDeviceMockRecorder) EnsureDeviceMappedOnHost(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceMappedOnHost", reflect.TypeOf((*MockDevice)(nil).EnsureDeviceMappedOnHost), ctx, name, secrets) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceMappedOnHost", reflect.TypeOf((*MockDevice)(nil).EnsureDeviceMappedOnHost), arg0, arg1, arg2) } // EnsureFormattedAndOpen mocks base method. -func (m *MockDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, error) { +func (m *MockDevice) EnsureFormattedAndOpen(arg0 context.Context, arg1 string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureFormattedAndOpen", ctx, luksPassphrase) + ret := m.ctrl.Call(m, "EnsureFormattedAndOpen", arg0, arg1) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // EnsureFormattedAndOpen indicates an expected call of EnsureFormattedAndOpen. -func (mr *MockDeviceMockRecorder) EnsureFormattedAndOpen(ctx, luksPassphrase any) *gomock.Call { +func (mr *MockDeviceMockRecorder) EnsureFormattedAndOpen(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureFormattedAndOpen", reflect.TypeOf((*MockDevice)(nil).EnsureFormattedAndOpen), arg0, arg1) +} + +// IsMappingStale mocks base method. +func (m *MockDevice) IsMappingStale(arg0 context.Context) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsMappingStale", arg0) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsMappingStale indicates an expected call of IsMappingStale. +func (mr *MockDeviceMockRecorder) IsMappingStale(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureFormattedAndOpen", reflect.TypeOf((*MockDevice)(nil).EnsureFormattedAndOpen), ctx, luksPassphrase) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMappingStale", reflect.TypeOf((*MockDevice)(nil).IsMappingStale), arg0) } // MappedDeviceName mocks base method. @@ -128,15 +240,15 @@ func (mr *MockDeviceMockRecorder) RawDevicePath() *gomock.Call { } // RotatePassphrase mocks base method. -func (m *MockDevice) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error { +func (m *MockDevice) RotatePassphrase(arg0 context.Context, arg1, arg2, arg3 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RotatePassphrase", ctx, volumeId, previousLUKSPassphrase, luksPassphrase) + ret := m.ctrl.Call(m, "RotatePassphrase", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // RotatePassphrase indicates an expected call of RotatePassphrase. -func (mr *MockDeviceMockRecorder) RotatePassphrase(ctx, volumeId, previousLUKSPassphrase, luksPassphrase any) *gomock.Call { +func (mr *MockDeviceMockRecorder) RotatePassphrase(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotatePassphrase", reflect.TypeOf((*MockDevice)(nil).RotatePassphrase), ctx, volumeId, previousLUKSPassphrase, luksPassphrase) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotatePassphrase", reflect.TypeOf((*MockDevice)(nil).RotatePassphrase), arg0, arg1, arg2, arg3) } diff --git a/utils/devices/devices.go b/utils/devices/devices.go index 057229160..29479997f 100644 --- a/utils/devices/devices.go +++ b/utils/devices/devices.go @@ -67,6 +67,7 @@ type Devices interface { GetLunSerial(ctx context.Context, path string) (string, error) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) + GetLUKSDevicePathForVolume(ctx context.Context, volumeID string) (string, error) ScanTargetLUN(ctx context.Context, deviceAddresses []models.ScsiDeviceAddress) error CloseLUKSDevice(ctx context.Context, devicePath string) error EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error @@ -804,6 +805,53 @@ func (c *Client) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string return DevMapperRoot + strings.TrimRight(string(b[luksDeviceUUIDNameOffset:]), "\n"), nil } +// GetLUKSDevicePathForVolume finds the LUKS device path for a given volume ID. +// Every LUKS device Trident creates should include the volume ID, which includes a volume uuid in the suffix. +// Use that knowledge to find the correct LUKS mapper device and path. +func (c *Client) GetLUKSDevicePathForVolume(ctx context.Context, volumeID string) (string, error) { + fields := LogFields{"volumeID": volumeID} + Logc(ctx).WithFields(fields).Debug(">>>> devices.GetLUKSDevicePathForVolume") + defer Logc(ctx).WithFields(fields).Debug("<<<< devices.GetLUKSDevicePathForVolume") + const dmDevicePattern = "/sys/block/dm-*" + + // This isn't great; we essentially have to reconstruct the suffix of the LUKS mapper device name + // which is the internal volume name of the volume. If this is required for iSCSI, + // we must test with SAN and SAN-Eco. + // "pvc-33bd3006-4765-498e-b61d-eae1d035c487" becomes "pvc_33bd3006_4765_498e_b61d_eae1d035c487" + luksSuffix := strings.ReplaceAll(volumeID, "-", "_") + dmDeviceDirs, err := afero.Glob(c.osFs, dmDevicePattern) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Warn("Could not read dm device directories.") + return "", err + } + + for _, deviceDir := range dmDeviceDirs { + // "/sys/block/dm-#/dm/name" contains the name of the device-mapper device. + namePath := filepath.Join(deviceDir, "dm", "name") + nameBytes, err := c.osFs.ReadFile(namePath) + if err != nil { + // If an error occurs or the /dm-#/dm/name file is empty, log and continue to next dm-#. + Logc(ctx).WithFields(fields).WithError(err).Error("Could not inspect dm device name.") + continue // Try next dm-# + } else if len(nameBytes) == 0 { + continue + } + mapperDevice := strings.TrimSpace(string(nameBytes)) + + if strings.Contains(mapperDevice, luksSuffix) { + dmNode := filepath.Base(deviceDir) // /sys/block/dm-# -> dm-# + Logc(ctx).WithFields(LogFields{ + "volumeID": volumeID, // pvc-33bd3006-4765-498e-b61d-eae1d035c487 + "mapperDevice": mapperDevice, // luks-pvc_33bd3006_4765_498e_b61d_eae1d035c487 + "dmNode": dmNode, // dm-# + }).Info("Found LUKS device for volume.") + return DevMapperRoot + mapperDevice, nil + } + } + + return "", errors.NotFoundError("no LUKS mapper found for volume ID %s", luksSuffix) +} + // ScanTargetLUN scans a single LUN or all the LUNs on an iSCSI target to discover it. // If all the LUNs are to be scanned please pass -1 for lunID. func (c *Client) ScanTargetLUN(ctx context.Context, deviceAddresses []models.ScsiDeviceAddress) error { diff --git a/utils/devices/devices_test.go b/utils/devices/devices_test.go index 002037f80..452ff9aa0 100644 --- a/utils/devices/devices_test.go +++ b/utils/devices/devices_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package devices @@ -847,6 +847,84 @@ func TestGetLUKSDeviceForMultipathDevice(t *testing.T) { } } +func TestGetLUKSDevicePathForVolume(t *testing.T) { + const ( + volumeID = "pvc-33bd3006-4765-498e-b61d-eae1d035c487" + luksSuffix = "pvc_33bd3006_4765_498e_b61d_eae1d035c487" + mapperName = "luks-" + luksSuffix + mapperDevPath = "/dev/mapper/" + mapperName + ) + tests := map[string]struct { + getFs func() afero.Fs + expectPath string + assertError assert.ErrorAssertionFunc + }{ + "Happy Path": { + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(mapperName), 0o644) + return fs + }, + expectPath: mapperDevPath, + assertError: assert.NoError, + }, + "No Matching Device": { + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("not-a-luks-device"), 0o644) + return fs + }, + expectPath: "", + assertError: assert.Error, + }, + "Error Reading Name File": { + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + // Do not create the name file, so ReadFile will error + return fs + }, + expectPath: "", + assertError: assert.Error, + }, + "Empty Name File": { + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(""), 0o644) + return fs + }, + expectPath: "", + assertError: assert.Error, + }, + "Multiple Devices, Only One Matches": { + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/dm", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("not-a-luks-device"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/name", []byte(mapperName), 0o644) + return fs + }, + expectPath: mapperDevPath, + assertError: assert.NoError, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + fs := tc.getFs() + client := &Client{osFs: afero.Afero{Fs: fs}} + path, err := client.GetLUKSDevicePathForVolume(ctx, volumeID) + tc.assertError(t, err) + assert.Equal(t, tc.expectPath, path) + }) + } +} + func TestFindMultipathDeviceForDevice(t *testing.T) { device := "sda" tests := map[string]struct { diff --git a/utils/devices/luks/luks.go b/utils/devices/luks/luks.go index ffc125d6c..6f15696e2 100644 --- a/utils/devices/luks/luks.go +++ b/utils/devices/luks/luks.go @@ -1,12 +1,13 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package luks -//go:generate mockgen -destination=../../../mocks/mock_utils/mock_devices/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/devices/luks Device +//go:generate mockgen -destination=../../../mocks/mock_utils/mock_devices/mock_luks/mock_luks.go -package mock_luks github.com/netapp/trident/utils/devices/luks OS,Device import ( "context" "fmt" + "os" "strings" "github.com/spf13/afero" @@ -20,10 +21,59 @@ import ( const ( devicePrefix = "luks-" - // LUKS2 requires ~16MiB for overhead. Default to 18MiB just in case. + // MetadataSize is ~16MiB for overhead for LUKS2 headers. Default to 18MiB just in case. MetadataSize = 18874368 ) +// OS is an abstraction over afero.Fs, afero.LinkReader and os utility functions +// to provide a concise interface for LUKSDevice's to interact with the filesystem. +// It should not be used outside of this package, but it is exported for mock generation. +type OS interface { + Stat(name string) (os.FileInfo, error) + Glob(pattern string) ([]string, error) + ReadDir(dirname string) ([]os.FileInfo, error) + ReadFile(filename string) ([]byte, error) + ReadlinkIfPossible(name string) (string, error) +} + +// osFs is a helper that wraps operations on the OS filesystem and implements osFsAbs. +// It should not be used outside of this package. +type osFs struct { + fs afero.Fs +} + +// Ensure osFs always implements OS. +var _ OS = &osFs{} + +func newOsFs(fs afero.Fs) OS { + return &osFs{fs: fs} +} + +func (o *osFs) Stat(name string) (os.FileInfo, error) { + return o.fs.Stat(name) +} + +func (o *osFs) Glob(pattern string) ([]string, error) { + return afero.Glob(o.fs, pattern) +} + +func (o *osFs) ReadDir(dirname string) ([]os.FileInfo, error) { + return afero.ReadDir(o.fs, dirname) +} + +func (o *osFs) ReadFile(filename string) ([]byte, error) { + return afero.ReadFile(o.fs, filename) +} + +func (o *osFs) ReadlinkIfPossible(name string) (string, error) { + if lr, ok := o.fs.(afero.LinkReader); ok { + return lr.ReadlinkIfPossible(name) + } + // Symlinks don't work with in-memory filesystems. + // Regardless, fall back to os.Readlink. + return os.Readlink(name) +} + type Device interface { EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) MappedDevicePath() string @@ -32,6 +82,7 @@ type Device interface { EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, error) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error + IsMappingStale(ctx context.Context) bool } type LUKSDevice struct { @@ -39,25 +90,23 @@ type LUKSDevice struct { mappedDeviceName string command execCmd.Command devices devices.Devices - osFs afero.Fs + osFs OS } func NewDevice(rawDevicePath, volumeId string, command execCmd.Command) *LUKSDevice { luksDeviceName := devicePrefix + volumeId - devices := devices.New() - osFs := afero.NewOsFs() - return NewDetailed(rawDevicePath, luksDeviceName, command, devices, osFs) + return NewDetailed(rawDevicePath, luksDeviceName, command, devices.New(), afero.NewOsFs()) } -func NewDetailed(rawDevicePath, mappedDeviceName string, command execCmd.Command, devices devices.Devices, - osFs afero.Fs, +func NewDetailed( + rawDevicePath, mappedDeviceName string, command execCmd.Command, devices devices.Devices, osFs afero.Fs, ) *LUKSDevice { return &LUKSDevice{ rawDevicePath: rawDevicePath, mappedDeviceName: mappedDeviceName, command: command, devices: devices, - osFs: osFs, + osFs: newOsFs(osFs), } } @@ -71,7 +120,7 @@ func NewDeviceFromMappingPath( return NewDevice(rawDevicePath, volumeId, command), nil } -// EnsureLUKSDeviceMappedOnHost ensures the specified device is LUKS formatted, opened, and has the current passphrase. +// EnsureDeviceMappedOnHost ensures the specified device is LUKS formatted, opened, and has the current passphrase. func (d *LUKSDevice) EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) { // Try to Open with current luks passphrase luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase := GetLUKSPassphrasesFromSecretMap(secrets) @@ -136,6 +185,18 @@ func (d *LUKSDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase return d.ensureLUKSDevice(ctx, luksPassphrase) } +// IsMappingStale checks if the LUKS mapping is stale (i.e., mapped but the underlying device is gone). +// Currently, this is only supported for LUKS w/NVMe devices. +func (d *LUKSDevice) IsMappingStale(ctx context.Context) bool { + fields := LogFields{ + "devicePath": d.rawDevicePath, + "mappedPath": d.MappedDevicePath(), + } + Logc(ctx).WithFields(fields).Debug(">>>> luks.IsMappingStale") + defer Logc(ctx).WithFields(fields).Debug("<<<< luks.IsMappingStale") + return d.isMappingStale(ctx) +} + func (d *LUKSDevice) ensureLUKSDevice(ctx context.Context, luksPassphrase string) (bool, error) { // First check if LUKS device is already opened. This is OK to check even if the device isn't LUKS formatted. if isOpen, err := d.IsOpen(ctx); err != nil { diff --git a/utils/devices/luks/luks_darwin.go b/utils/devices/luks/luks_darwin.go index 4a37e69ec..86c2d701b 100644 --- a/utils/devices/luks/luks_darwin.go +++ b/utils/devices/luks/luks_darwin.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package luks @@ -63,3 +63,7 @@ func (d *LUKSDevice) Resize(ctx context.Context, luksPassphrase string) error { defer Logc(ctx).Debug("<<<< devices_darwin.Resize") return errors.UnsupportedError("Resize is not supported for darwin") } + +func (d *LUKSDevice) isMappingStale(_ context.Context) bool { + return false +} diff --git a/utils/devices/luks/luks_linux.go b/utils/devices/luks/luks_linux.go index dd3d4fa62..a607df2ec 100644 --- a/utils/devices/luks/luks_linux.go +++ b/utils/devices/luks/luks_linux.go @@ -1,10 +1,12 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package luks import ( "fmt" + "os" "os/exec" + "path/filepath" "strings" "time" @@ -119,7 +121,6 @@ func (d *LUKSDevice) format(ctx context.Context, luksPassphrase string) error { // underlying device already has a format present. func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, luksPassphrase string) error { fields := LogFields{"device": d.RawDevicePath()} - Logc(ctx).WithFields(fields).Debug("Attempting to LUKS format device.") // Check if the device is already LUKS formatted. if luksFormatted, err := d.IsLUKSFormatted(ctx); err != nil { @@ -169,8 +170,10 @@ func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { return false, errors.New("no device path for LUKS device") } device := d.RawDevicePath() + luksDeviceName := d.MappedDeviceName() - Logc(ctx).WithField("device", device).Debug("Checking if device is a LUKS device.") + fields := LogFields{"device": device, "luksDeviceName": luksDeviceName} + Logc(ctx).WithFields(fields).Debug("Checking if device is a LUKS device.") if err := beforeLuksCheck.Inject(); err != nil { return false, err @@ -180,7 +183,7 @@ func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { ctx, "cryptsetup", luksCommandTimeout, true, "", "isLuks", device, ) if err != nil { - fields := LogFields{"device": device, "output": string(output)} + fields["output"] = string(output) // If the error isn't an exit error, then some other issue happened. exitError, ok := err.(execCmd.ExitError) @@ -201,7 +204,7 @@ func (d *LUKSDevice) IsLUKSFormatted(ctx context.Context) (bool, error) { return false, nil } - Logc(ctx).WithField("device", device).Debug("Device is a LUKS device.") + Logc(ctx).WithFields(fields).Debug("Device is a LUKS device.") return true, nil } @@ -404,3 +407,80 @@ func (d *LUKSDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) } return true, nil } + +// isMappingStale determines whether the LUKS mapping is stale by checking if the underlying device is still accessible. +// This is only supported with LUKS NVMe devices. +func (d *LUKSDevice) isMappingStale(ctx context.Context) bool { + fields := LogFields{ + "mappedDevicePath": d.MappedDevicePath(), + "rawDevicePath": d.rawDevicePath, + } + const dmDevicePattern = "/sys/block/dm-*" + + // A non-existent device mapper cannot be stale. + if _, err := d.osFs.Stat(d.MappedDevicePath()); os.IsNotExist(err) { + Logc(ctx).WithFields(fields).Info("LUKS device mapper not found.") + return false + } + deviceNode := filepath.Base(d.rawDevicePath) + mapperName := d.mappedDeviceName + + dmDirs, err := d.osFs.Glob(dmDevicePattern) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Debug("Could not read dm device directories.") + return true + } + + for _, dmDir := range dmDirs { + dmNamePath := filepath.Join(dmDir, "dm", "name") + dmNameBytes, err := d.osFs.ReadFile(dmNamePath) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Error("Could not inspect dm device name.") + continue + } else if strings.TrimSpace(string(dmNameBytes)) != mapperName { + continue + } + Logc(ctx).WithFields(fields).WithField("dmDevice", dmNamePath).Debug("Found LUKS device-mapper device.") + + slavesDir := filepath.Join(dmDir, "slaves") + slaveEntries, err := d.osFs.ReadDir(slavesDir) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Error("Could not determine target device for LUKS mapper.") + return true + } else if len(slaveEntries) == 0 { + Logc(ctx).WithFields(fields).Debug("No target devices found for LUKS mapper.") + return true + } + + for _, slaveEntry := range slaveEntries { + slaveNode := slaveEntry.Name() + if slaveNode != deviceNode { + continue + } + + slavePath := filepath.Join(slavesDir, slaveNode) + target, err := d.osFs.ReadlinkIfPossible(slavePath) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Debug("Target device symlink is broken for LUKS mapper.") + return true + } + absTarget := target + if !filepath.IsAbs(absTarget) { + absTarget = filepath.Join(filepath.Dir(slavePath), target) + } + if _, err := d.osFs.Stat(absTarget); err != nil { + Logc(ctx).WithFields(fields).WithError(err).Debug("Target device is not accessible for LUKS mapper.") + return true + } + + Logc(ctx).WithFields(fields).Debug("LUKS device is not stale.") + return false + } + + Logc(ctx).WithFields(fields).Debug("Device node not found among target devices; treating as stale.") + return true + } + + // No matching dm-* found for the mapped device, so it must be stale. + return true +} diff --git a/utils/devices/luks/luks_linux_test.go b/utils/devices/luks/luks_linux_test.go index 10b96feb6..9af144185 100644 --- a/utils/devices/luks/luks_linux_test.go +++ b/utils/devices/luks/luks_linux_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. //go:build linux @@ -6,7 +6,10 @@ package luks import ( "context" + "fmt" + "os" "testing" + "time" "github.com/spf13/afero" "github.com/stretchr/testify/assert" @@ -14,6 +17,7 @@ import ( "golang.org/x/sys/unix" "github.com/netapp/trident/mocks/mock_utils/mock_devices" + "github.com/netapp/trident/mocks/mock_utils/mock_devices/mock_luks" "github.com/netapp/trident/mocks/mock_utils/mock_exec" mockexec "github.com/netapp/trident/mocks/mock_utils/mock_exec" "github.com/netapp/trident/utils/devices" @@ -810,3 +814,161 @@ func TestGenerateAnonymousMemFile(t *testing.T) { err = unix.Close(fd) assert.NoError(t, err, "expected no error closing anonymous mem file") } + +// Stub file info for unit testing +type fakeFileInfo struct { + name string +} + +func (f fakeFileInfo) Name() string { return f.name } +func (f fakeFileInfo) Size() int64 { return 0 } +func (f fakeFileInfo) Mode() os.FileMode { return 0 } +func (f fakeFileInfo) ModTime() time.Time { return time.Time{} } +func (f fakeFileInfo) IsDir() bool { return false } +func (f fakeFileInfo) Sys() interface{} { return nil } + +func TestLUKSDevice_IsMappingStale(t *testing.T) { + type deviceOption func(device *LUKSDevice) + instrumentDevice := func(opts ...deviceOption) *LUKSDevice { + device := &LUKSDevice{ + rawDevicePath: "/dev/sdb", + mappedDeviceName: "pvc-test", + } + for _, opt := range opts { + opt(device) + } + return device + } + + tt := map[string]struct { + createOpt func(*gomock.Controller) deviceOption + assertBool assert.BoolAssertionFunc + }{ + "with no mapper not found": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, os.ErrNotExist).Times(1) + + device.osFs = mockOS + } + }, + assertBool: assert.False, + }, + "with failure to glob dm-* directories": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{"/sys/block/dm-0"}, errors.New("mock-error")) + + device.osFs = mockOS + } + }, + assertBool: assert.True, + }, + "with no dm-* directories found": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{}, nil) + + device.osFs = mockOS + } + }, + assertBool: assert.True, + }, + "with dm-* directory found, wrong device name": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{"/sys/block/dm-0"}, nil) + mockOS.EXPECT().ReadFile("/sys/block/dm-0/dm/name").Return([]byte("not-the-mapper"), nil) + + device.osFs = mockOS + } + }, + assertBool: assert.True, + }, + "with dm-* directory found, correct device name, no slaves": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{"/sys/block/dm-0"}, nil) + mockOS.EXPECT().ReadFile("/sys/block/dm-0/dm/name").Return([]byte(device.mappedDeviceName), nil) + mockOS.EXPECT().ReadDir("/sys/block/dm-0/slaves").Return([]os.FileInfo{}, nil) + + device.osFs = mockOS + } + }, + assertBool: assert.True, + }, + "with dm-* directory found, correct device name, slave symlink broken": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{"/sys/block/dm-0"}, nil) + mockOS.EXPECT().ReadFile("/sys/block/dm-0/dm/name").Return([]byte(device.mappedDeviceName), nil) + // One slave entry matching deviceNode + slaveInfo := fakeFileInfo{name: "sdb"} + mockOS.EXPECT().ReadDir("/sys/block/dm-0/slaves").Return([]os.FileInfo{slaveInfo}, nil) + mockOS.EXPECT().ReadlinkIfPossible("/sys/block/dm-0/slaves/sdb").Return("", + fmt.Errorf("broken symlink")) + + device.osFs = mockOS + } + }, + assertBool: assert.True, + }, + "with dm-* directory found, correct device name, slave symlink ok, target device missing": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{"/sys/block/dm-0"}, nil) + mockOS.EXPECT().ReadFile("/sys/block/dm-0/dm/name").Return([]byte(device.mappedDeviceName), nil) + // One slave entry matching deviceNode + slaveInfo := fakeFileInfo{name: "sdb"} + mockOS.EXPECT().ReadDir("/sys/block/dm-0/slaves").Return([]os.FileInfo{slaveInfo}, nil) + mockOS.EXPECT().ReadlinkIfPossible("/sys/block/dm-0/slaves/sdb").Return("/dev/sdb", nil) + mockOS.EXPECT().Stat("/dev/sdb").Return(nil, os.ErrNotExist) + + device.osFs = mockOS + } + }, + assertBool: assert.True, + }, + "with dm-* directory found, correct device name, slave symlink ok, target device present": { + createOpt: func(ctrl *gomock.Controller) deviceOption { + return func(device *LUKSDevice) { + mockOS := mock_luks.NewMockOS(ctrl) + mockOS.EXPECT().Stat(device.MappedDevicePath()).Return(nil, nil) + mockOS.EXPECT().Glob("/sys/block/dm-*").Return([]string{"/sys/block/dm-0"}, nil) + mockOS.EXPECT().ReadFile("/sys/block/dm-0/dm/name").Return([]byte(device.mappedDeviceName), nil) + // One slave entry matching deviceNode + slaveInfo := fakeFileInfo{name: "sdb"} + mockOS.EXPECT().ReadDir("/sys/block/dm-0/slaves").Return([]os.FileInfo{slaveInfo}, nil) + mockOS.EXPECT().ReadlinkIfPossible("/sys/block/dm-0/slaves/sdb").Return("/dev/sdb", nil) + mockOS.EXPECT().Stat("/dev/sdb").Return(nil, nil) + + device.osFs = mockOS + } + }, + assertBool: assert.False, + }, + } + + for name, params := range tt { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + assertBool := params.assertBool + device := instrumentDevice(params.createOpt(ctrl)) + assertBool(t, device.IsMappingStale(ctx)) + }) + } +} diff --git a/utils/devices/luks/luks_test.go b/utils/devices/luks/luks_test.go index 8ff855fa8..d1b422d51 100644 --- a/utils/devices/luks/luks_test.go +++ b/utils/devices/luks/luks_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package luks @@ -14,3 +14,25 @@ func TestNewDevice(t *testing.T) { assert.Equal(t, luksDevice.MappedDevicePath(), "/dev/mapper/luks-pvc-test") assert.Equal(t, luksDevice.MappedDeviceName(), "luks-pvc-test") } + +func TestIsLegacyDevicePath(t *testing.T) { + tests := map[string]struct { + name string + devicePath string + expected bool + }{ + "legacy luks device path": { + devicePath: "/dev/mapper/luks-trident_pvc_4b7874ba_58d7_4d93_8d36_09a09b837f81", + expected: true, + }, + "non-legacy luks device path": { + devicePath: "/dev/mapper/mpath-36001405b09b0d1f4d0000000000000a1", + expected: false, + }, + } + for name, params := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(t, params.expected, IsLegacyDevicePath(params.devicePath)) + }) + } +} diff --git a/utils/devices/luks/luks_windows.go b/utils/devices/luks/luks_windows.go index 72516ee91..73ed58355 100644 --- a/utils/devices/luks/luks_windows.go +++ b/utils/devices/luks/luks_windows.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package luks @@ -71,3 +71,7 @@ func IsOpen(ctx context.Context, devicePath string) (bool, error) { defer Logc(ctx).Debug("<<<< devices_windows.IsOpen") return false, errors.UnsupportedError("IsOpen is not supported for windows") } + +func (d *LUKSDevice) isMappingStale(_ context.Context) bool { + return false +} diff --git a/utils/devices/luks/utils_test.go b/utils/devices/luks/utils_test.go deleted file mode 100644 index 37c8d2795..000000000 --- a/utils/devices/luks/utils_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. - -package luks - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIsLegacyDevicePath(t *testing.T) { - tests := map[string]struct { - name string - devicePath string - expected bool - }{ - "legacy luks device path": { - devicePath: "/dev/mapper/luks-trident_pvc_4b7874ba_58d7_4d93_8d36_09a09b837f81", - expected: true, - }, - "non-legacy luks device path": { - devicePath: "/dev/mapper/mpath-36001405b09b0d1f4d0000000000000a1", - expected: false, - }, - } - for name, params := range tests { - t.Run(name, func(t *testing.T) { - assert.Equal(t, params.expected, IsLegacyDevicePath(params.devicePath)) - }) - } -} diff --git a/utils/filesystem/json.go b/utils/filesystem/json.go index f35795de2..3083a925d 100644 --- a/utils/filesystem/json.go +++ b/utils/filesystem/json.go @@ -1,4 +1,4 @@ -// Copyright 2022 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package filesystem diff --git a/utils/iscsi/iscsi.go b/utils/iscsi/iscsi.go index 7a34acd27..1b682ed8e 100644 --- a/utils/iscsi/iscsi.go +++ b/utils/iscsi/iscsi.go @@ -25,6 +25,7 @@ import ( "github.com/netapp/trident/internal/fiji" . "github.com/netapp/trident/logging" "github.com/netapp/trident/pkg/collection" + "github.com/netapp/trident/pkg/convert" "github.com/netapp/trident/pkg/network" "github.com/netapp/trident/utils/devices" "github.com/netapp/trident/utils/devices/luks" @@ -405,18 +406,12 @@ func (client *Client) AttachVolume( return mpathSize, fmt.Errorf("could not find device %v; %s", devicePath, err) } - var isLUKSDevice, luksFormatted bool - if publishInfo.LUKSEncryption != "" { - isLUKSDevice, err = strconv.ParseBool(publishInfo.LUKSEncryption) - if err != nil { - return mpathSize, fmt.Errorf("could not parse LUKSEncryption into a bool, got %v", - publishInfo.LUKSEncryption) - } - } - // Return the device in the publish info in case the mount will be done later publishInfo.DevicePath = devicePath + // If LUKS encryption is requested, ensure the device is formatted and open. + var luksFormatted bool + isLUKSDevice := convert.ToBool(publishInfo.LUKSEncryption) if isLUKSDevice { luksDevice := luks.NewDevice(devicePath, name, client.command) luksFormatted, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) @@ -427,6 +422,17 @@ func (client *Client) AttachVolume( devicePath = luksDevice.MappedDevicePath() } + // Fail fast if the device should be a LUKS device but is not LUKS formatted. + if isLUKSDevice && !luksFormatted { + Logc(ctx).WithFields(LogFields{ + "devicePath": publishInfo.DevicePath, + "luksMapperPath": devicePath, + "isLUKSFormatted": luksFormatted, + "isLUKSDevice": isLUKSDevice, + }).Error("Device should be a LUKS device but is not LUKS formatted.") + return mpathSize, errors.New("device should be a LUKS device but is not LUKS formatted") + } + if publishInfo.FilesystemType == filesystem.Raw { return mpathSize, nil } @@ -440,7 +446,7 @@ func (client *Client) AttachVolume( if unformatted, err := client.devices.IsDeviceUnformatted(ctx, devicePath); err != nil { Logc(ctx).WithField( "device", devicePath, - ).WithError(err).Errorf("Unable to identify if the device is unformatted.") + ).WithError(err).Errorf("Unable to identify if the device is not formatted.") return mpathSize, err } else if !unformatted { Logc(ctx).WithField( @@ -448,17 +454,16 @@ func (client *Client) AttachVolume( ).WithError(err).Errorf("Device is not unformatted.") return mpathSize, fmt.Errorf("device %v is not unformatted", devicePath) } - } else { - // We can safely assume if we just luksFormatted the device, we can also add a filesystem without dataloss - if !luksFormatted { - Logc(ctx).WithField("device", - devicePath).Errorf("Unable to identify if the luks device is empty; err: %v", err) - return mpathSize, err - } } - Logc(ctx).WithFields(LogFields{"volume": name, "fstype": publishInfo.FilesystemType}).Debug("Formatting LUN.") - if err = client.fileSystemClient.FormatVolume(ctx, devicePath, publishInfo.FilesystemType, publishInfo.FormatOptions); err != nil { + Logc(ctx).WithFields(LogFields{ + "volume": name, + "lunID": lunID, + "fstype": publishInfo.FilesystemType, + "formatOptions": publishInfo.FormatOptions, + }).Debug("Formatting iSCSI LUN.") + err = client.fileSystemClient.FormatVolume(ctx, devicePath, publishInfo.FilesystemType, publishInfo.FormatOptions) + if err != nil { return mpathSize, fmt.Errorf("error formatting LUN %s, device %s: %v", name, deviceToUse, err) } } else if existingFstype != filesystem.UnknownFstype && existingFstype != publishInfo.FilesystemType { diff --git a/utils/iscsi/iscsi_test.go b/utils/iscsi/iscsi_test.go index b36fc5681..8756a6ba6 100644 --- a/utils/iscsi/iscsi_test.go +++ b/utils/iscsi/iscsi_test.go @@ -1287,86 +1287,6 @@ tcp: [4] 127.0.0.2:3260,1029 ` + targetIQN + ` (non-flash)` volumeAuthSecrets: make(map[string]string, 0), assertError: assert.Error, }, - "invalid LUKS encryption value in publish info": { - chrootPathPrefix: "", - getCommand: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-V").Return(nil, nil) - mockCommand.EXPECT().Execute(context.TODO(), "pgrep", "multipathd").Return([]byte("150"), nil) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipathd", 5*time.Second, false, "show", - "config").Return([]byte(multipathConfig("no", false)), nil) - mockCommand.EXPECT().Execute(context.TODO(), "iscsiadm", "-m", - "session").Return([]byte(iscsiadmSessionOutput), nil) - return mockCommand - }, - getOSClient: func(controller *gomock.Controller) OS { - mockOsClient := mock_iscsi.NewMockOS(controller) - mockOsClient.EXPECT().PathExists("/dev/sda/block").Return(true, nil) - return mockOsClient - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().WaitForDevice(context.TODO(), "/dev/dm-0").Return(nil) - mockDevices.EXPECT().GetMultipathDeviceUUID("dm-0").Return("mpath-53594135475a464a3847314d3930354756483748", nil) - mockDevices.EXPECT().GetLunSerial(context.TODO(), "/dev/sda").Return(vpdpg80Serial, nil).Times(3) - mockDevices.EXPECT().ScanTargetLUN(context.TODO(), ScsiScanZeros) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(2) - mockDevices.EXPECT().VerifyMultipathDeviceSize(context.TODO(), "dm-0", "sda").Return(int64(0), true, - nil) - return mockDevices - }, - getFileSystemClient: func(controller *gomock.Controller) filesystem.Filesystem { - mockFileSystem := mock_filesystem.NewMockFilesystem(controller) - return mockFileSystem - }, - getMountClient: func(controller *gomock.Controller) mount.Mount { - mockMount := mock_mount.NewMockMount(controller) - return mockMount - }, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), targetIQN). - Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}). - Times(6) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil).Times(2) - return mockReconcileUtils - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - f, err := fs.Create("/dev/sda/vpd_pg80") - assert.NoError(t, err) - - _, err = f.Write(vpdpg80SerialBytes(vpdpg80Serial)) - assert.NoError(t, err) - - _, err = fs.Create("/dev/sda/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/dev/sda/delete") - assert.NoError(t, err) - - err = fs.MkdirAll("/sys/block/sda/holders/dm-0", 777) - assert.NoError(t, err) - return fs - }, - publishInfo: models.VolumePublishInfo{ - LUKSEncryption: "foo", - FilesystemType: filesystem.Ext4, - VolumeAccessInfo: models.VolumeAccessInfo{ - IscsiAccessInfo: models.IscsiAccessInfo{ - IscsiTargetPortal: "127.0.0.1", - IscsiPortals: []string{"127.0.0.2"}, - IscsiTargetIQN: targetIQN, - IscsiLunSerial: vpdpg80Serial, - }, - }, - }, - volumeName: "test-volume", - volumeMountPoint: "/mnt/test-volume", - volumeAuthSecrets: make(map[string]string, 0), - assertError: assert.Error, - }, "failure ensuring LUKS device mapped on host": { chrootPathPrefix: "", getCommand: func(controller *gomock.Controller) tridentexec.Command { diff --git a/utils/nvme/nvme.go b/utils/nvme/nvme.go index 1af7e7e1a..6ad4b4279 100644 --- a/utils/nvme/nvme.go +++ b/utils/nvme/nvme.go @@ -323,12 +323,22 @@ func (nh *NVMeHandler) NVMeMountVolume( // Initially, the device path raw device path for this NVMe namespace. devicePath := publishInfo.DevicePath - // Format and open a LUKS device if LUKS Encryption is set to true. + // If LUKS encryption is requested, ensure the device is formatted and open. var luksFormatted bool var err error isLUKSDevice := convert.ToBool(publishInfo.LUKSEncryption) if isLUKSDevice { luksDevice := luks.NewDevice(devicePath, name, nh.command) + if luksDevice.IsMappingStale(ctx) { + luksPath := luksDevice.MappedDevicePath() + Logc(ctx).WithFields(LogFields{ + "devicePath": devicePath, + "luksMapper": luksPath, + }).Info("Removing stale LUKS mapping.") + if err := nh.devicesClient.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksPath); err != nil { + return fmt.Errorf("could not remove LUKS mapping '%s' for device '%s'; %w", luksPath, devicePath, err) + } + } luksFormatted, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) if err != nil { From ce9e271c156e2c16a84d1b4c026a7da65d654e67 Mon Sep 17 00:00:00 2001 From: Joe Webster <31218426+jwebster7@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:10:31 -0600 Subject: [PATCH 18/30] Stale LUKS mappers revisited --- .../csi/node_helpers/kubernetes/plugin.go | 4 +- frontend/csi/node_server.go | 66 ++-- frontend/csi/node_server_test.go | 61 +-- frontend/csi/volume_publish_manager_test.go | 3 +- .../mock_devices/mock_devices_client.go | 12 +- utils/devices/devices.go | 140 +++++-- utils/devices/devices_test.go | 370 ++++++++++++++---- utils/filesystem/json.go | 2 +- utils/nvme/nvme.go | 4 +- 9 files changed, 461 insertions(+), 201 deletions(-) diff --git a/frontend/csi/node_helpers/kubernetes/plugin.go b/frontend/csi/node_helpers/kubernetes/plugin.go index 1a29279a6..70625e954 100644 --- a/frontend/csi/node_helpers/kubernetes/plugin.go +++ b/frontend/csi/node_helpers/kubernetes/plugin.go @@ -192,13 +192,13 @@ func (h *helper) AddPublishedPath(ctx context.Context, volumeID, pathToAdd strin volTrackingInfo, err := h.ReadTrackingInfo(ctx, volumeID) if err != nil { - return fmt.Errorf("failed to read the tracking file; %v", err) + return fmt.Errorf("failed to read the tracking file; %w", err) } volTrackingInfo.PublishedPaths[pathToAdd] = struct{}{} if err := h.WriteTrackingInfo(ctx, volumeID, volTrackingInfo); err != nil { - return fmt.Errorf("failed to update the tracking file; %v", err) + return fmt.Errorf("failed to update the tracking file; %w", err) } h.publishedPaths[volumeID] = volTrackingInfo.PublishedPaths diff --git a/frontend/csi/node_server.go b/frontend/csi/node_server.go index b659e852d..8a9454be4 100644 --- a/frontend/csi/node_server.go +++ b/frontend/csi/node_server.go @@ -644,12 +644,12 @@ func (p *Plugin) nodeExpandVolume( devicePath := publishInfo.DevicePath if convert.ToBool(publishInfo.LUKSEncryption) { if !luks.IsLegacyDevicePath(devicePath) { - devicePath, err = p.devices.GetLUKSDevicePathForVolume(ctx, volumeId) + devicePath, err = p.devices.GetLUKSDeviceForMultipathDevice(devicePath) if err != nil { Logc(ctx).WithFields(LogFields{ "volumeId": volumeId, "publishedPath": publishInfo.DevicePath, - }).WithError(err).Error("Failed to get LUKS device path for volume.") + }).WithError(err).Error("Failed to get LUKS device path from device path.") return status.Error(codes.Internal, err.Error()) } } @@ -1468,18 +1468,15 @@ func (p *Plugin) nodeUnstageFCPVolume( publishInfo.DevicePath = dmPath } } else { - // If not using luks legacy device path we need to find the LUKS mapper device. - luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) + // If not using LUKS legacy device path, we need to find the LUKS mapper device. + luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(publishInfo.DevicePath) if err != nil { - // If the LUKS device is not found, the functional difference is negligible to unstage. - // But it may be useful to log at different levels for observability. - log := Logc(ctx).WithFields(fields).WithError(err) - if errors.IsNotFoundError(err) { - log.Warn("Failed to get LUKS device path for volume.") - } else { - log.Debug("Could not determine LUKS device path for volume.") + if !errors.IsNotFoundError(err) { + Logc(ctx).WithFields(fields).WithError(err).Warn( + "Could not determine LUKS device path from multipath device. " + + "Continuing with device removal.") } - log.Debug("Continuing with device removal.") + Logc(ctx).WithFields(fields).Info("No LUKS device path found from multipath device.") } } err = p.devices.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksMapperPath) @@ -1522,10 +1519,11 @@ func (p *Plugin) nodeUnstageFCPVolume( "multipathDevice": deviceInfo.MultipathDevice, } - luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) + luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(deviceInfo.MultipathDevice) if err != nil { if !errors.IsNotFoundError(err) { - Logc(ctx).WithFields(fields).WithError(err).Error("Failed to get LUKS device path from multipath device.") + Logc(ctx).WithFields(fields). + WithError(err).Error("Failed to get LUKS device path from multipath device.") return err } Logc(ctx).WithFields(fields).Info("No LUKS device path found from multipath device.") @@ -1997,7 +1995,7 @@ func (p *Plugin) nodeUnstageISCSIVolume( if convert.ToBool(publishInfo.LUKSEncryption) { var err error var luksMapperPath string - fields := LogFields{"device": publishInfo.DevicePath, "volume": req.GetVolumeId()} + fields := LogFields{"device": publishInfo.DevicePath} // Set device path to dm device to correctly verify legacy volumes. if luks.IsLegacyDevicePath(publishInfo.DevicePath) { luksMapperPath = publishInfo.DevicePath @@ -2011,22 +2009,17 @@ func (p *Plugin) nodeUnstageISCSIVolume( publishInfo.DevicePath = dmPath } } else { - // Use the volume ID to get the LUKS mapper path. - // This should always work if the mapper is still present. - luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) + // If not using LUKS legacy device path, we need to find the LUKS mapper device. + luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(publishInfo.DevicePath) if err != nil { - // If the LUKS device is not found, the functional difference is negligible to unstage. - // But it may be useful to log at different levels for observability. - log := Logc(ctx).WithFields(fields).WithError(err) - if errors.IsNotFoundError(err) { - log.Warn("Failed to get LUKS device path for volume.") - } else { - log.Debug("Could not determine LUKS device path for volume.") + if !errors.IsNotFoundError(err) { + Logc(ctx).WithFields(fields).WithError(err).Warn( + "Could not determine LUKS device path from multipath device. " + + "Continuing with device removal.") } - log.Debug("Continuing with device removal.") + Logc(ctx).WithFields(fields).Info("No LUKS device path found from multipath device.") } } - err = p.devices.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksMapperPath) if err != nil { Logc(ctx).WithError(err).Debug("Unable to remove LUKS device. Continuing with tracking file removal.") @@ -2077,7 +2070,7 @@ func (p *Plugin) nodeUnstageISCSIVolume( "multipathDevice": deviceInfo.MultipathDevice, } - luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) + luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(deviceInfo.MultipathDevice) if err != nil { if !errors.IsNotFoundError(err) { Logc(ctx).WithFields(fields).WithError(err).Error("Failed to get LUKS device path from multipath device.") @@ -3093,19 +3086,14 @@ func (p *Plugin) nodeUnstageNVMeVolume( "publishedPath": publishInfo.DevicePath, } - // Use the volume ID to get the LUKS mapper path. - // This should always work if the mapper is still present. - luksMapperPath, err = p.devices.GetLUKSDevicePathForVolume(ctx, req.GetVolumeId()) + luksMapperPath, err = p.devices.GetLUKSDevicePathForDevicePath(ctx, devicePath) if err != nil { - // If the LUKS device is not found, the functional difference is negligible to unstage. - // But it may be useful to log at different levels for observability. - log := Logc(ctx).WithFields(fields).WithError(err) - if errors.IsNotFoundError(err) { - log.Warn("Failed to get LUKS device path for volume.") - } else { - log.Debug("Could not determine LUKS device path for volume.") + if !errors.IsNotFoundError(err) { + Logc(ctx).WithFields(fields).WithError(err).Error("Failed to get LUKS device path from device path.") + return &csi.NodeUnstageVolumeResponse{}, err } - log.Debug("Continuing with device removal.") + Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to get LUKS device path from device path. " + + "Device may already be removed.") } if luksMapperPath != "" { diff --git a/frontend/csi/node_server_test.go b/frontend/csi/node_server_test.go index 78b096d9b..3afe1bb8e 100644 --- a/frontend/csi/node_server_test.go +++ b/frontend/csi/node_server_test.go @@ -2131,7 +2131,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), @@ -2178,7 +2178,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) @@ -2223,7 +2223,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(fmt.Errorf("mock error")) return mockDeviceClient @@ -2242,7 +2242,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), @@ -2317,8 +2317,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { return mockDeviceClient }, }, - // mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) - "SAN: iSCSI unstage: GetLUKSDevicePathForVolume error": { + "SAN: iSCSI unstage: GetLUKSDeviceForMultipathDevice error": { assertError: assert.Error, request: NewNodeUnstageVolumeRequestBuilder().Build(), publishInfo: NewVolumePublishInfoBuilder(TypeiSCSIVolumePublishInfo).WithLUKSEncryption("true").Build(), @@ -2338,8 +2337,8 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), - gomock.Any()).Return(mockDevicePath, errors.New("mock error")) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", fmt.Errorf( + "mock error")) return mockDeviceClient }, }, @@ -2359,7 +2358,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) return mockDeviceClient @@ -2393,7 +2392,7 @@ func TestNodeUnstageISCSIVolume(t *testing.T) { getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().GetMultipathDeviceBySerial(gomock.Any(), gomock.Any()) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return(mockDevicePath, nil) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return(mockDevicePath, nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), mockDevicePath). Return(nil) mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), mockDevicePath).Return(nil) @@ -11937,7 +11936,7 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume(gomock.Any(), gomock.Any()).Return("", errors.New("")) + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", errors.New("")) return mockDeviceClient }, expErrCode: codes.Internal, @@ -11985,9 +11984,7 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("x/device-path", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("x/device-path", nil).AnyTimes() return mockDeviceClient }, expErrCode: codes.InvalidArgument, @@ -12035,9 +12032,7 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("x/device-path", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("x/device-path", nil).AnyTimes() return mockDeviceClient }, expErrCode: codes.InvalidArgument, @@ -12085,9 +12080,7 @@ func TestNodeExpandVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("x/device-path", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("x/device-path", nil).AnyTimes() return mockDeviceClient }, expErrCode: codes.Internal, @@ -12530,9 +12523,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("", errors.New("")).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", errors.New("")).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() return mockDeviceClient }, @@ -12719,9 +12710,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("", errors.New("")).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("", errors.New("")).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() // mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() @@ -12769,9 +12758,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() @@ -12820,9 +12807,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.MaxWaitExceededError("")).AnyTimes() @@ -12870,9 +12855,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() // mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), // gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.MaxWaitExceededError("")).AnyTimes() @@ -12920,9 +12903,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosedWithMaxWaitLimit(gomock.Any(), gomock.Any()).Return(errors.MaxWaitExceededError("")).AnyTimes() @@ -12971,9 +12952,7 @@ func TestNodeUnstageFCPVolume(t *testing.T) { }, getDeviceClient: func() devices.Devices { mockDeviceClient := mock_devices.NewMockDevices(gomock.NewController(t)) - mockDeviceClient.EXPECT().GetLUKSDevicePathForVolume( - gomock.Any(), gomock.Any(), - ).Return("multipath-device", nil).AnyTimes() + mockDeviceClient.EXPECT().GetLUKSDeviceForMultipathDevice(gomock.Any()).Return("multipath-device", nil).AnyTimes() mockDeviceClient.EXPECT().RemoveMultipathDeviceMappingWithRetries(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockDeviceClient.EXPECT().EnsureLUKSDeviceClosed(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() diff --git a/frontend/csi/volume_publish_manager_test.go b/frontend/csi/volume_publish_manager_test.go index 908c1fb8b..8f71d21cd 100644 --- a/frontend/csi/volume_publish_manager_test.go +++ b/frontend/csi/volume_publish_manager_test.go @@ -144,9 +144,8 @@ func TestReadTrackingInfo(t *testing.T) { mockJSONUtils.EXPECT().ReadJSONFile(gomock.Any(), emptyTrackInfo, fName, "volume tracking info"). SetArg(1, *trackInfo).Return(nil) trackInfo, err := v.ReadTrackingInfo(context.Background(), volId) - assert.NoError(t, err, "no error expected when write succeed") - assert.NotNil(t, trackInfo, "expected a valid tracking info") assert.Equal(t, fsType, trackInfo.FilesystemType, "tracking file did not have expected value in it") + assert.NoError(t, err, "tracking file should have been written") emptyTrackInfo = &models.VolumeTrackingInfo{} mockJSONUtils.EXPECT().ReadJSONFile(gomock.Any(), emptyTrackInfo, fName, diff --git a/mocks/mock_utils/mock_devices/mock_devices_client.go b/mocks/mock_utils/mock_devices/mock_devices_client.go index aa1908cc3..02ff99944 100644 --- a/mocks/mock_utils/mock_devices/mock_devices_client.go +++ b/mocks/mock_utils/mock_devices/mock_devices_client.go @@ -212,19 +212,19 @@ func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(arg0 any) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), arg0) } -// GetLUKSDevicePathForVolume mocks base method. -func (m *MockDevices) GetLUKSDevicePathForVolume(arg0 context.Context, arg1 string) (string, error) { +// GetLUKSDevicePathForDevicePath mocks base method. +func (m *MockDevices) GetLUKSDevicePathForDevicePath(arg0 context.Context, arg1 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLUKSDevicePathForVolume", arg0, arg1) + ret := m.ctrl.Call(m, "GetLUKSDevicePathForDevicePath", arg0, arg1) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetLUKSDevicePathForVolume indicates an expected call of GetLUKSDevicePathForVolume. -func (mr *MockDevicesMockRecorder) GetLUKSDevicePathForVolume(arg0, arg1 any) *gomock.Call { +// GetLUKSDevicePathForDevicePath indicates an expected call of GetLUKSDevicePathForDevicePath. +func (mr *MockDevicesMockRecorder) GetLUKSDevicePathForDevicePath(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDevicePathForVolume", reflect.TypeOf((*MockDevices)(nil).GetLUKSDevicePathForVolume), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDevicePathForDevicePath", reflect.TypeOf((*MockDevices)(nil).GetLUKSDevicePathForDevicePath), arg0, arg1) } // GetLunSerial mocks base method. diff --git a/utils/devices/devices.go b/utils/devices/devices.go index 29479997f..89eceb2f6 100644 --- a/utils/devices/devices.go +++ b/utils/devices/devices.go @@ -67,7 +67,7 @@ type Devices interface { GetLunSerial(ctx context.Context, path string) (string, error) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) - GetLUKSDevicePathForVolume(ctx context.Context, volumeID string) (string, error) + GetLUKSDevicePathForDevicePath(ctx context.Context, devicePath string) (string, error) ScanTargetLUN(ctx context.Context, deviceAddresses []models.ScsiDeviceAddress) error CloseLUKSDevice(ctx context.Context, devicePath string) error EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error @@ -805,51 +805,131 @@ func (c *Client) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string return DevMapperRoot + strings.TrimRight(string(b[luksDeviceUUIDNameOffset:]), "\n"), nil } -// GetLUKSDevicePathForVolume finds the LUKS device path for a given volume ID. -// Every LUKS device Trident creates should include the volume ID, which includes a volume uuid in the suffix. -// Use that knowledge to find the correct LUKS mapper device and path. -func (c *Client) GetLUKSDevicePathForVolume(ctx context.Context, volumeID string) (string, error) { - fields := LogFields{"volumeID": volumeID} - Logc(ctx).WithFields(fields).Debug(">>>> devices.GetLUKSDevicePathForVolume") - defer Logc(ctx).WithFields(fields).Debug("<<<< devices.GetLUKSDevicePathForVolume") - const dmDevicePattern = "/sys/block/dm-*" - - // This isn't great; we essentially have to reconstruct the suffix of the LUKS mapper device name - // which is the internal volume name of the volume. If this is required for iSCSI, - // we must test with SAN and SAN-Eco. - // "pvc-33bd3006-4765-498e-b61d-eae1d035c487" becomes "pvc_33bd3006_4765_498e_b61d_eae1d035c487" - luksSuffix := strings.ReplaceAll(volumeID, "-", "_") +// GetLUKSDevicePathForDevicePath finds the LUKS device path for a given device path. It looks through every +// dm-* device to find the correct LUKS mapper device and path based on the which dm-#/slaves entries. +// This should work even if the underlying devices are ripped out before closing the LUKS mapper, +// because the slaves dirs will have stale entries until the underlying device is removed first. +func (c *Client) GetLUKSDevicePathForDevicePath(ctx context.Context, devicePath string) (string, error) { + fields := LogFields{"devicePath": devicePath} + Logc(ctx).WithFields(fields).Debug(">>>> devices.GetLUKSDevicePathForDevicePath") + defer Logc(ctx).WithFields(fields).Debug("<<<< devices.GetLUKSDevicePathForDevicePath") + + const luksUUIDPrefix = "CRYPT-LUKS2" + + // Clean the device path to get just the device name. + // Example: "/dev/nvmeXnY" -> "nvmeXnY"; "/dev/dm-#" -> "dm-#" + deviceName := strings.TrimPrefix(devicePath, DevPrefix) + deviceName = strings.TrimPrefix(deviceName, "/") + + // Include chroot prefix in the pattern. + // Glob the /sys/block/dm-* directories; we have to search them all. + dmDevicePattern := c.chrootPathPrefix + "/sys/block/dm-*" dmDeviceDirs, err := afero.Glob(c.osFs, dmDevicePattern) if err != nil { Logc(ctx).WithFields(fields).WithError(err).Warn("Could not read dm device directories.") return "", err } + // Search every dm device and their slave devices for the target device. for _, deviceDir := range dmDeviceDirs { - // "/sys/block/dm-#/dm/name" contains the name of the device-mapper device. - namePath := filepath.Join(deviceDir, "dm", "name") - nameBytes, err := c.osFs.ReadFile(namePath) + // Check if this dm device is a "CRYPT-LUKS2" device by inspecting the prefix of the uuid present in: + // "/sys/block/dm-*/dm/uuid" file. This ensures we only consider LUKS devices. + uuidPath := filepath.Join(deviceDir, "dm", "uuid") + uuidBytes, err := c.osFs.ReadFile(uuidPath) if err != nil { - // If an error occurs or the /dm-#/dm/name file is empty, log and continue to next dm-#. - Logc(ctx).WithFields(fields).WithError(err).Error("Could not inspect dm device name.") - continue // Try next dm-# - } else if len(nameBytes) == 0 { + Logc(ctx).WithField("device", deviceDir).WithFields(fields).WithError(err).Debug("Could not read dm uuid.") continue } - mapperDevice := strings.TrimSpace(string(nameBytes)) + uuid := strings.TrimSpace(string(uuidBytes)) + if !strings.HasPrefix(uuid, luksUUIDPrefix) { + // Ignore non-LUKS dm devices. + continue + } + + // Check if this LUKS device is a holder of our supplied devicePath (directly or indirectly) + // The correct dm-# device will have our device as a slave (directly or indirectly). + // Example: + // "devicePath" -> "/dev/nvme0n1" + // "deviceName" -> "nvme0n1" + // "/sys/block/dm-0/slaves/nvme0n1" -> our LUKS device. The mapper name also lives under the dm-0 entry. + if c.deviceIsSlaveOf(ctx, deviceName, filepath.Base(deviceDir), nil) { + namePath := filepath.Join(deviceDir, "dm", "name") + nameBytes, err := c.osFs.ReadFile(namePath) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Error("Could not read LUKS device name.") + continue + } - if strings.Contains(mapperDevice, luksSuffix) { - dmNode := filepath.Base(deviceDir) // /sys/block/dm-# -> dm-# + mapperDevice := strings.TrimSpace(string(nameBytes)) Logc(ctx).WithFields(LogFields{ - "volumeID": volumeID, // pvc-33bd3006-4765-498e-b61d-eae1d035c487 - "mapperDevice": mapperDevice, // luks-pvc_33bd3006_4765_498e_b61d_eae1d035c487 - "dmNode": dmNode, // dm-# - }).Info("Found LUKS device for volume.") + "devicePath": devicePath, + "mapperName": mapperDevice, + "mapperNode": filepath.Base(deviceDir), + }).Info("Found LUKS device for device path.") return DevMapperRoot + mapperDevice, nil } } - return "", errors.NotFoundError("no LUKS mapper found for volume ID %s", luksSuffix) + return "", errors.NotFoundError("no LUKS mapper found for device path %s", devicePath) +} + +// deviceIsSlaveOf is a helper function to check if a device is a slave of a dm device (recursively). +// For iSCSI and FCP, Trident supports nested dm-mappers (LUKS, Mpath) for a given LUN. +func (c *Client) deviceIsSlaveOf( + ctx context.Context, deviceName, dmDevice string, visited map[string]bool, +) bool { + // Initialize visited map on first call. + // This keeps track of dm devices we've already visited in this recursion chain and aids in cycle detection. + if visited == nil { + visited = make(map[string]bool) + } + + // This detects a cycle, which Trident doesn't support. + // Bail out if we run into a cyclic reference. + if visited[dmDevice] { + return false + } + visited[dmDevice] = true + + // "/sys/block/dm-*/slaves/*" + slavesDir := c.chrootPathPrefix + "/sys/block/" + dmDevice + "/slaves" + slaveDirs, err := c.osFs.ReadDir(slavesDir) + if err != nil { + Logc(ctx).WithField("device", dmDevice).WithError(err).Debug("Could not read dm slaves.") + return false + } + + // Examine each slave entry: + // If it matches our deviceName, return true. + // If it is another dm device, recurse into it. + // Otherwise, continue checking other slaves. + for _, slaveDir := range slaveDirs { + // Technically, the slaves here could be a physical or logical block device. + // Examples of a physical device are: sdX, nvmeXnY, etc. + // Examples of a logical device are: dm-#, etc. + slaveName := slaveDir.Name() + if slaveName == deviceName { + Logc(ctx).WithFields(LogFields{ + "deviceName": slaveName, + "mapperName": dmDevice, + }).Debug("Found matching slave device.") + return true + } + // Recursively check nested dm devices + if strings.HasPrefix(slaveName, "dm-") { + Logc(ctx).WithFields(LogFields{ + "deviceName": deviceName, + "mapperName": dmDevice, + "nestedName": slaveName, + }).Debug("Found nested dm device; recursing.") + if c.deviceIsSlaveOf(ctx, deviceName, slaveName, visited) { + return true + } + } + } + + Logc(ctx).WithField("device", dmDevice).WithError(err).Debug("No slave devices for this device.") + return false } // ScanTargetLUN scans a single LUN or all the LUNs on an iSCSI target to discover it. diff --git a/utils/devices/devices_test.go b/utils/devices/devices_test.go index 452ff9aa0..3f2d3db17 100644 --- a/utils/devices/devices_test.go +++ b/utils/devices/devices_test.go @@ -847,84 +847,6 @@ func TestGetLUKSDeviceForMultipathDevice(t *testing.T) { } } -func TestGetLUKSDevicePathForVolume(t *testing.T) { - const ( - volumeID = "pvc-33bd3006-4765-498e-b61d-eae1d035c487" - luksSuffix = "pvc_33bd3006_4765_498e_b61d_eae1d035c487" - mapperName = "luks-" + luksSuffix - mapperDevPath = "/dev/mapper/" + mapperName - ) - tests := map[string]struct { - getFs func() afero.Fs - expectPath string - assertError assert.ErrorAssertionFunc - }{ - "Happy Path": { - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) - _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(mapperName), 0o644) - return fs - }, - expectPath: mapperDevPath, - assertError: assert.NoError, - }, - "No Matching Device": { - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) - _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("not-a-luks-device"), 0o644) - return fs - }, - expectPath: "", - assertError: assert.Error, - }, - "Error Reading Name File": { - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) - // Do not create the name file, so ReadFile will error - return fs - }, - expectPath: "", - assertError: assert.Error, - }, - "Empty Name File": { - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) - _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(""), 0o644) - return fs - }, - expectPath: "", - assertError: assert.Error, - }, - "Multiple Devices, Only One Matches": { - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) - _ = fs.MkdirAll("/sys/block/dm-1/dm", 0o755) - _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("not-a-luks-device"), 0o644) - _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/name", []byte(mapperName), 0o644) - return fs - }, - expectPath: mapperDevPath, - assertError: assert.NoError, - }, - } - - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - ctx := context.Background() - fs := tc.getFs() - client := &Client{osFs: afero.Afero{Fs: fs}} - path, err := client.GetLUKSDevicePathForVolume(ctx, volumeID) - tc.assertError(t, err) - assert.Equal(t, tc.expectPath, path) - }) - } -} - func TestFindMultipathDeviceForDevice(t *testing.T) { device := "sda" tests := map[string]struct { @@ -1153,3 +1075,295 @@ func TestClearFormatting(t *testing.T) { }) } } + +func TestGetLUKSDevicePathForDevicePath(t *testing.T) { + tests := map[string]struct { + devicePath string + getFs func() afero.Fs + expectResult string + assertError assert.ErrorAssertionFunc + }{ + "Happy Path - Direct slave": { + devicePath: "/dev/nvme0n1", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Create LUKS dm-0 device with nvme0n1 as slave + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("CRYPT-LUKS2-abc123-luks-device\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("luks-abc123\n"), 0o644) + // Create symlink for slave (represented as directory in test) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/nvme0n1", 0o755) + return fs + }, + expectResult: "/dev/mapper/luks-abc123", + assertError: assert.NoError, + }, + "Happy Path - Nested dm devices": { + devicePath: "/dev/sda", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Create LUKS dm-2 that has dm-1 as slave + _ = fs.MkdirAll("/sys/block/dm-2/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-2/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-2/dm/uuid", []byte("CRYPT-LUKS2-nested-luks\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-2/dm/name", []byte("luks-nested\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-2/slaves/dm-1", 0o755) + + // Create intermediate dm-1 that has sda as slave + _ = fs.MkdirAll("/sys/block/dm-1/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("mpath-abc123\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/sda", 0o755) + + return fs + }, + expectResult: "/dev/mapper/luks-nested", + assertError: assert.NoError, + }, + "Device path without /dev prefix": { + devicePath: "nvme0n1", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("CRYPT-LUKS2-test\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("luks-test\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/nvme0n1", 0o755) + return fs + }, + expectResult: "/dev/mapper/luks-test", + assertError: assert.NoError, + }, + "No LUKS device found": { + devicePath: "/dev/sdb", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Create non-LUKS dm device + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("mpath-123\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/sdb", 0o755) + return fs + }, + expectResult: "", + assertError: assert.Error, + }, + "Multiple LUKS devices - returns first match": { + devicePath: "/dev/nvme0n1", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // First LUKS device + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("CRYPT-LUKS2-first\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("luks-first\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/nvme0n1", 0o755) + + // Second LUKS device with same slave (shouldn't happen in practice) + _ = fs.MkdirAll("/sys/block/dm-1/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("CRYPT-LUKS2-second\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/name", []byte("luks-second\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/nvme0n1", 0o755) + + return fs + }, + expectResult: "/dev/mapper/luks-first", + assertError: assert.NoError, + }, + "Error reading dm directories": { + devicePath: "/dev/sda", + getFs: func() afero.Fs { + // Return empty filesystem to simulate glob error + return afero.NewReadOnlyFs(afero.NewMemMapFs()) + }, + expectResult: "", + assertError: assert.Error, // Glob returns empty list on error + }, + "UUID file missing - skip device": { + devicePath: "/dev/sda", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + // No UUID file + _ = fs.MkdirAll("/sys/block/dm-0/slaves/sda", 0o755) + return fs + }, + expectResult: "", + assertError: assert.Error, + }, + "Name file missing - continue to next device": { + devicePath: "/dev/sda", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // First device with missing name file + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("CRYPT-LUKS2-noname\n"), 0o644) + // No name file + _ = fs.MkdirAll("/sys/block/dm-0/slaves/sda", 0o755) + + // Second device with everything correct + _ = fs.MkdirAll("/sys/block/dm-1/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("CRYPT-LUKS2-withname\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-1/dm/name", []byte("luks-good\n"), 0o644) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/sda", 0o755) + + return fs + }, + expectResult: "/dev/mapper/luks-good", + assertError: assert.NoError, + }, + "Broken symlink scenario": { + devicePath: "/dev/nvme0n1", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // LUKS device with broken symlink (simulated as directory entry) + _ = fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("CRYPT-LUKS2-broken\n"), 0o644) + _ = afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("luks-broken\n"), 0o644) + // This represents a broken symlink - the entry exists but points nowhere + _ = fs.MkdirAll("/sys/block/dm-0/slaves/nvme0n1", 0o755) + return fs + }, + expectResult: "/dev/mapper/luks-broken", + assertError: assert.NoError, + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + deviceClient := NewDetailed(nil, params.getFs(), nil) + result, err := deviceClient.GetLUKSDevicePathForDevicePath(context.TODO(), params.devicePath) + params.assertError(t, err) + assert.Equal(t, params.expectResult, result) + }) + } +} + +func TestDeviceIsSlaveOf(t *testing.T) { + tests := map[string]struct { + deviceName string + dmDevice string + getFs func() afero.Fs + expectResult bool + }{ + "Direct slave": { + deviceName: "sda", + dmDevice: "dm-0", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/sda", 0o755) + return fs + }, + expectResult: true, + }, + "Not a slave": { + deviceName: "sdb", + dmDevice: "dm-0", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/sda", 0o755) + return fs + }, + expectResult: false, + }, + "Nested slave - one level": { + deviceName: "sda", + dmDevice: "dm-1", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // dm-1 has dm-0 as slave + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/dm-0", 0o755) + // dm-0 has sda as slave + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/sda", 0o755) + return fs + }, + expectResult: true, + }, + "Nested slave - multiple levels": { + deviceName: "nvme0n1", + dmDevice: "dm-3", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // dm-3 -> dm-2 -> dm-1 -> nvme0n1 + _ = fs.MkdirAll("/sys/block/dm-3/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-3/slaves/dm-2", 0o755) + _ = fs.MkdirAll("/sys/block/dm-2/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-2/slaves/dm-1", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/nvme0n1", 0o755) + return fs + }, + expectResult: true, + }, + "Cycle detection": { + deviceName: "sda", + dmDevice: "dm-0", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Create a cycle: dm-0 -> dm-1 -> dm-0 + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-0/slaves/dm-1", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/dm-0", 0o755) + return fs + }, + expectResult: false, + }, + "No slaves directory": { + deviceName: "sda", + dmDevice: "dm-0", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // No slaves directory created + return fs + }, + expectResult: false, + }, + "Empty slaves directory": { + deviceName: "sda", + dmDevice: "dm-0", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + _ = fs.MkdirAll("/sys/block/dm-0/slaves", 0o755) + // No slave entries + return fs + }, + expectResult: false, + }, + "Mixed devices and dm slaves": { + deviceName: "sdc", + dmDevice: "dm-2", + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // dm-2 has both regular devices and dm devices as slaves + _ = fs.MkdirAll("/sys/block/dm-2/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-2/slaves/sda", 0o755) + _ = fs.MkdirAll("/sys/block/dm-2/slaves/sdb", 0o755) + _ = fs.MkdirAll("/sys/block/dm-2/slaves/dm-1", 0o755) + // dm-1 has sdc as slave + _ = fs.MkdirAll("/sys/block/dm-1/slaves", 0o755) + _ = fs.MkdirAll("/sys/block/dm-1/slaves/sdc", 0o755) + return fs + }, + expectResult: true, + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + deviceClient := NewDetailed(nil, params.getFs(), nil) + result := deviceClient.deviceIsSlaveOf(context.TODO(), params.deviceName, params.dmDevice, nil) + assert.Equal(t, params.expectResult, result) + }) + } +} diff --git a/utils/filesystem/json.go b/utils/filesystem/json.go index 3083a925d..f35795de2 100644 --- a/utils/filesystem/json.go +++ b/utils/filesystem/json.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2022 NetApp, Inc. All Rights Reserved. package filesystem diff --git a/utils/nvme/nvme.go b/utils/nvme/nvme.go index 6ad4b4279..7bf00294b 100644 --- a/utils/nvme/nvme.go +++ b/utils/nvme/nvme.go @@ -332,8 +332,8 @@ func (nh *NVMeHandler) NVMeMountVolume( if luksDevice.IsMappingStale(ctx) { luksPath := luksDevice.MappedDevicePath() Logc(ctx).WithFields(LogFields{ - "devicePath": devicePath, - "luksMapper": luksPath, + "device": devicePath, + "mapper": luksPath, }).Info("Removing stale LUKS mapping.") if err := nh.devicesClient.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksPath); err != nil { return fmt.Errorf("could not remove LUKS mapping '%s' for device '%s'; %w", luksPath, devicePath, err) From 05e0ac51e0ae4374c97348dea756c90e07bffa25 Mon Sep 17 00:00:00 2001 From: VinayKumarHavanur <54576364+VinayKumarHavanur@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:57:48 +0530 Subject: [PATCH 19/30] Fix REST API volume lookup to ignore volume state --- storage_drivers/ontap/api/ontap_rest.go | 2 +- storage_drivers/ontap/api/ontap_rest_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/storage_drivers/ontap/api/ontap_rest.go b/storage_drivers/ontap/api/ontap_rest.go index 37f8bc1a2..db259c227 100644 --- a/storage_drivers/ontap/api/ontap_rest.go +++ b/storage_drivers/ontap/api/ontap_rest.go @@ -620,7 +620,7 @@ func (c *RestClient) getVolumeByNameAndStyle( style string, fields []string, ) (*models.Volume, error) { - result, err := c.getAllVolumesByPatternStyleAndState(ctx, volumeName, style, models.VolumeStateOnline, fields) + result, err := c.getAllVolumesByPatternStyleAndState(ctx, volumeName, style, "", fields) if err != nil { return nil, err } diff --git a/storage_drivers/ontap/api/ontap_rest_test.go b/storage_drivers/ontap/api/ontap_rest_test.go index de23823c4..f0e48e238 100644 --- a/storage_drivers/ontap/api/ontap_rest_test.go +++ b/storage_drivers/ontap/api/ontap_rest_test.go @@ -4591,6 +4591,8 @@ func TestGetAllVolumesByPatternStyleAndState_failure(t *testing.T) { {"InvalidStyle", "InvalidState", mockGetVolumeResponse, true}, {models.VolumeStyleFlexvol, "InvalidState", mockGetVolumeResponse, true}, {models.VolumeStyleFlexvol, models.VolumeStateOnline, mockGetVolumeResponseNumRecordsNil, false}, + {models.VolumeStyleFlexvol, "", mockGetVolumeResponse, false}, + {models.VolumeStyleFlexvol, "", mockGetVolumeResponseNumRecordsNil, false}, } for _, test := range tests { From a12483d8a86d7fe6067c398f84d8b4247dddb218 Mon Sep 17 00:00:00 2001 From: jharrod Date: Wed, 7 Jan 2026 14:09:45 -0700 Subject: [PATCH 20/30] 25.10 Address volume creations failures caused by stale transactions from Trident restarts Co-authored-by: Joe Webster <31218426+jwebster7@users.noreply.github.com> --- cli/k8s_client/k8s_client.go | 2 +- core/orchestrator_core.go | 8 ++++++++ frontend/crd/crd_controller_test.go | 6 +++--- .../crd/apis/netapp/v1/transaction.go | 3 +-- .../crd/apis/netapp/v1/transaction_test.go | 17 ++++++++--------- persistent_store/crdv1.go | 2 +- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/cli/k8s_client/k8s_client.go b/cli/k8s_client/k8s_client.go index d798ddf74..208d4ed57 100644 --- a/cli/k8s_client/k8s_client.go +++ b/cli/k8s_client/k8s_client.go @@ -1,4 +1,4 @@ -// Copyright 2023 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package k8sclient diff --git a/core/orchestrator_core.go b/core/orchestrator_core.go index 219ea8ac5..3050930c8 100644 --- a/core/orchestrator_core.go +++ b/core/orchestrator_core.go @@ -22,6 +22,7 @@ import ( "github.com/netapp/trident/core/metrics" "github.com/netapp/trident/frontend" controllerhelpers "github.com/netapp/trident/frontend/csi/controller_helpers" + "github.com/netapp/trident/internal/fiji" . "github.com/netapp/trident/logging" persistentstore "github.com/netapp/trident/persistent_store" "github.com/netapp/trident/pkg/capacity" @@ -42,6 +43,8 @@ import ( "github.com/netapp/trident/utils/nvme" ) +var addVolumeAfterAddVolumeTxn = fiji.Register("addVolumeAfterAddVolumeTransaction", "orchestrator_core") + type TridentOrchestrator struct { backends map[string]storage.Backend // key is UUID, not name volumes map[string]*storage.Volume @@ -2005,6 +2008,11 @@ func (o *TridentOrchestrator) addVolumeInitial( return nil, err } + // addVolumeAfterAddVolumeTxn allows fault injection for automated testing. + if err := addVolumeAfterAddVolumeTxn.Inject(); err != nil { + return nil, err + } + // Copy the volume config into a working copy should any backend mutate the config but fail to create the volume. mutableConfig := volumeConfig.ConstructClone() diff --git a/frontend/crd/crd_controller_test.go b/frontend/crd/crd_controller_test.go index 739d886fd..57c1cd34e 100644 --- a/frontend/crd/crd_controller_test.go +++ b/frontend/crd/crd_controller_test.go @@ -1056,9 +1056,9 @@ func TestCrdControllerTransactionFinalizerRemoval(t *testing.T) { "txn": savedTxn, }).Debug("Got transaction.") - // Ensure the CRD was saved with a Trident finalizer - if !savedTxn.HasTridentFinalizers() { - t.Fatalf("expected transaction CRD to have Trident finalizer") + // Ensure the CRD was saved with no Trident finalizer + if savedTxn.HasTridentFinalizers() { + t.Fatalf("expected transaction CRD to not have Trident finalizer") } Logc(ctx()).Debug("Deleting transaction.") diff --git a/persistent_store/crd/apis/netapp/v1/transaction.go b/persistent_store/crd/apis/netapp/v1/transaction.go index 43b22a9f0..53eb01808 100644 --- a/persistent_store/crd/apis/netapp/v1/transaction.go +++ b/persistent_store/crd/apis/netapp/v1/transaction.go @@ -19,8 +19,7 @@ func NewTridentTransaction(txn *storage.VolumeTransaction) (*TridentTransaction, Kind: "TridentTransaction", }, ObjectMeta: metav1.ObjectMeta{ - Name: NameFix(txn.Name()), - Finalizers: GetTridentFinalizers(), + Name: NameFix(txn.Name()), }, } diff --git a/persistent_store/crd/apis/netapp/v1/transaction_test.go b/persistent_store/crd/apis/netapp/v1/transaction_test.go index 0e7b92efb..c06d16f81 100644 --- a/persistent_store/crd/apis/netapp/v1/transaction_test.go +++ b/persistent_store/crd/apis/netapp/v1/transaction_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 NetApp, Inc. All Rights Reserved. +// Copyright 2025 NetApp, Inc. All Rights Reserved. package v1 @@ -7,6 +7,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -41,8 +42,7 @@ func TestNewTransaction(t *testing.T) { Kind: "TridentTransaction", }, ObjectMeta: metav1.ObjectMeta{ - Name: NameFix(volConfig.Name), - Finalizers: GetTridentFinalizers(), + Name: NameFix(volConfig.Name), }, Transaction: runtime.RawExtension{ Raw: MustEncode(json.Marshal(txn)), @@ -53,6 +53,7 @@ func TestNewTransaction(t *testing.T) { if !reflect.DeepEqual(volumeTransaction, expected) { t.Fatalf("TridentTransaction does not match expected result, got %v expected %v", volumeTransaction, expected) } + assert.Empty(t, volumeTransaction.Finalizers) // Transactions should never have finalizers. } func TestNewSnapshotTransaction(t *testing.T) { @@ -88,8 +89,7 @@ func TestNewSnapshotTransaction(t *testing.T) { Kind: "TridentTransaction", }, ObjectMeta: metav1.ObjectMeta{ - Name: NameFix(volConfig.Name), - Finalizers: GetTridentFinalizers(), + Name: NameFix(volConfig.Name), }, Transaction: runtime.RawExtension{ Raw: MustEncode(json.Marshal(txn)), @@ -100,6 +100,7 @@ func TestNewSnapshotTransaction(t *testing.T) { if !reflect.DeepEqual(volumeTransaction, expected) { t.Fatalf("TridentTransaction does not match expected result, got %v expected %v", volumeTransaction, expected) } + assert.Empty(t, volumeTransaction.Finalizers) // Transactions should never have finalizers. } func TestTransaction_Persistent(t *testing.T) { @@ -123,8 +124,7 @@ func TestTransaction_Persistent(t *testing.T) { Kind: "TridentTransaction", }, ObjectMeta: metav1.ObjectMeta{ - Name: NameFix(volConfig.Name), - Finalizers: GetTridentFinalizers(), + Name: NameFix(volConfig.Name), }, Transaction: runtime.RawExtension{ Raw: MustEncode(json.Marshal(txn)), @@ -180,8 +180,7 @@ func TestSnapshotTransaction_Persistent(t *testing.T) { Kind: "TridentTransaction", }, ObjectMeta: metav1.ObjectMeta{ - Name: NameFix(volConfig.Name), - Finalizers: GetTridentFinalizers(), + Name: NameFix(volConfig.Name), }, Transaction: runtime.RawExtension{ Raw: MustEncode(json.Marshal(txn)), diff --git a/persistent_store/crdv1.go b/persistent_store/crdv1.go index 13166810b..8a280c438 100644 --- a/persistent_store/crdv1.go +++ b/persistent_store/crdv1.go @@ -1163,7 +1163,7 @@ func (k *CRDClientV1) GetVolumeTransaction( } func (k *CRDClientV1) DeleteVolumeTransaction(ctx context.Context, volTxn *storage.VolumeTransaction) error { - ctx = context.WithoutCancel(ctx) // Transactions should not be cancelled + ctx = context.WithoutCancel(ctx) // Transactions should not be canceled err := k.crdClient.TridentV1().TridentTransactions(k.namespace).Delete(ctx, v1.NameFix(volTxn.Name()), k.deleteOpts()) From 3860b6322cac2cb22ed0b111ef0fbfa1f72b6d4e Mon Sep 17 00:00:00 2001 From: VinayKumarHavanur <54576364+VinayKumarHavanur@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:09:44 +0530 Subject: [PATCH 21/30] Fix the issue with wrong FsType when the volume is created after retry --- .../mock_ontap/mock_api.go | 8 +- storage_drivers/ontap/api/abstraction.go | 2 +- storage_drivers/ontap/api/abstraction_rest.go | 8 +- .../ontap/api/abstraction_rest_test.go | 7 +- storage_drivers/ontap/api/abstraction_zapi.go | 9 +- .../ontap/api/abstraction_zapi_test.go | 9 +- storage_drivers/ontap/ontap_asa.go | 4 +- storage_drivers/ontap/ontap_asa_test.go | 9 +- storage_drivers/ontap/ontap_san.go | 73 +++++++++----- storage_drivers/ontap/ontap_san_economy.go | 2 +- .../ontap/ontap_san_economy_test.go | 20 ++-- storage_drivers/ontap/ontap_san_test.go | 96 ++++++++++++++++--- 12 files changed, 179 insertions(+), 68 deletions(-) diff --git a/mocks/mock_storage_drivers/mock_ontap/mock_api.go b/mocks/mock_storage_drivers/mock_ontap/mock_api.go index 703c8ff05..609aebf29 100644 --- a/mocks/mock_storage_drivers/mock_ontap/mock_api.go +++ b/mocks/mock_storage_drivers/mock_ontap/mock_api.go @@ -985,17 +985,17 @@ func (mr *MockOntapAPIMockRecorder) LunRename(ctx, lunPath, newLunPath any) *gom } // LunSetAttribute mocks base method. -func (m *MockOntapAPI) LunSetAttribute(ctx context.Context, lunPath, attribute, fstype, arg4, luks, formatOptions string) error { +func (m *MockOntapAPI) LunSetAttribute(ctx context.Context, lunPath, attribute, fstype, arg4, luks, formatOptions, poolName string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LunSetAttribute", ctx, lunPath, attribute, fstype, arg4, luks, formatOptions) + ret := m.ctrl.Call(m, "LunSetAttribute", ctx, lunPath, attribute, fstype, arg4, luks, formatOptions, poolName) ret0, _ := ret[0].(error) return ret0 } // LunSetAttribute indicates an expected call of LunSetAttribute. -func (mr *MockOntapAPIMockRecorder) LunSetAttribute(ctx, lunPath, attribute, fstype, arg4, luks, formatOptions any) *gomock.Call { +func (mr *MockOntapAPIMockRecorder) LunSetAttribute(ctx, lunPath, attribute, fstype, arg4, luks, formatOptions, poolName any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LunSetAttribute", reflect.TypeOf((*MockOntapAPI)(nil).LunSetAttribute), ctx, lunPath, attribute, fstype, arg4, luks, formatOptions) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LunSetAttribute", reflect.TypeOf((*MockOntapAPI)(nil).LunSetAttribute), ctx, lunPath, attribute, fstype, arg4, luks, formatOptions, poolName) } // LunSetComment mocks base method. diff --git a/storage_drivers/ontap/api/abstraction.go b/storage_drivers/ontap/api/abstraction.go index 6a6295042..9a8a28b65 100644 --- a/storage_drivers/ontap/api/abstraction.go +++ b/storage_drivers/ontap/api/abstraction.go @@ -92,7 +92,7 @@ type OntapAPI interface { LunDestroy(ctx context.Context, lunPath string) error LunGetFSType(ctx context.Context, lunPath string) (string, error) LunGetAttribute(ctx context.Context, lunPath, attributeName string) (string, error) - LunSetAttribute(ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string) error + LunSetAttribute(ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string) error LunSetComment(ctx context.Context, lunPath, comment string) error LunSetQosPolicyGroup(ctx context.Context, lunPath string, qosPolicyGroup QosPolicyGroup) error LunGetByName(ctx context.Context, name string) (*Lun, error) diff --git a/storage_drivers/ontap/api/abstraction_rest.go b/storage_drivers/ontap/api/abstraction_rest.go index 8e03e48a5..958ac57bd 100644 --- a/storage_drivers/ontap/api/abstraction_rest.go +++ b/storage_drivers/ontap/api/abstraction_rest.go @@ -2224,7 +2224,7 @@ func (d OntapAPIREST) LunDestroy(ctx context.Context, lunPath string) error { } func (d OntapAPIREST) LunSetAttribute( - ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string, + ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string, ) error { if strings.Contains(lunPath, failureLUNSetAttr) { return errors.New("injected error") @@ -2256,6 +2256,12 @@ func (d OntapAPIREST) LunSetAttribute( } } + // Save the pool name attribute at the end. Set new attribute as needed before this. + if err := d.api.LunSetAttribute(ctx, lunPath, "poolName", poolName); err != nil { + Logc(ctx).WithField("LUN", lunPath).Warning("Failed to save the pool name attribute for new LUN.") + return fmt.Errorf("failed to save the pool name attribute for new LUN: %w", err) + } + return nil } diff --git a/storage_drivers/ontap/api/abstraction_rest_test.go b/storage_drivers/ontap/api/abstraction_rest_test.go index e10e7f8fb..e1e232512 100644 --- a/storage_drivers/ontap/api/abstraction_rest_test.go +++ b/storage_drivers/ontap/api/abstraction_rest_test.go @@ -4021,18 +4021,19 @@ func TestLunSetAttribute(t *testing.T) { // case 1: Positive test, update LUN attribute. context is empty. rsi.EXPECT().LunSetAttribute(ctx, "/", "filesystem", "fake-FStype").Return(nil) - err := oapi.LunSetAttribute(ctx, "/", "filesystem", "fake-FStype", "", "", "") + rsi.EXPECT().LunSetAttribute(ctx, "/", "poolName", "").Return(nil) + err := oapi.LunSetAttribute(ctx, "/", "filesystem", "fake-FStype", "", "", "", "") assert.NoError(t, err, "error returned while modifying a LUN attribute") // case 2: Positive test, update LUN attribute. pass the value in context.. rsi.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() err = oapi.LunSetAttribute(ctx, "/", "filesystem", "fake-FStype", - "context", "LUKS", "formatOptions") + "context", "LUKS", "formatOptions", "poolName") assert.NoError(t, err, "error returned while modifying a LUN attribute") // case 3 Negative test, update LUN attribute returned error.. err = oapi.LunSetAttribute(ctx, "failure_7c3a89e2_7d83_457b_9e29_bfdb082c1d8b", - "filesystem", "fake-FStype", "context", "LUKS", "formatOptions") + "filesystem", "fake-FStype", "context", "LUKS", "formatOptions", "poolName") assert.Error(t, err, "no error returned while modifying a LUN attribute") } diff --git a/storage_drivers/ontap/api/abstraction_zapi.go b/storage_drivers/ontap/api/abstraction_zapi.go index 0307c1e35..6c160506d 100644 --- a/storage_drivers/ontap/api/abstraction_zapi.go +++ b/storage_drivers/ontap/api/abstraction_zapi.go @@ -571,7 +571,7 @@ func (d OntapAPIZAPI) LunGetAttribute(ctx context.Context, lunPath, attributeNam } func (d OntapAPIZAPI) LunSetAttribute( - ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string, + ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string, ) error { var attrResponse interface{} var err error @@ -607,6 +607,13 @@ func (d OntapAPIZAPI) LunSetAttribute( } } + // Save the pool name attribute at the end. Set new attribute as needed before this. + attrResponse, err = d.api.LunSetAttribute(lunPath, "poolName", poolName) + if err = azgo.GetError(ctx, attrResponse, err); err != nil { + Logc(ctx).WithField("LUN", lunPath).Warning("Failed to save the pool name attribute for new LUN.") + return fmt.Errorf("failed to save the pool name attribute for new LUN: %w", err) + } + return nil } diff --git a/storage_drivers/ontap/api/abstraction_zapi_test.go b/storage_drivers/ontap/api/abstraction_zapi_test.go index e9d24d245..1ca4a9452 100644 --- a/storage_drivers/ontap/api/abstraction_zapi_test.go +++ b/storage_drivers/ontap/api/abstraction_zapi_test.go @@ -132,18 +132,19 @@ func TestLunSetAttributeZapi(t *testing.T) { // case 1a: Positive test, update LUN attribute - fsType. zapi.EXPECT().LunSetAttribute(tempLunPath, tempAttribute, "fake-FStype").Return(&response, nil).Times(1) - err := oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "") + zapi.EXPECT().LunSetAttribute(tempLunPath, "poolName", "").Return(&response, nil).Times(1) + err := oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "", "") assert.NoError(t, err, "error returned while modifying a LUN attribute") // case 1b: Negative test, d.api.LunSetAttribute for fsType return error zapi.EXPECT().LunSetAttribute(tempLunPath, tempAttribute, "fake-FStype").Return(nil, errors.New("error")).Times(1) - err = oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "") + err = oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "", "") assert.Error(t, err) - // case 2: Positive test, update LUN attributes those are: context, luks, formatOptions. + // case 2: Positive test, update LUN attributes those are: context, luks, formatOptions, poolName. zapi.EXPECT().LunSetAttribute(tempLunPath, gomock.Any(), gomock.Any()).Return(&response, nil).AnyTimes() err = oapi.LunSetAttribute(ctx, tempLunPath, "filesystem", "", - "context", "LUKS", "formatOptions") + "context", "LUKS", "formatOptions", "poolName") assert.NoError(t, err, "error returned while modifying a LUN attribute") } diff --git a/storage_drivers/ontap/ontap_asa.go b/storage_drivers/ontap/ontap_asa.go index 485149ce8..98f7cb740 100644 --- a/storage_drivers/ontap/ontap_asa.go +++ b/storage_drivers/ontap/ontap_asa.go @@ -420,9 +420,9 @@ func (d *ASAStorageDriver) Create( // Save the fstype in a LUN attribute so we know what to do in Attach. If this fails, clean up and // move on to the next pool. - // Save the context, fstype, and LUKS value in LUN comment + // Save the context, fstype, LUKS value, and pool name in LUN comment err = d.API.LunSetAttribute(ctx, name, LUNAttributeFSType, fstype, string(d.Config.DriverContext), - luksEncryption, formatOptions) + luksEncryption, formatOptions, storagePool.Name()) if err != nil { errMessage := fmt.Sprintf("error saving file system type for LUN %s: %v", name, err) diff --git a/storage_drivers/ontap/ontap_asa_test.go b/storage_drivers/ontap/ontap_asa_test.go index c5e491973..e5dd96e7b 100644 --- a/storage_drivers/ontap/ontap_asa_test.go +++ b/storage_drivers/ontap/ontap_asa_test.go @@ -737,7 +737,7 @@ func TestCreateASA(t *testing.T) { mockAPI.EXPECT().TieringPolicyValue(ctx).Return("fake").Times(1) mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil).Times(1) mockAPI.EXPECT().LunSetComment(ctx, volumeName, labels).Return(nil).Times(1) - mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions]).Return(nil).Times(1) + mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions], storagePool.Name()).Return(nil).Times(1) }, verify: func(t *testing.T, err error) { assert.NoError(t, err, "Should not be an error") @@ -763,7 +763,7 @@ func TestCreateASA(t *testing.T) { mockAPI.EXPECT().TieringPolicyValue(ctx).Return("fake").Times(1) mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil).Times(1) mockAPI.EXPECT().LunSetComment(ctx, volumeName, labels).Return(nil).Times(1) - mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions]).Return(nil).Times(1) + mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions], storagePool.Name()).Return(nil).Times(1) }, verify: func(t *testing.T, err error) { assert.NoError(t, err, "Should not be an error") @@ -782,7 +782,7 @@ func TestCreateASA(t *testing.T) { assert.Equal(t, expectedLabels, labels, "Labels should match the expected value") return nil }).Times(1) - mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions]).Return(nil).Times(1) + mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions], storagePool.Name()).Return(nil).Times(1) }, verify: func(t *testing.T, err error) { assert.NoError(t, err, "Should not be an error") @@ -857,7 +857,8 @@ func TestCreateASA(t *testing.T) { storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], - storagePool.InternalAttributes()[FormatOptions]). + storagePool.InternalAttributes()[FormatOptions], + storagePool.Name()). Return(errors.New("api-error")).Times(1) mockAPI.EXPECT().LunDestroy(ctx, volumeName).Return(nil).Times(1) }, diff --git a/storage_drivers/ontap/ontap_san.go b/storage_drivers/ontap/ontap_san.go index 335779fa1..93a6beb69 100644 --- a/storage_drivers/ontap/ontap_san.go +++ b/storage_drivers/ontap/ontap_san.go @@ -260,8 +260,11 @@ func (d *SANStorageDriver) validate(ctx context.Context) error { return nil } -// destroyVolumeIfNoLUN attempts to destroy volume if there exists a volume with no associated LUN. -// This is used to make Create() idempotent by cleaning up a Flexvol with no LUN. +// cleanupIncompleteLUN attempts to destroy volume if there exists a volume with no associated LUN. +// This is used to make Create() idempotent by cleaning up a Flexvol with no LUN or +// the LUN exists but is associated with a different pool as it was not cleaned up properly and creation is retried +// with different pool. +// This can happen if volume creation succeeded but LUN creation failed in a previous Create() call. // Returns (Volume State, error) // // Caller can check for: @@ -271,15 +274,18 @@ func (d *SANStorageDriver) validate(ctx context.Context) error { // - Could not destroy the required volume for an error. // Volume state:true indicating both volume and required LUN exist. // Volume state:false indicating no volume existed or cleaned up now. -func (d *SANStorageDriver) destroyVolumeIfNoLUN(ctx context.Context, volConfig *storage.VolumeConfig) (bool, error) { +func (d *SANStorageDriver) cleanupIncompleteLUN( + ctx context.Context, volConfig *storage.VolumeConfig, poolName string, +) (bool, error) { name := volConfig.InternalName fields := LogFields{ - "Method": "destroyVolumeIfNoLUN", - "Type": "SANStorageDriver", - "name": name, + "Method": "cleanupIncompleteLUN", + "Type": "SANStorageDriver", + "name": name, + "poolName": poolName, } - Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> destroyVolumeIfNoLUN") - defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< destroyVolumeIfNoLUN") + Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> cleanupIncompleteLUN") + defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< cleanupIncompleteLUN") volExists, err := d.API.VolumeExists(ctx, name) if err != nil { @@ -297,20 +303,44 @@ func (d *SANStorageDriver) destroyVolumeIfNoLUN(ctx context.Context, volConfig * // Verify if LUN exists. newLUNPath := lunPath(name) extantLUN, err := d.API.LunGetByName(ctx, newLUNPath) - if extantLUN != nil { - // Volume and LUN both exist. No clean up needed. - return true, nil - } - if !errors.IsNotFoundError(err) { + if err != nil && !errors.IsNotFoundError(err) { // Could not verify if LUN exists. Clean up pending. return false, fmt.Errorf("error checking for existing LUN %s: %v", newLUNPath, err) } - // LUN does not exist, but volume. Initiate clean-up. - if err = d.API.VolumeDestroy(ctx, name, true, true); err != nil { - Logc(ctx).WithField("volume", name).Errorf("Could not clean up volume: %v", err) - return true, fmt.Errorf("could not clean up partial create of vol/lun: %v", err) + + var destroyReason string + var destroyErrorMsg string + + if extantLUN != nil { + // Both the volume and LUN exist. Check if the last attribute set on the LUN, i.e., the pool name, matches. + lunPoolName, err := d.API.LunGetAttribute(ctx, newLUNPath, "poolName") + if err != nil || lunPoolName != poolName { + // If there is an error getting pool name or pool name doesn't match + Logc(ctx).WithFields(LogFields{ + "LUN": newLUNPath, + "lunPoolName": lunPoolName, + "inputPoolName": poolName, + "error": err, + }).Info("Pool name not found or mismatch detected. Destroying volume.") + + destroyReason = "Destroyed volume with mismatched pool name." + destroyErrorMsg = "could not destroy volume with mismatched pool" + } else { + // Pool name matches or no pool name attribute. No clean up needed. + return true, nil + } + } else { + // LUN does not exist, but volume does. Initiate clean-up. + destroyReason = "Cleaned up volume since LUN create failed." + destroyErrorMsg = "could not clean up partial create of vol/lun" + } + + if err := d.API.VolumeDestroy(ctx, name, true, true); err != nil { + Logc(ctx).WithError(err).WithField("volume", name).Errorf("Could not clean up volume") + return true, fmt.Errorf("%s: %v", destroyErrorMsg, err) } - Logc(ctx).WithField("volume", name).Debug("Cleaned up volume since LUN create failed.") + Logc(ctx).WithField("volume", name).Debug(destroyReason) + return false, nil } @@ -332,7 +362,7 @@ func (d *SANStorageDriver) Create( defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Create") // Early exit if volume+LUN exist. Clean up volume if no LUN exists. - volExists, err := d.destroyVolumeIfNoLUN(ctx, volConfig) + volExists, err := d.cleanupIncompleteLUN(ctx, volConfig, storagePool.Name()) if err != nil { return fmt.Errorf("failure checking for existence of volume and cleaning if any: %v", err) } @@ -574,11 +604,10 @@ func (d *SANStorageDriver) Create( // Save the fstype in a LUN attribute so we know what to do in Attach. If this fails, clean up and // move on to the next pool. - // Save the context, fstype, and LUKS value in LUN comment + // Save the context, fstype, LUKS value, and pool name in LUN comment err = d.API.LunSetAttribute(ctx, lunPath, LUNAttributeFSType, fstype, string(d.Config.DriverContext), - luksEncryption, formatOptions) + luksEncryption, formatOptions, storagePool.Name()) if err != nil { - errMessage := fmt.Sprintf("ONTAP-SAN pool %s/%s; error saving file system type for LUN %s: %v", storagePool.Name(), aggregate, name, err) Logc(ctx).Error(errMessage) diff --git a/storage_drivers/ontap/ontap_san_economy.go b/storage_drivers/ontap/ontap_san_economy.go index c90622edb..2ac3a08fa 100644 --- a/storage_drivers/ontap/ontap_san_economy.go +++ b/storage_drivers/ontap/ontap_san_economy.go @@ -709,7 +709,7 @@ func (d *SANEconomyStorageDriver) Create( // Save the fstype in a LUN attribute so we know what to do in Attach err = d.API.LunSetAttribute(ctx, lunPathEco, LUNAttributeFSType, fstype, string(d.Config.DriverContext), - luksEncryption, formatOptions) + luksEncryption, formatOptions, storagePool.Name()) if err != nil { errMessage := fmt.Sprintf( diff --git a/storage_drivers/ontap/ontap_san_economy_test.go b/storage_drivers/ontap/ontap_san_economy_test.go index d93aa3063..d43e0813f 100644 --- a/storage_drivers/ontap/ontap_san_economy_test.go +++ b/storage_drivers/ontap/ontap_san_economy_test.go @@ -639,7 +639,7 @@ func TestOntapSanEconomyVolumeCreate(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&api.Lun{Size: "1073741824"}, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) result := d.Create(ctx, volConfig, pool1, volAttrs) @@ -996,7 +996,7 @@ func TestOntapSanEconomyVolumeCreate_OverPoolSizeLimit_CreateNewFlexvol(t *testi mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&api.Lun{Size: "1073741824"}, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) result := d.Create(ctx, volConfig, pool1, volAttrs) @@ -1065,7 +1065,7 @@ func TestOntapSanEconomyVolumeCreate_NotOverPoolSizeLimit_UseExistingFlexvol(t * mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&api.Lun{Size: "1073741824"}, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) result := d.Create(ctx, volConfig, pool1, volAttrs) @@ -1331,7 +1331,7 @@ func TestOntapSanEconomyVolumeCreate_TooManyLUNs(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&api.Lun{Size: "1073741824"}, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) result := d.Create(ctx, volConfig, pool1, volAttrs) @@ -1393,7 +1393,7 @@ func TestOntapSanEconomyVolumeCreate_LUNSetAttributeFailed(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&api.Lun{Size: "1073741824"}, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(errors.New("failed to set attribute")) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(errors.New("failed to set attribute")) switch test.errorType { case "Lun": @@ -1440,7 +1440,7 @@ func TestOntapSanEconomyVolumeCreate_Resize(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&lun, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().VolumeSize(ctx, gomock.Any()).Return(uint64(1073741824), nil).Times(2) result := d.Create(ctx, volConfig, pool1, volAttrs) @@ -1474,7 +1474,7 @@ func TestOntapSanEconomyVolumeCreate_ResizeVolumeSizeFailed(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&lun, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().VolumeSize(ctx, gomock.Any()).Return(uint64(1073741824), errors.New("failed to set size")) result := d.Create(ctx, volConfig, pool1, volAttrs) @@ -1508,7 +1508,7 @@ func TestOntapSanEconomyVolumeCreate_ResizeSetSizeFailed(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&lun, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().VolumeSize(ctx, gomock.Any()).Return(uint64(1073741824), nil) mockAPI.EXPECT().VolumeSetSize(ctx, gomock.Any(), gomock.Any()).Return(errors.New("failed to set volume size")) @@ -1543,7 +1543,7 @@ func TestOntapSanEconomyVolumeCreate_ResizeVolumeSizeFailed2(t *testing.T) { mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Times(1).Return(&lun, nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Times(1).Return(nil) + gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil) mockAPI.EXPECT().VolumeSize(ctx, gomock.Any()).Return(uint64(1073741824), nil) mockAPI.EXPECT().VolumeSetSize(ctx, gomock.Any(), gomock.Any()).Return(nil) mockAPI.EXPECT().VolumeSize(ctx, gomock.Any()).Return(uint64(1073741824), errors.New("failed to get volume size")) @@ -1592,7 +1592,7 @@ func TestOntapSanEconomyVolumeCreate_FormatOptions(t *testing.T) { // This is the assertion of this unit test, // checking whether the argument FormatOptions matches with what we pass in the internal attributes. mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), tempFormatOptions).Times(1).Return(nil) + gomock.Any(), tempFormatOptions, gomock.Any()).Times(1).Return(nil) result := d.Create(ctx, volConfig, pool1, volAttrs) diff --git a/storage_drivers/ontap/ontap_san_test.go b/storage_drivers/ontap/ontap_san_test.go index 629a667f0..d007fed8e 100644 --- a/storage_drivers/ontap/ontap_san_test.go +++ b/storage_drivers/ontap/ontap_san_test.go @@ -332,8 +332,8 @@ func expectLunAndVolumeCreateSequence(ctx context.Context, mockAPI *mockapi.Mock }, ).MaxTimes(1) - mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), fsType, gomock.Any(), luks, gomock.Any()).DoAndReturn( - func(ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string) error { + mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), fsType, gomock.Any(), luks, gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string) error { return nil }, ).MaxTimes(1) @@ -876,7 +876,7 @@ func TestOntapSanVolumeCreate_LabelLengthExceeding(t *testing.T) { assert.Error(t, err, "Error is nil") } -func TestOntapSanVolume_DestroyVolumeIfNoLUN(t *testing.T) { +func TestOntapSanVolume_CleanupIncompleteLUN(t *testing.T) { ctx = context.Background() mockAPI, driver := newMockOntapSANDriver(t) volConfig := getVolumeConfig() @@ -913,24 +913,84 @@ func TestOntapSanVolume_DestroyVolumeIfNoLUN(t *testing.T) { assertMessage: "Volume existed.", }, { - name: "LUNExists", + name: "LUNExists_EmptyPoolName", mocks: func(mockAPI *mockapi.MockOntapAPI) { mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil) + mockAPI.EXPECT().LunGetAttribute(ctx, gomock.Any(), "poolName").Return("", nil) + mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), true, true).Return(nil) + }, + wantErr: assert.NoError, + volExists: false, + assertMessage: "Volume should be destroyed when pool name is empty", + }, + { + name: "LUNExists_PoolNameMatches", + mocks: func(mockAPI *mockapi.MockOntapAPI) { + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) + mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil) + mockAPI.EXPECT().LunGetAttribute(ctx, gomock.Any(), "poolName").Return("testPool", nil) }, wantErr: assert.NoError, volExists: true, - assertMessage: "LUN does not exist", + assertMessage: "LUN should exist with matching pool name", + }, + { + name: "LUNExists_PoolNameMismatch", + mocks: func(mockAPI *mockapi.MockOntapAPI) { + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) + mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil) + mockAPI.EXPECT().LunGetAttribute(ctx, gomock.Any(), "poolName").Return("differentPool", nil) + mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), true, true).Return(nil) + }, + wantErr: assert.NoError, + volExists: false, + assertMessage: "Volume should be destroyed due to pool name mismatch", + }, + { + name: "LUNExists_PoolNameMismatch_VolumeDestroyFails", + mocks: func(mockAPI *mockapi.MockOntapAPI) { + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) + mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil) + mockAPI.EXPECT().LunGetAttribute(ctx, gomock.Any(), "poolName").Return("differentPool", nil) + mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), true, true).Return(errors.New("volume destroy failed")) + }, + wantErr: assert.Error, + volExists: true, + assertMessage: "Should error when volume destroy fails after pool name mismatch", + }, + { + name: "LUNExists_GetPoolNameAttributeFails", + mocks: func(mockAPI *mockapi.MockOntapAPI) { + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) + mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil) + mockAPI.EXPECT().LunGetAttribute(ctx, gomock.Any(), "poolName").Return("", errors.New("failed to get attribute")) + mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), true, true).Return(nil) + }, + wantErr: assert.NoError, + volExists: false, + assertMessage: "Volume should be destroyed when pool name attribute retrieval fails", }, { name: "LUNFindError", mocks: func(mockAPI *mockapi.MockOntapAPI) { mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(nil, nil) + mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), true, true).Return(nil) + }, + wantErr: assert.NoError, + volExists: false, + assertMessage: "Volume should be destroyed when LUN is not found (ambiguous state).", + }, + { + name: "LUNGetByName_ReturnsNonNotFoundError", + mocks: func(mockAPI *mockapi.MockOntapAPI) { + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) + mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(nil, errors.New("API connection error")) }, wantErr: assert.Error, volExists: false, - assertMessage: "LUN is found.", + assertMessage: "Should return error when LunGetByName returns a non-NotFoundError", }, { name: "LUNDoesNotExist", @@ -960,7 +1020,7 @@ func TestOntapSanVolume_DestroyVolumeIfNoLUN(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.mocks(mockAPI) - volExists, err := driver.destroyVolumeIfNoLUN(ctx, &volConfig) + volExists, err := driver.cleanupIncompleteLUN(ctx, &volConfig, "testPool") assert.Equal(t, test.volExists, volExists, "volume exist status is not expected.") if !test.wantErr(t, err, test.assertMessage) { return @@ -972,7 +1032,7 @@ func TestOntapSanVolume_DestroyVolumeIfNoLUN(t *testing.T) { volConfig.IsMirrorDestination = true mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) t.Run("mirrored configuration", func(t *testing.T) { - volExists, err := driver.destroyVolumeIfNoLUN(ctx, &volConfig) + volExists, err := driver.cleanupIncompleteLUN(ctx, &volConfig, "testPool") assert.True(t, volExists, "volume does not exist") assert.NoError(t, err, "volume exist check return error") }) @@ -1031,8 +1091,12 @@ func TestOntapSanVolumeCreate_ValidationFail(t *testing.T) { FileSystem: "xfs", }, mocks: func(mockAPI *mockapi.MockOntapAPI) { - mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil) - mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil) + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(true, nil).Times(1) + mockAPI.EXPECT().LunGetByName(ctx, gomock.Any()).Return(dummyLun, nil).Times(1) + mockAPI.EXPECT().LunGetAttribute(ctx, gomock.Any(), "poolName").Return("", nil).Times(1) + mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), true, true).Return(nil).Times(1) + mockAPI.EXPECT().TieringPolicyValue(ctx).Return("fake-tier-policy").Times(1) + mockAPI.EXPECT().GetSVMAggregateSpace(ctx, "pool1").Return(nil, errors.New("aggregate not found")).Times(1) }, wantErr: assert.Error, assertMessage: "Volume is not present in backend", @@ -1046,7 +1110,9 @@ func TestOntapSanVolumeCreate_ValidationFail(t *testing.T) { FileSystem: "xfs", }, mocks: func(mockAPI *mockapi.MockOntapAPI) { - mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(false, nil) + mockAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(false, nil).Times(1) + mockAPI.EXPECT().TieringPolicyValue(ctx).Return("fake-tier-policy").Times(1) + mockAPI.EXPECT().GetSVMAggregateSpace(ctx, "pool1").Return(nil, errors.New("aggregate not found")).Times(1) }, wantErr: assert.Error, assertMessage: "SnapshotReserve validation passed", @@ -1279,7 +1345,7 @@ func TestOntapSanVolumeCreate_VolumeCreateFail(t *testing.T) { mockAPI.EXPECT().VolumeCreate(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Return(errors.New("failed to set LUN attribute")) + gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("failed to set LUN attribute")) mockAPI.EXPECT().LunDestroy(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, @@ -1294,7 +1360,7 @@ func TestOntapSanVolumeCreate_VolumeCreateFail(t *testing.T) { mockAPI.EXPECT().VolumeCreate(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Return(errors.New("failed to set LUN attribute")) + gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("failed to set LUN attribute")) mockAPI.EXPECT().LunDestroy(ctx, gomock.Any()).Return(errors.New("LUN destroy failed")) mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, @@ -1309,7 +1375,7 @@ func TestOntapSanVolumeCreate_VolumeCreateFail(t *testing.T) { mockAPI.EXPECT().VolumeCreate(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any()).Return(errors.New("failed to set LUN attribute")) + gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("failed to set LUN attribute")) mockAPI.EXPECT().LunDestroy(ctx, gomock.Any()).Return(nil) mockAPI.EXPECT().VolumeDestroy(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("volume destroy failed")) @@ -1368,7 +1434,7 @@ func TestOntapSanVolumeCreate_FormatOptions(t *testing.T) { // This is the assertion of this unit test, // checking whether the argument FormatOptions matches with what we pass in the internal attributes. - mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), fsType, gomock.Any(), luks, tempFormatOptions).Return(nil).MaxTimes(1) + mockAPI.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), fsType, gomock.Any(), luks, tempFormatOptions, gomock.Any()).Return(nil).MaxTimes(1) volConfig := getVolumeConfig() volAttrs := map[string]sa.Request{} From cb9a80cd3913aa7f5cb331ddea4a5b35a636fa82 Mon Sep 17 00:00:00 2001 From: jharrod Date: Fri, 27 Feb 2026 14:34:26 -0700 Subject: [PATCH 22/30] Blkid fix 25.10 --- .../mock_devices/mock_devices_client.go | 219 +++++++++--------- .../mock_devices/mock_luks/mock_luks.go | 98 ++++---- utils/devices/luks/luks.go | 51 ++-- utils/devices/luks/luks_darwin.go | 4 +- utils/devices/luks/luks_linux.go | 19 +- utils/devices/luks/luks_linux_test.go | 57 +++-- utils/devices/luks/luks_windows.go | 4 +- utils/fcp/fcp.go | 16 +- utils/iscsi/iscsi.go | 15 +- utils/nvme/nvme.go | 15 +- 10 files changed, 276 insertions(+), 222 deletions(-) diff --git a/mocks/mock_utils/mock_devices/mock_devices_client.go b/mocks/mock_utils/mock_devices/mock_devices_client.go index 02ff99944..e66a1d205 100644 --- a/mocks/mock_utils/mock_devices/mock_devices_client.go +++ b/mocks/mock_utils/mock_devices/mock_devices_client.go @@ -10,18 +10,19 @@ package mock_devices import ( - context "context" reflect "reflect" time "time" models "github.com/netapp/trident/utils/models" gomock "go.uber.org/mock/gomock" + context "golang.org/x/net/context" ) // MockDevices is a mock of Devices interface. type MockDevices struct { ctrl *gomock.Controller recorder *MockDevicesMockRecorder + isgomock struct{} } // MockDevicesMockRecorder is the mock recorder for MockDevices. @@ -42,352 +43,352 @@ func (m *MockDevices) EXPECT() *MockDevicesMockRecorder { } // ClearFormatting mocks base method. -func (m *MockDevices) ClearFormatting(arg0 context.Context, arg1 string) error { +func (m *MockDevices) ClearFormatting(ctx context.Context, devicePath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ClearFormatting", arg0, arg1) + ret := m.ctrl.Call(m, "ClearFormatting", ctx, devicePath) ret0, _ := ret[0].(error) return ret0 } // ClearFormatting indicates an expected call of ClearFormatting. -func (mr *MockDevicesMockRecorder) ClearFormatting(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) ClearFormatting(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearFormatting", reflect.TypeOf((*MockDevices)(nil).ClearFormatting), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClearFormatting", reflect.TypeOf((*MockDevices)(nil).ClearFormatting), ctx, devicePath) } // CloseLUKSDevice mocks base method. -func (m *MockDevices) CloseLUKSDevice(arg0 context.Context, arg1 string) error { +func (m *MockDevices) CloseLUKSDevice(ctx context.Context, devicePath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CloseLUKSDevice", arg0, arg1) + ret := m.ctrl.Call(m, "CloseLUKSDevice", ctx, devicePath) ret0, _ := ret[0].(error) return ret0 } // CloseLUKSDevice indicates an expected call of CloseLUKSDevice. -func (mr *MockDevicesMockRecorder) CloseLUKSDevice(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) CloseLUKSDevice(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseLUKSDevice", reflect.TypeOf((*MockDevices)(nil).CloseLUKSDevice), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseLUKSDevice", reflect.TypeOf((*MockDevices)(nil).CloseLUKSDevice), ctx, devicePath) } // EnsureDeviceReadable mocks base method. -func (m *MockDevices) EnsureDeviceReadable(arg0 context.Context, arg1 string) error { +func (m *MockDevices) EnsureDeviceReadable(ctx context.Context, device string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureDeviceReadable", arg0, arg1) + ret := m.ctrl.Call(m, "EnsureDeviceReadable", ctx, device) ret0, _ := ret[0].(error) return ret0 } // EnsureDeviceReadable indicates an expected call of EnsureDeviceReadable. -func (mr *MockDevicesMockRecorder) EnsureDeviceReadable(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) EnsureDeviceReadable(ctx, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceReadable", reflect.TypeOf((*MockDevices)(nil).EnsureDeviceReadable), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceReadable", reflect.TypeOf((*MockDevices)(nil).EnsureDeviceReadable), ctx, device) } // EnsureLUKSDeviceClosed mocks base method. -func (m *MockDevices) EnsureLUKSDeviceClosed(arg0 context.Context, arg1 string) error { +func (m *MockDevices) EnsureLUKSDeviceClosed(ctx context.Context, devicePath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosed", arg0, arg1) + ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosed", ctx, devicePath) ret0, _ := ret[0].(error) return ret0 } // EnsureLUKSDeviceClosed indicates an expected call of EnsureLUKSDeviceClosed. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosed(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosed(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosed", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosed), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosed", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosed), ctx, devicePath) } // EnsureLUKSDeviceClosedWithMaxWaitLimit mocks base method. -func (m *MockDevices) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0 context.Context, arg1 string) error { +func (m *MockDevices) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosedWithMaxWaitLimit", arg0, arg1) + ret := m.ctrl.Call(m, "EnsureLUKSDeviceClosedWithMaxWaitLimit", ctx, luksDevicePath) ret0, _ := ret[0].(error) return ret0 } // EnsureLUKSDeviceClosedWithMaxWaitLimit indicates an expected call of EnsureLUKSDeviceClosedWithMaxWaitLimit. -func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksDevicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), ctx, luksDevicePath) } // FindDevicesForMultipathDevice mocks base method. -func (m *MockDevices) FindDevicesForMultipathDevice(arg0 context.Context, arg1 string) []string { +func (m *MockDevices) FindDevicesForMultipathDevice(ctx context.Context, device string) []string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindDevicesForMultipathDevice", arg0, arg1) + ret := m.ctrl.Call(m, "FindDevicesForMultipathDevice", ctx, device) ret0, _ := ret[0].([]string) return ret0 } // FindDevicesForMultipathDevice indicates an expected call of FindDevicesForMultipathDevice. -func (mr *MockDevicesMockRecorder) FindDevicesForMultipathDevice(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FindDevicesForMultipathDevice(ctx, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDevicesForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).FindDevicesForMultipathDevice), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDevicesForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).FindDevicesForMultipathDevice), ctx, device) } // FindMultipathDeviceForDevice mocks base method. -func (m *MockDevices) FindMultipathDeviceForDevice(arg0 context.Context, arg1 string) string { +func (m *MockDevices) FindMultipathDeviceForDevice(ctx context.Context, device string) string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindMultipathDeviceForDevice", arg0, arg1) + ret := m.ctrl.Call(m, "FindMultipathDeviceForDevice", ctx, device) ret0, _ := ret[0].(string) return ret0 } // FindMultipathDeviceForDevice indicates an expected call of FindMultipathDeviceForDevice. -func (mr *MockDevicesMockRecorder) FindMultipathDeviceForDevice(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FindMultipathDeviceForDevice(ctx, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipathDeviceForDevice", reflect.TypeOf((*MockDevices)(nil).FindMultipathDeviceForDevice), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindMultipathDeviceForDevice", reflect.TypeOf((*MockDevices)(nil).FindMultipathDeviceForDevice), ctx, device) } // FlushDevice mocks base method. -func (m *MockDevices) FlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo, arg2 bool) error { +func (m *MockDevices) FlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo, force bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlushDevice", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "FlushDevice", ctx, deviceInfo, force) ret0, _ := ret[0].(error) return ret0 } // FlushDevice indicates an expected call of FlushDevice. -func (mr *MockDevicesMockRecorder) FlushDevice(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FlushDevice(ctx, deviceInfo, force any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDevice", reflect.TypeOf((*MockDevices)(nil).FlushDevice), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDevice", reflect.TypeOf((*MockDevices)(nil).FlushDevice), ctx, deviceInfo, force) } // FlushOneDevice mocks base method. -func (m *MockDevices) FlushOneDevice(arg0 context.Context, arg1 string) error { +func (m *MockDevices) FlushOneDevice(ctx context.Context, devicePath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlushOneDevice", arg0, arg1) + ret := m.ctrl.Call(m, "FlushOneDevice", ctx, devicePath) ret0, _ := ret[0].(error) return ret0 } // FlushOneDevice indicates an expected call of FlushOneDevice. -func (mr *MockDevicesMockRecorder) FlushOneDevice(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) FlushOneDevice(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushOneDevice", reflect.TypeOf((*MockDevices)(nil).FlushOneDevice), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushOneDevice", reflect.TypeOf((*MockDevices)(nil).FlushOneDevice), ctx, devicePath) } // GetDeviceFSType mocks base method. -func (m *MockDevices) GetDeviceFSType(arg0 context.Context, arg1 string) (string, error) { +func (m *MockDevices) GetDeviceFSType(ctx context.Context, device string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeviceFSType", arg0, arg1) + ret := m.ctrl.Call(m, "GetDeviceFSType", ctx, device) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeviceFSType indicates an expected call of GetDeviceFSType. -func (mr *MockDevicesMockRecorder) GetDeviceFSType(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetDeviceFSType(ctx, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceFSType", reflect.TypeOf((*MockDevices)(nil).GetDeviceFSType), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeviceFSType", reflect.TypeOf((*MockDevices)(nil).GetDeviceFSType), ctx, device) } // GetDiskSize mocks base method. -func (m *MockDevices) GetDiskSize(arg0 context.Context, arg1 string) (int64, error) { +func (m *MockDevices) GetDiskSize(ctx context.Context, devicePath string) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDiskSize", arg0, arg1) + ret := m.ctrl.Call(m, "GetDiskSize", ctx, devicePath) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDiskSize indicates an expected call of GetDiskSize. -func (mr *MockDevicesMockRecorder) GetDiskSize(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetDiskSize(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskSize", reflect.TypeOf((*MockDevices)(nil).GetDiskSize), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskSize", reflect.TypeOf((*MockDevices)(nil).GetDiskSize), ctx, devicePath) } // GetLUKSDeviceForMultipathDevice mocks base method. -func (m *MockDevices) GetLUKSDeviceForMultipathDevice(arg0 string) (string, error) { +func (m *MockDevices) GetLUKSDeviceForMultipathDevice(multipathDevice string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLUKSDeviceForMultipathDevice", arg0) + ret := m.ctrl.Call(m, "GetLUKSDeviceForMultipathDevice", multipathDevice) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLUKSDeviceForMultipathDevice indicates an expected call of GetLUKSDeviceForMultipathDevice. -func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(arg0 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetLUKSDeviceForMultipathDevice(multipathDevice any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDeviceForMultipathDevice", reflect.TypeOf((*MockDevices)(nil).GetLUKSDeviceForMultipathDevice), multipathDevice) } // GetLUKSDevicePathForDevicePath mocks base method. -func (m *MockDevices) GetLUKSDevicePathForDevicePath(arg0 context.Context, arg1 string) (string, error) { +func (m *MockDevices) GetLUKSDevicePathForDevicePath(ctx context.Context, devicePath string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLUKSDevicePathForDevicePath", arg0, arg1) + ret := m.ctrl.Call(m, "GetLUKSDevicePathForDevicePath", ctx, devicePath) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLUKSDevicePathForDevicePath indicates an expected call of GetLUKSDevicePathForDevicePath. -func (mr *MockDevicesMockRecorder) GetLUKSDevicePathForDevicePath(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetLUKSDevicePathForDevicePath(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDevicePathForDevicePath", reflect.TypeOf((*MockDevices)(nil).GetLUKSDevicePathForDevicePath), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLUKSDevicePathForDevicePath", reflect.TypeOf((*MockDevices)(nil).GetLUKSDevicePathForDevicePath), ctx, devicePath) } // GetLunSerial mocks base method. -func (m *MockDevices) GetLunSerial(arg0 context.Context, arg1 string) (string, error) { +func (m *MockDevices) GetLunSerial(ctx context.Context, path string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLunSerial", arg0, arg1) + ret := m.ctrl.Call(m, "GetLunSerial", ctx, path) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLunSerial indicates an expected call of GetLunSerial. -func (mr *MockDevicesMockRecorder) GetLunSerial(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetLunSerial(ctx, path any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLunSerial", reflect.TypeOf((*MockDevices)(nil).GetLunSerial), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLunSerial", reflect.TypeOf((*MockDevices)(nil).GetLunSerial), ctx, path) } // GetMultipathDeviceBySerial mocks base method. -func (m *MockDevices) GetMultipathDeviceBySerial(arg0 context.Context, arg1 string) (string, error) { +func (m *MockDevices) GetMultipathDeviceBySerial(ctx context.Context, hexSerial string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceBySerial", arg0, arg1) + ret := m.ctrl.Call(m, "GetMultipathDeviceBySerial", ctx, hexSerial) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMultipathDeviceBySerial indicates an expected call of GetMultipathDeviceBySerial. -func (mr *MockDevicesMockRecorder) GetMultipathDeviceBySerial(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetMultipathDeviceBySerial(ctx, hexSerial any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceBySerial", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceBySerial), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceBySerial", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceBySerial), ctx, hexSerial) } // GetMultipathDeviceUUID mocks base method. -func (m *MockDevices) GetMultipathDeviceUUID(arg0 string) (string, error) { +func (m *MockDevices) GetMultipathDeviceUUID(multipathDevicePath string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", arg0) + ret := m.ctrl.Call(m, "GetMultipathDeviceUUID", multipathDevicePath) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMultipathDeviceUUID indicates an expected call of GetMultipathDeviceUUID. -func (mr *MockDevicesMockRecorder) GetMultipathDeviceUUID(arg0 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) GetMultipathDeviceUUID(multipathDevicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceUUID), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMultipathDeviceUUID", reflect.TypeOf((*MockDevices)(nil).GetMultipathDeviceUUID), multipathDevicePath) } // IsDeviceUnformatted mocks base method. -func (m *MockDevices) IsDeviceUnformatted(arg0 context.Context, arg1 string) (bool, error) { +func (m *MockDevices) IsDeviceUnformatted(ctx context.Context, device string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsDeviceUnformatted", arg0, arg1) + ret := m.ctrl.Call(m, "IsDeviceUnformatted", ctx, device) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // IsDeviceUnformatted indicates an expected call of IsDeviceUnformatted. -func (mr *MockDevicesMockRecorder) IsDeviceUnformatted(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) IsDeviceUnformatted(ctx, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDeviceUnformatted", reflect.TypeOf((*MockDevices)(nil).IsDeviceUnformatted), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDeviceUnformatted", reflect.TypeOf((*MockDevices)(nil).IsDeviceUnformatted), ctx, device) } // ListAllDevices mocks base method. -func (m *MockDevices) ListAllDevices(arg0 context.Context) { +func (m *MockDevices) ListAllDevices(ctx context.Context) { m.ctrl.T.Helper() - m.ctrl.Call(m, "ListAllDevices", arg0) + m.ctrl.Call(m, "ListAllDevices", ctx) } // ListAllDevices indicates an expected call of ListAllDevices. -func (mr *MockDevicesMockRecorder) ListAllDevices(arg0 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) ListAllDevices(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllDevices", reflect.TypeOf((*MockDevices)(nil).ListAllDevices), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAllDevices", reflect.TypeOf((*MockDevices)(nil).ListAllDevices), ctx) } // MultipathFlushDevice mocks base method. -func (m *MockDevices) MultipathFlushDevice(arg0 context.Context, arg1 *models.ScsiDeviceInfo) error { +func (m *MockDevices) MultipathFlushDevice(ctx context.Context, deviceInfo *models.ScsiDeviceInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MultipathFlushDevice", arg0, arg1) + ret := m.ctrl.Call(m, "MultipathFlushDevice", ctx, deviceInfo) ret0, _ := ret[0].(error) return ret0 } // MultipathFlushDevice indicates an expected call of MultipathFlushDevice. -func (mr *MockDevicesMockRecorder) MultipathFlushDevice(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) MultipathFlushDevice(ctx, deviceInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MultipathFlushDevice", reflect.TypeOf((*MockDevices)(nil).MultipathFlushDevice), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MultipathFlushDevice", reflect.TypeOf((*MockDevices)(nil).MultipathFlushDevice), ctx, deviceInfo) } // RemoveDevice mocks base method. -func (m *MockDevices) RemoveDevice(arg0 context.Context, arg1 []string, arg2 bool) error { +func (m *MockDevices) RemoveDevice(ctx context.Context, devices []string, ignoreErrors bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveDevice", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "RemoveDevice", ctx, devices, ignoreErrors) ret0, _ := ret[0].(error) return ret0 } // RemoveDevice indicates an expected call of RemoveDevice. -func (mr *MockDevicesMockRecorder) RemoveDevice(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) RemoveDevice(ctx, devices, ignoreErrors any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDevice", reflect.TypeOf((*MockDevices)(nil).RemoveDevice), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveDevice", reflect.TypeOf((*MockDevices)(nil).RemoveDevice), ctx, devices, ignoreErrors) } // RemoveMultipathDeviceMapping mocks base method. -func (m *MockDevices) RemoveMultipathDeviceMapping(arg0 context.Context, arg1 string) error { +func (m *MockDevices) RemoveMultipathDeviceMapping(ctx context.Context, devicePath string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveMultipathDeviceMapping", arg0, arg1) + ret := m.ctrl.Call(m, "RemoveMultipathDeviceMapping", ctx, devicePath) ret0, _ := ret[0].(error) return ret0 } // RemoveMultipathDeviceMapping indicates an expected call of RemoveMultipathDeviceMapping. -func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMapping(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMapping(ctx, devicePath any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMapping", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMapping), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMapping", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMapping), ctx, devicePath) } // RemoveMultipathDeviceMappingWithRetries mocks base method. -func (m *MockDevices) RemoveMultipathDeviceMappingWithRetries(arg0 context.Context, arg1 string, arg2 uint64, arg3 time.Duration) error { +func (m *MockDevices) RemoveMultipathDeviceMappingWithRetries(ctx context.Context, devicePath string, retries uint64, sleep time.Duration) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveMultipathDeviceMappingWithRetries", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "RemoveMultipathDeviceMappingWithRetries", ctx, devicePath, retries, sleep) ret0, _ := ret[0].(error) return ret0 } // RemoveMultipathDeviceMappingWithRetries indicates an expected call of RemoveMultipathDeviceMappingWithRetries. -func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMappingWithRetries(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) RemoveMultipathDeviceMappingWithRetries(ctx, devicePath, retries, sleep any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMappingWithRetries", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMappingWithRetries), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveMultipathDeviceMappingWithRetries", reflect.TypeOf((*MockDevices)(nil).RemoveMultipathDeviceMappingWithRetries), ctx, devicePath, retries, sleep) } // ScanTargetLUN mocks base method. -func (m *MockDevices) ScanTargetLUN(arg0 context.Context, arg1 []models.ScsiDeviceAddress) error { +func (m *MockDevices) ScanTargetLUN(ctx context.Context, deviceAddresses []models.ScsiDeviceAddress) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ScanTargetLUN", arg0, arg1) + ret := m.ctrl.Call(m, "ScanTargetLUN", ctx, deviceAddresses) ret0, _ := ret[0].(error) return ret0 } // ScanTargetLUN indicates an expected call of ScanTargetLUN. -func (mr *MockDevicesMockRecorder) ScanTargetLUN(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) ScanTargetLUN(ctx, deviceAddresses any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanTargetLUN", reflect.TypeOf((*MockDevices)(nil).ScanTargetLUN), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ScanTargetLUN", reflect.TypeOf((*MockDevices)(nil).ScanTargetLUN), ctx, deviceAddresses) } // VerifyMultipathDevice mocks base method. -func (m *MockDevices) VerifyMultipathDevice(arg0 context.Context, arg1 *models.VolumePublishInfo, arg2 []models.VolumePublishInfo, arg3 *models.ScsiDeviceInfo) (bool, error) { +func (m *MockDevices) VerifyMultipathDevice(ctx context.Context, publishInfo *models.VolumePublishInfo, allPublishInfos []models.VolumePublishInfo, deviceInfo *models.ScsiDeviceInfo) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VerifyMultipathDevice", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "VerifyMultipathDevice", ctx, publishInfo, allPublishInfos, deviceInfo) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // VerifyMultipathDevice indicates an expected call of VerifyMultipathDevice. -func (mr *MockDevicesMockRecorder) VerifyMultipathDevice(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) VerifyMultipathDevice(ctx, publishInfo, allPublishInfos, deviceInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDevice", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDevice), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDevice", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDevice), ctx, publishInfo, allPublishInfos, deviceInfo) } // VerifyMultipathDeviceSize mocks base method. -func (m *MockDevices) VerifyMultipathDeviceSize(arg0 context.Context, arg1, arg2 string) (int64, bool, error) { +func (m *MockDevices) VerifyMultipathDeviceSize(ctx context.Context, multipathDevice, device string) (int64, bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VerifyMultipathDeviceSize", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "VerifyMultipathDeviceSize", ctx, multipathDevice, device) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(error) @@ -395,35 +396,35 @@ func (m *MockDevices) VerifyMultipathDeviceSize(arg0 context.Context, arg1, arg2 } // VerifyMultipathDeviceSize indicates an expected call of VerifyMultipathDeviceSize. -func (mr *MockDevicesMockRecorder) VerifyMultipathDeviceSize(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) VerifyMultipathDeviceSize(ctx, multipathDevice, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDeviceSize", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDeviceSize), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyMultipathDeviceSize", reflect.TypeOf((*MockDevices)(nil).VerifyMultipathDeviceSize), ctx, multipathDevice, device) } // WaitForDevice mocks base method. -func (m *MockDevices) WaitForDevice(arg0 context.Context, arg1 string) error { +func (m *MockDevices) WaitForDevice(ctx context.Context, device string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForDevice", arg0, arg1) + ret := m.ctrl.Call(m, "WaitForDevice", ctx, device) ret0, _ := ret[0].(error) return ret0 } // WaitForDevice indicates an expected call of WaitForDevice. -func (mr *MockDevicesMockRecorder) WaitForDevice(arg0, arg1 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) WaitForDevice(ctx, device any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevice", reflect.TypeOf((*MockDevices)(nil).WaitForDevice), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevice", reflect.TypeOf((*MockDevices)(nil).WaitForDevice), ctx, device) } // WaitForDevicesRemoval mocks base method. -func (m *MockDevices) WaitForDevicesRemoval(arg0 context.Context, arg1 string, arg2 []string, arg3 time.Duration) error { +func (m *MockDevices) WaitForDevicesRemoval(ctx context.Context, devicePathPrefix string, deviceNames []string, maxWaitTime time.Duration) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitForDevicesRemoval", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "WaitForDevicesRemoval", ctx, devicePathPrefix, deviceNames, maxWaitTime) ret0, _ := ret[0].(error) return ret0 } // WaitForDevicesRemoval indicates an expected call of WaitForDevicesRemoval. -func (mr *MockDevicesMockRecorder) WaitForDevicesRemoval(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockDevicesMockRecorder) WaitForDevicesRemoval(ctx, devicePathPrefix, deviceNames, maxWaitTime any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevicesRemoval", reflect.TypeOf((*MockDevices)(nil).WaitForDevicesRemoval), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForDevicesRemoval", reflect.TypeOf((*MockDevices)(nil).WaitForDevicesRemoval), ctx, devicePathPrefix, deviceNames, maxWaitTime) } diff --git a/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go b/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go index 3cc3710f4..6cc5c4ef7 100644 --- a/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go +++ b/mocks/mock_utils/mock_devices/mock_luks/mock_luks.go @@ -11,7 +11,7 @@ package mock_luks import ( context "context" - fs "io/fs" + os "os" reflect "reflect" gomock "go.uber.org/mock/gomock" @@ -21,6 +21,7 @@ import ( type MockOS struct { ctrl *gomock.Controller recorder *MockOSMockRecorder + isgomock struct{} } // MockOSMockRecorder is the mock recorder for MockOS. @@ -41,84 +42,85 @@ func (m *MockOS) EXPECT() *MockOSMockRecorder { } // Glob mocks base method. -func (m *MockOS) Glob(arg0 string) ([]string, error) { +func (m *MockOS) Glob(pattern string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Glob", arg0) + ret := m.ctrl.Call(m, "Glob", pattern) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // Glob indicates an expected call of Glob. -func (mr *MockOSMockRecorder) Glob(arg0 any) *gomock.Call { +func (mr *MockOSMockRecorder) Glob(pattern any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Glob", reflect.TypeOf((*MockOS)(nil).Glob), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Glob", reflect.TypeOf((*MockOS)(nil).Glob), pattern) } // ReadDir mocks base method. -func (m *MockOS) ReadDir(arg0 string) ([]fs.FileInfo, error) { +func (m *MockOS) ReadDir(dirname string) ([]os.FileInfo, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadDir", arg0) - ret0, _ := ret[0].([]fs.FileInfo) + ret := m.ctrl.Call(m, "ReadDir", dirname) + ret0, _ := ret[0].([]os.FileInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadDir indicates an expected call of ReadDir. -func (mr *MockOSMockRecorder) ReadDir(arg0 any) *gomock.Call { +func (mr *MockOSMockRecorder) ReadDir(dirname any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDir", reflect.TypeOf((*MockOS)(nil).ReadDir), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDir", reflect.TypeOf((*MockOS)(nil).ReadDir), dirname) } // ReadFile mocks base method. -func (m *MockOS) ReadFile(arg0 string) ([]byte, error) { +func (m *MockOS) ReadFile(filename string) ([]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadFile", arg0) + ret := m.ctrl.Call(m, "ReadFile", filename) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadFile indicates an expected call of ReadFile. -func (mr *MockOSMockRecorder) ReadFile(arg0 any) *gomock.Call { +func (mr *MockOSMockRecorder) ReadFile(filename any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFile", reflect.TypeOf((*MockOS)(nil).ReadFile), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFile", reflect.TypeOf((*MockOS)(nil).ReadFile), filename) } // ReadlinkIfPossible mocks base method. -func (m *MockOS) ReadlinkIfPossible(arg0 string) (string, error) { +func (m *MockOS) ReadlinkIfPossible(name string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadlinkIfPossible", arg0) + ret := m.ctrl.Call(m, "ReadlinkIfPossible", name) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadlinkIfPossible indicates an expected call of ReadlinkIfPossible. -func (mr *MockOSMockRecorder) ReadlinkIfPossible(arg0 any) *gomock.Call { +func (mr *MockOSMockRecorder) ReadlinkIfPossible(name any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadlinkIfPossible", reflect.TypeOf((*MockOS)(nil).ReadlinkIfPossible), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadlinkIfPossible", reflect.TypeOf((*MockOS)(nil).ReadlinkIfPossible), name) } // Stat mocks base method. -func (m *MockOS) Stat(arg0 string) (fs.FileInfo, error) { +func (m *MockOS) Stat(name string) (os.FileInfo, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Stat", arg0) - ret0, _ := ret[0].(fs.FileInfo) + ret := m.ctrl.Call(m, "Stat", name) + ret0, _ := ret[0].(os.FileInfo) ret1, _ := ret[1].(error) return ret0, ret1 } // Stat indicates an expected call of Stat. -func (mr *MockOSMockRecorder) Stat(arg0 any) *gomock.Call { +func (mr *MockOSMockRecorder) Stat(name any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockOS)(nil).Stat), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockOS)(nil).Stat), name) } // MockDevice is a mock of Device interface. type MockDevice struct { ctrl *gomock.Controller recorder *MockDeviceMockRecorder + isgomock struct{} } // MockDeviceMockRecorder is the mock recorder for MockDevice. @@ -139,62 +141,64 @@ func (m *MockDevice) EXPECT() *MockDeviceMockRecorder { } // CheckPassphrase mocks base method. -func (m *MockDevice) CheckPassphrase(arg0 context.Context, arg1 string) (bool, error) { +func (m *MockDevice) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckPassphrase", arg0, arg1) + ret := m.ctrl.Call(m, "CheckPassphrase", ctx, luksPassphrase) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // CheckPassphrase indicates an expected call of CheckPassphrase. -func (mr *MockDeviceMockRecorder) CheckPassphrase(arg0, arg1 any) *gomock.Call { +func (mr *MockDeviceMockRecorder) CheckPassphrase(ctx, luksPassphrase any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPassphrase", reflect.TypeOf((*MockDevice)(nil).CheckPassphrase), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckPassphrase", reflect.TypeOf((*MockDevice)(nil).CheckPassphrase), ctx, luksPassphrase) } // EnsureDeviceMappedOnHost mocks base method. -func (m *MockDevice) EnsureDeviceMappedOnHost(arg0 context.Context, arg1 string, arg2 map[string]string) (bool, error) { +func (m *MockDevice) EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureDeviceMappedOnHost", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "EnsureDeviceMappedOnHost", ctx, name, secrets) ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // EnsureDeviceMappedOnHost indicates an expected call of EnsureDeviceMappedOnHost. -func (mr *MockDeviceMockRecorder) EnsureDeviceMappedOnHost(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockDeviceMockRecorder) EnsureDeviceMappedOnHost(ctx, name, secrets any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceMappedOnHost", reflect.TypeOf((*MockDevice)(nil).EnsureDeviceMappedOnHost), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureDeviceMappedOnHost", reflect.TypeOf((*MockDevice)(nil).EnsureDeviceMappedOnHost), ctx, name, secrets) } // EnsureFormattedAndOpen mocks base method. -func (m *MockDevice) EnsureFormattedAndOpen(arg0 context.Context, arg1 string) (bool, error) { +func (m *MockDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EnsureFormattedAndOpen", arg0, arg1) + ret := m.ctrl.Call(m, "EnsureFormattedAndOpen", ctx, luksPassphrase) ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // EnsureFormattedAndOpen indicates an expected call of EnsureFormattedAndOpen. -func (mr *MockDeviceMockRecorder) EnsureFormattedAndOpen(arg0, arg1 any) *gomock.Call { +func (mr *MockDeviceMockRecorder) EnsureFormattedAndOpen(ctx, luksPassphrase any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureFormattedAndOpen", reflect.TypeOf((*MockDevice)(nil).EnsureFormattedAndOpen), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureFormattedAndOpen", reflect.TypeOf((*MockDevice)(nil).EnsureFormattedAndOpen), ctx, luksPassphrase) } // IsMappingStale mocks base method. -func (m *MockDevice) IsMappingStale(arg0 context.Context) bool { +func (m *MockDevice) IsMappingStale(ctx context.Context) bool { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsMappingStale", arg0) + ret := m.ctrl.Call(m, "IsMappingStale", ctx) ret0, _ := ret[0].(bool) return ret0 } // IsMappingStale indicates an expected call of IsMappingStale. -func (mr *MockDeviceMockRecorder) IsMappingStale(arg0 any) *gomock.Call { +func (mr *MockDeviceMockRecorder) IsMappingStale(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMappingStale", reflect.TypeOf((*MockDevice)(nil).IsMappingStale), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMappingStale", reflect.TypeOf((*MockDevice)(nil).IsMappingStale), ctx) } // MappedDeviceName mocks base method. @@ -240,15 +244,15 @@ func (mr *MockDeviceMockRecorder) RawDevicePath() *gomock.Call { } // RotatePassphrase mocks base method. -func (m *MockDevice) RotatePassphrase(arg0 context.Context, arg1, arg2, arg3 string) error { +func (m *MockDevice) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RotatePassphrase", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "RotatePassphrase", ctx, volumeId, previousLUKSPassphrase, luksPassphrase) ret0, _ := ret[0].(error) return ret0 } // RotatePassphrase indicates an expected call of RotatePassphrase. -func (mr *MockDeviceMockRecorder) RotatePassphrase(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockDeviceMockRecorder) RotatePassphrase(ctx, volumeId, previousLUKSPassphrase, luksPassphrase any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotatePassphrase", reflect.TypeOf((*MockDevice)(nil).RotatePassphrase), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotatePassphrase", reflect.TypeOf((*MockDevice)(nil).RotatePassphrase), ctx, volumeId, previousLUKSPassphrase, luksPassphrase) } diff --git a/utils/devices/luks/luks.go b/utils/devices/luks/luks.go index 6f15696e2..1b10496d4 100644 --- a/utils/devices/luks/luks.go +++ b/utils/devices/luks/luks.go @@ -75,11 +75,11 @@ func (o *osFs) ReadlinkIfPossible(name string) (string, error) { } type Device interface { - EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) + EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, bool, error) MappedDevicePath() string MappedDeviceName() string RawDevicePath() string - EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, error) + EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (bool, bool, error) CheckPassphrase(ctx context.Context, luksPassphrase string) (bool, error) RotatePassphrase(ctx context.Context, volumeId, previousLUKSPassphrase, luksPassphrase string) error IsMappingStale(ctx context.Context) bool @@ -121,48 +121,48 @@ func NewDeviceFromMappingPath( } // EnsureDeviceMappedOnHost ensures the specified device is LUKS formatted, opened, and has the current passphrase. -func (d *LUKSDevice) EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, error) { +func (d *LUKSDevice) EnsureDeviceMappedOnHost(ctx context.Context, name string, secrets map[string]string) (bool, bool, error) { // Try to Open with current luks passphrase luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase := GetLUKSPassphrasesFromSecretMap(secrets) if luksPassphrase == "" { - return false, errors.New("LUKS passphrase cannot be empty") + return false, false, errors.New("LUKS passphrase cannot be empty") } if luksPassphraseName == "" { - return false, errors.New("LUKS passphrase name cannot be empty") + return false, false, errors.New("LUKS passphrase name cannot be empty") } Logc(ctx).WithFields(LogFields{ "volume": name, "luks-passphrase-name": luksPassphraseName, }).Info("Opening encrypted volume.") - luksFormatted, err := d.EnsureFormattedAndOpen(ctx, luksPassphrase) + luksFormatted, safeToFormat, err := d.EnsureFormattedAndOpen(ctx, luksPassphrase) // If we fail due to a format issue there is no need to try to open the device. if err == nil || errors.IsFormatError(err) { - return luksFormatted, err + return luksFormatted, safeToFormat, err } // If we failed to open, try previous passphrase if previousLUKSPassphrase == "" { // Return original error if there is no previous passphrase to use - return luksFormatted, fmt.Errorf("could not open LUKS device; %v", err) + return luksFormatted, safeToFormat, fmt.Errorf("could not open LUKS device; %v", err) } if luksPassphrase == previousLUKSPassphrase { - return luksFormatted, errors.New("could not open LUKS device, previous passphrase matches current") + return luksFormatted, safeToFormat, errors.New("could not open LUKS device, previous passphrase matches current") } if previousLUKSPassphraseName == "" { - return luksFormatted, errors.New("could not open LUKS device, no previous passphrase name provided") + return luksFormatted, safeToFormat, errors.New("could not open LUKS device, no previous passphrase name provided") } Logc(ctx).WithFields(LogFields{ "volume": name, "luks-passphrase-name": previousLUKSPassphraseName, }).Info("Opening encrypted volume.") - luksFormatted, err = d.EnsureFormattedAndOpen(ctx, previousLUKSPassphrase) + luksFormatted, safeToFormat, err = d.EnsureFormattedAndOpen(ctx, previousLUKSPassphrase) if err != nil { - return luksFormatted, fmt.Errorf("could not open LUKS device; %v", err) + return luksFormatted, safeToFormat, fmt.Errorf("could not open LUKS device; %v", err) } - return luksFormatted, nil + return luksFormatted, safeToFormat, nil } // MappedDevicePath returns the location of the LUKS device when opened. @@ -181,7 +181,11 @@ func (d *LUKSDevice) RawDevicePath() string { } // EnsureFormattedAndOpen ensures the specified device is LUKS formatted and opened. -func (d *LUKSDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) (formatted bool, err error) { +// Returns two booleans: the first indicates if the device is crypt formatted, +// the second indicates if the device is safe to file format, meaning the device was empty before being crypt formatted. +func (d *LUKSDevice) EnsureFormattedAndOpen(ctx context.Context, luksPassphrase string) ( + formatted, safeToFileFormat bool, err error, +) { return d.ensureLUKSDevice(ctx, luksPassphrase) } @@ -197,7 +201,10 @@ func (d *LUKSDevice) IsMappingStale(ctx context.Context) bool { return d.isMappingStale(ctx) } -func (d *LUKSDevice) ensureLUKSDevice(ctx context.Context, luksPassphrase string) (bool, error) { +// ensureLUKSDevice ensures the device is LUKS formatted and opened. +// Returns two booleans: the first indicates if the device is formatted, +// the second indicates if the device is safe to file format, meaning the device was empty before being crypt formatted. +func (d *LUKSDevice) ensureLUKSDevice(ctx context.Context, luksPassphrase string) (bool, bool, error) { // First check if LUKS device is already opened. This is OK to check even if the device isn't LUKS formatted. if isOpen, err := d.IsOpen(ctx); err != nil { // If the LUKS device isn't found, it means that we need to check if the device is LUKS formatted. @@ -205,16 +212,18 @@ func (d *LUKSDevice) ensureLUKSDevice(ctx context.Context, luksPassphrase string // If any other error occurs, bail out. if !errors.IsNotFoundError(err) { Logc(ctx).WithError(err).Error("Could not check if device is an open LUKS device.") - return false, err + return false, false, err } } else if isOpen { Logc(ctx).Debug("Device is LUKS formatted and open.") - return true, nil + return true, false, nil } - if err := d.formatUnformattedDevice(ctx, luksPassphrase); err != nil { + var safeToFileFormat bool + var err error + if safeToFileFormat, err = d.formatUnformattedDevice(ctx, luksPassphrase); err != nil { Logc(ctx).WithError(err).Error("Could not LUKS format device.") - return false, fmt.Errorf("could not LUKS format device; %w", err) + return false, safeToFileFormat, fmt.Errorf("could not LUKS format device; %w", err) } // At this point, we should be able to open the device. @@ -222,11 +231,11 @@ func (d *LUKSDevice) ensureLUKSDevice(ctx context.Context, luksPassphrase string // At this point, we couldn't open the LUKS device, but we do know // the device is LUKS formatted because LUKSFormat didn't fail. Logc(ctx).WithError(err).Error("Could not open LUKS formatted device.") - return true, fmt.Errorf("could not open LUKS device; %v", err) + return true, safeToFileFormat, fmt.Errorf("could not open LUKS device; %v", err) } Logc(ctx).Debug("Device is LUKS formatted and open.") - return true, nil + return true, safeToFileFormat, nil } func GetLUKSPassphrasesFromSecretMap(secrets map[string]string) (string, string, string, string) { diff --git a/utils/devices/luks/luks_darwin.go b/utils/devices/luks/luks_darwin.go index 86c2d701b..3b0dc380d 100644 --- a/utils/devices/luks/luks_darwin.go +++ b/utils/devices/luks/luks_darwin.go @@ -38,10 +38,10 @@ func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { // formatUnformattedDevice attempts to set up LUKS headers on a device with the specified passphrase, but bails if the // underlying device already has a format present that is not LUKS. -func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, _ string) error { +func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, _ string) (bool, error) { Logc(ctx).Debug(">>>> devices_darwin.formatUnformattedDevice") defer Logc(ctx).Debug("<<<< devices_darwin.formatUnformattedDevice") - return errors.UnsupportedError("formatUnformattedDevice is not supported for darwin") + return false, errors.UnsupportedError("formatUnformattedDevice is not supported for darwin") } // Open makes the device accessible on the host diff --git a/utils/devices/luks/luks_linux.go b/utils/devices/luks/luks_linux.go index a607df2ec..ab25426c1 100644 --- a/utils/devices/luks/luks_linux.go +++ b/utils/devices/luks/luks_linux.go @@ -119,22 +119,23 @@ func (d *LUKSDevice) format(ctx context.Context, luksPassphrase string) error { // formatUnformattedDevice attempts to set up LUKS headers on a device with the specified passphrase, but bails out if the // underlying device already has a format present. -func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, luksPassphrase string) error { +// Returns true if the device was just crypt formatted and ready for file format. +func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, luksPassphrase string) (bool, error) { fields := LogFields{"device": d.RawDevicePath()} // Check if the device is already LUKS formatted. if luksFormatted, err := d.IsLUKSFormatted(ctx); err != nil { - return fmt.Errorf("failed to check if device is LUKS formatted; %w", err) + return false, fmt.Errorf("failed to check if device is LUKS formatted; %w", err) } else if luksFormatted { Logc(ctx).WithFields(fields).Debug("Device is already LUKS formatted.") - return nil + return false, nil } // Ensure the device is empty before attempting LUKS format. if unformatted, err := d.devices.IsDeviceUnformatted(ctx, d.RawDevicePath()); err != nil { - return fmt.Errorf("failed to check if device is unformatted; %w", err) + return false, fmt.Errorf("failed to check if device is unformatted; %w", err) } else if !unformatted { - return errors.New("cannot LUKS format device; device is not empty") + return false, errors.New("cannot LUKS format device; device is not empty") } // Attempt LUKS format. @@ -148,18 +149,18 @@ func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, luksPassphrase Logc(ctx).WithError(clearFormatErr).Error("Failed to clear LUKS format. Format retries may fail.") } } - return fmt.Errorf("failed to LUKS format device; %w", err) + return false, fmt.Errorf("failed to LUKS format device; %w", err) } // At this point, the device should be LUKS formatted. If it still is not formatted, fail. if luksFormatted, err := d.IsLUKSFormatted(ctx); err != nil { - return fmt.Errorf("failed to check if device is LUKS formatted; %w", err) + return false, fmt.Errorf("failed to check if device is LUKS formatted; %w", err) } else if !luksFormatted { - return errors.New("device is not LUKS formatted") + return false, errors.New("device is not LUKS formatted") } Logc(ctx).WithFields(fields).Debug("Device is LUKS formatted.") - return nil + return true, nil } // IsLUKSFormatted returns whether LUKS headers have been placed on the device diff --git a/utils/devices/luks/luks_linux_test.go b/utils/devices/luks/luks_linux_test.go index 9af144185..f83b3faa4 100644 --- a/utils/devices/luks/luks_linux_test.go +++ b/utils/devices/luks/luks_linux_test.go @@ -110,7 +110,8 @@ func TestLUKSDevice_Format(t *testing.T) { mockDevices.EXPECT().IsDeviceUnformatted(gomock.Any(), "/dev/sdb").Return(true, nil) luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, mockDevices, afero.NewMemMapFs()) - err := luksDevice.formatUnformattedDevice(context.Background(), "passphrase") + safeToFormat, err := luksDevice.formatUnformattedDevice(context.Background(), "passphrase") + assert.True(t, safeToFormat) assert.NoError(t, err) } @@ -126,7 +127,8 @@ func TestLUKSFormat_UnformattedCheckError(t *testing.T) { mockDevices.EXPECT().IsDeviceUnformatted(gomock.Any(), "/dev/sdb").Return(false, errors.New("mock error")) luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, mockDevices, afero.NewMemMapFs()) - err := luksDevice.formatUnformattedDevice(context.Background(), "passphrase") + safeToFormat, err := luksDevice.formatUnformattedDevice(context.Background(), "passphrase") + assert.False(t, safeToFormat) assert.Error(t, err) } @@ -145,7 +147,8 @@ func TestLUKSFormat_SecondFormatCheckError(t *testing.T) { mockDevices.EXPECT().IsDeviceUnformatted(gomock.Any(), "/dev/sdb").Return(true, nil) luksDevice := NewDetailed("/dev/sdb", "pvc-test", mockCommand, mockDevices, afero.NewMemMapFs()) - err := luksDevice.formatUnformattedDevice(context.Background(), "passphrase") + safeToFormat, err := luksDevice.formatUnformattedDevice(context.Background(), "passphrase") + assert.False(t, safeToFormat) assert.Error(t, err) } @@ -162,7 +165,8 @@ func TestLUKSDevice_LUKSFormat_FailsCheckingIfDeviceIsLUKS(t *testing.T) { // Mock any cryptsetup calls that may occur. mockCryptsetupIsLuks(mockCommand).Return([]byte(""), luksError) - err := luksDevice.formatUnformattedDevice(ctx, "mysecretlukspassphrase") + safeToFormat, err := luksDevice.formatUnformattedDevice(ctx, "mysecretlukspassphrase") + assert.False(t, safeToFormat) assert.Error(t, err) } @@ -278,7 +282,8 @@ func TestEnsureLUKSDevice_FailsWithExecError(t *testing.T) { mockCryptsetupLuksStatus(mockCommand).Return([]byte(""), luksError) luksDevice := NewDetailed("/dev/sdb", devicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) - luksFormatted, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + luksFormatted, safeToFormat, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + assert.False(t, safeToFormat) assert.Error(t, err) assert.Equal(t, false, luksFormatted) } @@ -291,7 +296,8 @@ func TestEnsureLUKSDevice_IsOpen(t *testing.T) { mockCryptsetupLuksStatus(mockCommand) luksDevice := NewDetailed("/dev/sdb", devicePrefix+"pvc-test", mockCommand, devices.New(), afero.NewMemMapFs()) - luksFormatted, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + luksFormatted, safeToFormat, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + assert.False(t, safeToFormat) assert.NoError(t, err) assert.True(t, luksFormatted) } @@ -314,7 +320,8 @@ func TestEnsureLUKSDevice_LUKSFormatFails(t *testing.T) { luksDevice := NewDetailed(rawDevicePath, devicePrefix+"pvc-test", mockCommand, mockDevices, afero.NewMemMapFs()) - luksFormatted, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + luksFormatted, safeToFormat, err := luksDevice.ensureLUKSDevice(context.Background(), "mysecretlukspassphrase") + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -618,7 +625,8 @@ func TestEnsureFormattedAndOpen(t *testing.T) { mockCommand := mock_exec.NewMockCommand(gomock.NewController(t)) mockCryptsetupLuksStatus(mockCommand) luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) - formatted, err := luksDevice.EnsureFormattedAndOpen(context.Background(), passphrase) + formatted, safeToFormat, err := luksDevice.EnsureFormattedAndOpen(context.Background(), passphrase) + assert.False(t, safeToFormat) assert.True(t, formatted) assert.NoError(t, err) } @@ -633,7 +641,8 @@ func TestMountLUKSDevice_firstPassphraseSuccess(t *testing.T) { mockCryptsetupLuksOpen(mockCommand).Return([]byte{}, nil) luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.NoError(t, err) assert.True(t, luksFormatted) } @@ -654,7 +663,8 @@ func TestMountLUKSDevice_secondPassphraseSuccess(t *testing.T) { mockCryptsetupLuksOpen(mockCommand).Return([]byte{}, nil) luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.NoError(t, err) assert.True(t, luksFormatted) } @@ -671,7 +681,8 @@ func TestMountLUKSDevice_passphraseRotationFails(t *testing.T) { mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, errors.New("mock error")).Times(2) luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -682,7 +693,8 @@ func TestMountLUKSDevice_NoPassphraseFailure(t *testing.T) { luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) secrets := map[string]string{} - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -695,7 +707,8 @@ func TestMountLUKSDevice_NoPassphraseNameFailure(t *testing.T) { secrets := map[string]string{ "luks-passphrase": "secretA", } - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -711,7 +724,8 @@ func TestMountLUKSDevice_NoSecondPassphraseNameFailure(t *testing.T) { luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, errors.New("mock error")).Times(1) - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -727,7 +741,8 @@ func TestMountLUKSDevice_NoSecondPassphraseNameSpecifiedFailure(t *testing.T) { "luks-passphrase": "secretA", "luks-passphrase-name": "A", } - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -743,7 +758,8 @@ func TestMountLUKSDevice_NoSecondPassphraseNameBlankFailure(t *testing.T) { "previous-luks-passphrase": "", "previous-luks-passphrase-name": "", } - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -759,7 +775,8 @@ func TestMountLUKSDevice_DuplicatePassphraseFailure(t *testing.T) { "previous-luks-passphrase": "secretA", "previous-luks-passphrase-name": "A", } - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -774,7 +791,8 @@ func TestMountLUKSDevice_FirstPassphraseBlankFailure(t *testing.T) { "previous-luks-passphrase": "secretB", "previous-luks-passphrase-name": "B", } - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } @@ -791,7 +809,8 @@ func TestMountLUKSDevice_FirstPassphraseBlankFailureasdf(t *testing.T) { luksDevice := NewDetailed("/dev/sdb", "1234", mockCommand, nil, nil) mockCryptsetupLuksStatus(mockCommand).Return([]byte{}, errors.New("mock-error")).Times(2) - luksFormatted, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + luksFormatted, safeToFormat, err := luksDevice.EnsureDeviceMappedOnHost(context.Background(), "pvc-test", secrets) + assert.False(t, safeToFormat) assert.Error(t, err) assert.False(t, luksFormatted) } diff --git a/utils/devices/luks/luks_windows.go b/utils/devices/luks/luks_windows.go index 73ed58355..c247d2159 100644 --- a/utils/devices/luks/luks_windows.go +++ b/utils/devices/luks/luks_windows.go @@ -38,10 +38,10 @@ func (d *LUKSDevice) IsOpen(ctx context.Context) (bool, error) { // formatUnformattedDevice attempts to set up LUKS headers on a device with the specified passphrase, but bails if thea // underlying device already has a format present that is not LUKS. -func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, _ string) error { +func (d *LUKSDevice) formatUnformattedDevice(ctx context.Context, _ string) (bool, error) { Logc(ctx).Debug(">>>> devices_windows.formatUnformattedDevice") defer Logc(ctx).Debug("<<<< devices_windows.formatUnformattedDevice") - return errors.UnsupportedError("formatUnformattedDevice is not supported for windows") + return false, errors.UnsupportedError("formatUnformattedDevice is not supported for windows") } // Open makes the device accessible on the host diff --git a/utils/fcp/fcp.go b/utils/fcp/fcp.go index 803ceac02..d619b8149 100644 --- a/utils/fcp/fcp.go +++ b/utils/fcp/fcp.go @@ -497,10 +497,10 @@ func (client *Client) AttachVolume( } // Return the device in the publish info in case the mount will be done later publishInfo.DevicePath = devicePath - + var safeToFormat bool if isLUKSDevice { luksDevice := luks.NewDevice(devicePath, name, client.command) - luksFormatted, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) + luksFormatted, safeToFormat, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) if err != nil { return mpathSize, err } @@ -511,10 +511,16 @@ func (client *Client) AttachVolume( return mpathSize, nil } - existingFstype, err := client.deviceClient.GetDeviceFSType(ctx, devicePath) - if err != nil { - return mpathSize, err + var existingFstype string + if isLUKSDevice && safeToFormat { + existingFstype = "" + } else { + existingFstype, err = client.deviceClient.GetDeviceFSType(ctx, devicePath) + if err != nil { + return mpathSize, err + } } + if existingFstype == "" { if !isLUKSDevice { if unformatted, err := client.deviceClient.IsDeviceUnformatted(ctx, devicePath); err != nil { diff --git a/utils/iscsi/iscsi.go b/utils/iscsi/iscsi.go index 1b682ed8e..b1110058c 100644 --- a/utils/iscsi/iscsi.go +++ b/utils/iscsi/iscsi.go @@ -411,10 +411,11 @@ func (client *Client) AttachVolume( // If LUKS encryption is requested, ensure the device is formatted and open. var luksFormatted bool + var safeToFormat bool isLUKSDevice := convert.ToBool(publishInfo.LUKSEncryption) if isLUKSDevice { luksDevice := luks.NewDevice(devicePath, name, client.command) - luksFormatted, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) + luksFormatted, safeToFormat, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) if err != nil { return mpathSize, err } @@ -437,10 +438,16 @@ func (client *Client) AttachVolume( return mpathSize, nil } - existingFstype, err := client.devices.GetDeviceFSType(ctx, devicePath) - if err != nil { - return mpathSize, err + var existingFstype string + if isLUKSDevice && safeToFormat { + existingFstype = "" + } else { + existingFstype, err = client.devices.GetDeviceFSType(ctx, devicePath) + if err != nil { + return mpathSize, err + } } + if existingFstype == "" { if !isLUKSDevice { if unformatted, err := client.devices.IsDeviceUnformatted(ctx, devicePath); err != nil { diff --git a/utils/nvme/nvme.go b/utils/nvme/nvme.go index 7bf00294b..380f47d28 100644 --- a/utils/nvme/nvme.go +++ b/utils/nvme/nvme.go @@ -326,6 +326,7 @@ func (nh *NVMeHandler) NVMeMountVolume( // If LUKS encryption is requested, ensure the device is formatted and open. var luksFormatted bool var err error + var safeToFormat bool isLUKSDevice := convert.ToBool(publishInfo.LUKSEncryption) if isLUKSDevice { luksDevice := luks.NewDevice(devicePath, name, nh.command) @@ -340,7 +341,7 @@ func (nh *NVMeHandler) NVMeMountVolume( } } - luksFormatted, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) + luksFormatted, safeToFormat, err = luksDevice.EnsureDeviceMappedOnHost(ctx, name, secrets) if err != nil { return err } @@ -368,10 +369,16 @@ func (nh *NVMeHandler) NVMeMountVolume( return nil } - existingFstype, err := nh.devicesClient.GetDeviceFSType(ctx, devicePath) - if err != nil { - return err + var existingFstype string + if isLUKSDevice && safeToFormat { + existingFstype = "" + } else { + existingFstype, err = nh.devicesClient.GetDeviceFSType(ctx, devicePath) + if err != nil { + return err + } } + if existingFstype == "" { if !isLUKSDevice { if unformatted, err := nh.devicesClient.IsDeviceUnformatted(ctx, devicePath); err != nil { From 769bfbb18b5974fca9a457be24900ece7fd5b2f4 Mon Sep 17 00:00:00 2001 From: Joe Webster <31218426+jwebster7@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:57:58 -0600 Subject: [PATCH 23/30] Require healthy paths during iscsi volume expansion Trident now checks the state of all paths to a LUN before rescanning or resizing the multipath map. If any path is unhealthy, expansion is deferred until all paths are remediated. Trident now also requires stable reads of path health, SCSI disk size, and multipath device size before returning success. --- frontend/csi/node_server.go | 24 +- frontend/csi/node_server_test.go | 54 +- .../mock_devices/mock_devices_client.go | 14 + .../mock_iscsi/mock_iscsi_client.go | 42 +- pkg/collection/list.go | 25 +- pkg/collection/list_test.go | 96 ++ utils/devices/devices.go | 5 +- utils/devices/devices_darwin.go | 11 +- utils/devices/devices_linux.go | 444 ++++++- utils/devices/devices_linux_test.go | 1048 ++++++++++++++++- utils/devices/devices_test.go | 55 +- utils/devices/devices_windows.go | 13 +- utils/iscsi/iscsi.go | 232 +--- utils/iscsi/iscsi_linux_test.go | 2 +- utils/iscsi/iscsi_test.go | 601 +--------- utils/models/types.go | 76 +- utils/models/types_test.go | 343 ++++++ 17 files changed, 2174 insertions(+), 911 deletions(-) diff --git a/frontend/csi/node_server.go b/frontend/csi/node_server.go index 8a9454be4..ab5e3baec 100644 --- a/frontend/csi/node_server.go +++ b/frontend/csi/node_server.go @@ -742,24 +742,16 @@ func (p *Plugin) nodePrepareISCSIVolumeForExpansion( "filesystemType": publishInfo.FilesystemType, }).Debug("PublishInfo for block device to expand.") - var err error - - // Make sure device is ready. - if p.iscsi.IsAlreadyAttached(ctx, lunID, publishInfo.IscsiTargetIQN) { - // Rescan device to detect increased size. - if err = p.iscsi.RescanDevices( - ctx, publishInfo.IscsiTargetIQN, publishInfo.IscsiLunNumber, requiredBytes); err != nil { - Logc(ctx).WithField("device", publishInfo.DevicePath).WithError(err). - Error("Unable to scan device.") - err = status.Error(codes.Internal, err.Error()) - } - } else { - err = fmt.Errorf("device %s to expand is not attached", publishInfo.DevicePath) - Logc(ctx).WithField("devicePath", publishInfo.DevicePath).WithError(err).Error( - "Unable to expand volume.") + // Resize the volume. + if err := p.iscsi.ExpandVolume(ctx, publishInfo, requiredBytes); err != nil { + Logc(ctx).WithFields(LogFields{ + "lunID": publishInfo.IscsiLunNumber, + "devicePath": publishInfo.DevicePath, + }).WithError(err).Error("Unable to resize device(s) for LUN.") return status.Error(codes.Internal, err.Error()) } - return err + + return nil } func (p *Plugin) NodeGetCapabilities( diff --git a/frontend/csi/node_server_test.go b/frontend/csi/node_server_test.go index 3afe1bb8e..02b705b2a 100644 --- a/frontend/csi/node_server_test.go +++ b/frontend/csi/node_server_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package csi @@ -75,8 +75,7 @@ func TestNodeStageVolume(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) return mockISCSIClient }, @@ -252,8 +251,7 @@ func TestNodeStageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) return mockISCSIClient }, @@ -520,7 +518,9 @@ func TestNodeStageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(false) + mockISCSIClient.EXPECT().ExpandVolume( + gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(errors.New("volume not attached")) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) return mockISCSIClient }, @@ -538,9 +538,9 @@ func TestNodeStageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any()).Return(errors.New("some error")) + mockISCSIClient.EXPECT().ExpandVolume( + gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(errors.New("volume resize failed")) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) return mockISCSIClient }, @@ -558,8 +558,7 @@ func TestNodeStageISCSIVolume(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) return mockISCSIClient }, @@ -2733,8 +2732,7 @@ func TestNodeStageVolume_Multithreaded(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) mockTrackingClient.EXPECT().WriteTrackingInfo(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(nil) } @@ -2881,8 +2879,7 @@ func TestNodeStageVolume_Multithreaded(t *testing.T) { mockISCSIClient.EXPECT().AttachVolumeRetry( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(int64(1), nil) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mockISCSIClient.EXPECT().AddSession(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) mockTrackingClient.EXPECT().WriteTrackingInfo(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(nil) } @@ -11662,8 +11659,7 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return mockISCSIClient }, mockFilesystem: func() filesystem.Filesystem { @@ -11703,8 +11699,8 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), + gomock.Any()).Return(errors.New("failure")).AnyTimes() return mockISCSIClient }, @@ -11738,8 +11734,7 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("")).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return mockISCSIClient }, @@ -11773,8 +11768,7 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return mockISCSIClient }, mockFilesystem: func() filesystem.Filesystem { @@ -11924,8 +11918,7 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return mockISCSIClient }, mockFilesystem: func() filesystem.Filesystem { @@ -11972,8 +11965,7 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() return mockISCSIClient }, mockFilesystem: func() filesystem.Filesystem { @@ -12020,8 +12012,8 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), + gomock.Any()).Return(nil).AnyTimes() return mockISCSIClient }, mockFilesystem: func() filesystem.Filesystem { @@ -12068,8 +12060,8 @@ func TestNodeExpandVolume(t *testing.T) { }, setupISCSIMock: func() iscsi.ISCSI { mockISCSIClient := mock_iscsi.NewMockISCSI(gomock.NewController(t)) - mockISCSIClient.EXPECT().IsAlreadyAttached(gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes() - mockISCSIClient.EXPECT().RescanDevices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockISCSIClient.EXPECT().ExpandVolume(gomock.Any(), gomock.Any(), + gomock.Any()).Return(errors.New("failure")).AnyTimes() return mockISCSIClient }, mockFilesystem: func() filesystem.Filesystem { diff --git a/mocks/mock_utils/mock_devices/mock_devices_client.go b/mocks/mock_utils/mock_devices/mock_devices_client.go index e66a1d205..a1d3427ab 100644 --- a/mocks/mock_utils/mock_devices/mock_devices_client.go +++ b/mocks/mock_utils/mock_devices/mock_devices_client.go @@ -112,6 +112,20 @@ func (mr *MockDevicesMockRecorder) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, l return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureLUKSDeviceClosedWithMaxWaitLimit", reflect.TypeOf((*MockDevices)(nil).EnsureLUKSDeviceClosedWithMaxWaitLimit), ctx, luksDevicePath) } +// ExpandMultipathDevice mocks base method. +func (m *MockDevices) ExpandMultipathDevice(ctx context.Context, getter models.SCSIDeviceInfoGetter, targetSizeBytes int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExpandMultipathDevice", ctx, getter, targetSizeBytes) + ret0, _ := ret[0].(error) + return ret0 +} + +// ExpandMultipathDevice indicates an expected call of ExpandMultipathDevice. +func (mr *MockDevicesMockRecorder) ExpandMultipathDevice(ctx, getter, targetSizeBytes any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandMultipathDevice", reflect.TypeOf((*MockDevices)(nil).ExpandMultipathDevice), ctx, getter, targetSizeBytes) +} + // FindDevicesForMultipathDevice mocks base method. func (m *MockDevices) FindDevicesForMultipathDevice(ctx context.Context, device string) []string { m.ctrl.T.Helper() diff --git a/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go b/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go index 7797c7ff9..03ecb8865 100644 --- a/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go +++ b/mocks/mock_utils/mock_iscsi/mock_iscsi_client.go @@ -97,6 +97,34 @@ func (mr *MockISCSIMockRecorder) EnsureSessionsWithPortalDiscovery(ctx, hostData return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureSessionsWithPortalDiscovery", reflect.TypeOf((*MockISCSI)(nil).EnsureSessionsWithPortalDiscovery), ctx, hostDataIPs) } +// EnsureVolumeFormattedAndMounted mocks base method. +func (m *MockISCSI) EnsureVolumeFormattedAndMounted(ctx context.Context, name, mountPoint string, publishInfo *models.VolumePublishInfo, luksFormatted bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureVolumeFormattedAndMounted", ctx, name, mountPoint, publishInfo, luksFormatted) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnsureVolumeFormattedAndMounted indicates an expected call of EnsureVolumeFormattedAndMounted. +func (mr *MockISCSIMockRecorder) EnsureVolumeFormattedAndMounted(ctx, name, mountPoint, publishInfo, luksFormatted any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureVolumeFormattedAndMounted", reflect.TypeOf((*MockISCSI)(nil).EnsureVolumeFormattedAndMounted), ctx, name, mountPoint, publishInfo, luksFormatted) +} + +// ExpandVolume mocks base method. +func (m *MockISCSI) ExpandVolume(ctx context.Context, publishInfo *models.VolumePublishInfo, targetSizeBytes int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExpandVolume", ctx, publishInfo, targetSizeBytes) + ret0, _ := ret[0].(error) + return ret0 +} + +// ExpandVolume indicates an expected call of ExpandVolume. +func (mr *MockISCSIMockRecorder) ExpandVolume(ctx, publishInfo, targetSizeBytes any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandVolume", reflect.TypeOf((*MockISCSI)(nil).ExpandVolume), ctx, publishInfo, targetSizeBytes) +} + // GetDeviceInfoForLUN mocks base method. func (m *MockISCSI) GetDeviceInfoForLUN(ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, needFSType bool) (*models.ScsiDeviceInfo, error) { m.ctrl.T.Helper() @@ -281,20 +309,6 @@ func (mr *MockISCSIMockRecorder) RemovePortalsFromSession(ctx, publishInfo, sess return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePortalsFromSession", reflect.TypeOf((*MockISCSI)(nil).RemovePortalsFromSession), ctx, publishInfo, sessions) } -// RescanDevices mocks base method. -func (m *MockISCSI) RescanDevices(ctx context.Context, targetIQN string, lunID int32, minSize int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RescanDevices", ctx, targetIQN, lunID, minSize) - ret0, _ := ret[0].(error) - return ret0 -} - -// RescanDevices indicates an expected call of RescanDevices. -func (mr *MockISCSIMockRecorder) RescanDevices(ctx, targetIQN, lunID, minSize any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RescanDevices", reflect.TypeOf((*MockISCSI)(nil).RescanDevices), ctx, targetIQN, lunID, minSize) -} - // SafeToLogOut mocks base method. func (m *MockISCSI) SafeToLogOut(ctx context.Context, hostNumber, sessionNumber int) bool { m.ctrl.T.Helper() diff --git a/pkg/collection/list.go b/pkg/collection/list.go index c01a37c28..2fa75d99a 100644 --- a/pkg/collection/list.go +++ b/pkg/collection/list.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package collection @@ -98,7 +98,7 @@ func RemoveStringConditionally(slice []string, s string, fn func(string, string) } result = append(result, item) } - return + return result } // ReplaceAtIndex returns a string with the rune at the specified index replaced. @@ -142,3 +142,24 @@ func StringInSlice(s string, list []string) bool { } return false } + +// EqualValues accepts 2 slices of any standard comparable type and returns whether their values are equivalent. +func EqualValues[C comparable](s1, s2 []C) bool { + if len(s1) != len(s2) { + return false + } + + elemOccurrences := make(map[any]int, len(s1)) + for _, elem := range s1 { + elemOccurrences[elem] += 1 + } + + for _, v := range s2 { + elemOccurrences[v]-- + if elemOccurrences[v] < 0 { + return false + } + } + + return true +} diff --git a/pkg/collection/list_test.go b/pkg/collection/list_test.go index 6a2b8b806..64a409cf7 100644 --- a/pkg/collection/list_test.go +++ b/pkg/collection/list_test.go @@ -392,6 +392,102 @@ func TestReplaceAtIndex(t *testing.T) { assert.Equal(t, "boo", actual) } +func TestEqualValues(t *testing.T) { + tests := map[string]struct { + s1 []string + s2 []string + equal bool + }{ + "identical slices": { + s1: []string{"a", "b", "c"}, + s2: []string{"a", "b", "c"}, + equal: true, + }, + "same elements different order": { + s1: []string{"c", "a", "b"}, + s2: []string{"a", "b", "c"}, + equal: true, + }, + "both nil": { + s1: nil, + s2: nil, + equal: true, + }, + "both empty": { + s1: []string{}, + s2: []string{}, + equal: true, + }, + "nil and empty are equal": { + s1: nil, + s2: []string{}, + equal: true, + }, + "empty and nil are equal": { + s1: []string{}, + s2: nil, + equal: true, + }, + "different lengths": { + s1: []string{"a", "b"}, + s2: []string{"a", "b", "c"}, + equal: false, + }, + "same length different values": { + s1: []string{"a", "b", "c"}, + s2: []string{"a", "b", "d"}, + equal: false, + }, + "one nil one populated": { + s1: nil, + s2: []string{"a"}, + equal: false, + }, + "one empty one populated": { + s1: []string{}, + s2: []string{"a"}, + equal: false, + }, + "duplicate elements equal cardinality": { + s1: []string{"a", "a", "b"}, + s2: []string{"a", "b", "a"}, + equal: true, + }, + "duplicate elements unequal cardinality": { + s1: []string{"a", "a", "b"}, + s2: []string{"a", "b", "b"}, + equal: false, + }, + "single element equal": { + s1: []string{"x"}, + s2: []string{"x"}, + equal: true, + }, + "single element not equal": { + s1: []string{"x"}, + s2: []string{"y"}, + equal: false, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.equal, EqualValues(tc.s1, tc.s2)) + }) + } + + // Also test with int type to verify generics work. + t.Run("int slices same elements different order", func(t *testing.T) { + assert.True(t, EqualValues([]int{3, 1, 2}, []int{1, 2, 3})) + }) + t.Run("int slices different values", func(t *testing.T) { + assert.False(t, EqualValues([]int{1, 2, 3}, []int{1, 2, 4})) + }) + t.Run("int nil and empty are equal", func(t *testing.T) { + assert.True(t, EqualValues[int](nil, []int{})) + }) +} + func TestAppendToStringList(t *testing.T) { tests := []struct { stringList string diff --git a/utils/devices/devices.go b/utils/devices/devices.go index 89eceb2f6..42b62d8e6 100644 --- a/utils/devices/devices.go +++ b/utils/devices/devices.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. //go:generate mockgen -destination=../../mocks/mock_utils/mock_devices/mock_devices_client.go github.com/netapp/trident/utils/devices Devices //go:generate mockgen -destination=../../mocks/mock_utils/mock_devices/mock_size_getter_client.go github.com/netapp/trident/utils/devices SizeGetter @@ -80,6 +80,9 @@ type Devices interface { sleep time.Duration) error ClearFormatting(ctx context.Context, devicePath string) error GetMultipathDeviceBySerial(ctx context.Context, hexSerial string) (string, error) + // ExpandMultipathDevice expands a multipath device by resizing all dm-slaves then + // resizing the multipath device-mapper and waiting for all devices sizes to converge. + ExpandMultipathDevice(ctx context.Context, getter models.SCSIDeviceInfoGetter, targetSizeBytes int64) error } type SizeGetter interface { diff --git a/utils/devices/devices_darwin.go b/utils/devices/devices_darwin.go index 8641a1eef..184efba5d 100644 --- a/utils/devices/devices_darwin.go +++ b/utils/devices/devices_darwin.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. // NOTE: This file should only contain functions for handling devices for Darwin flavor @@ -9,6 +9,7 @@ import ( . "github.com/netapp/trident/logging" "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/models" ) // flushOneDevice unused stub function @@ -68,3 +69,11 @@ func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error { defer Logc(ctx).Debug("<<<< devices_darwin.CloseLUKSDevice") return errors.UnsupportedError("CloseLUKSDevice is not supported for darwin") } + +func (c *Client) ExpandMultipathDevice( + ctx context.Context, _ models.SCSIDeviceInfoGetter, _ int64, +) error { + Logc(ctx).Debug(">>>> devices_darwin.ExpandMultipathDevice") + defer Logc(ctx).Debug("<<<< devices_darwin.ExpandMultipathDevice") + return errors.UnsupportedError("ExpandMultipathDevice is not supported for darwin") +} diff --git a/utils/devices/devices_linux.go b/utils/devices/devices_linux.go index d7cf836ab..9f45dcfe5 100644 --- a/utils/devices/devices_linux.go +++ b/utils/devices/devices_linux.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. // NOTE: This file should only contain functions for handling devices for linux flavor @@ -7,6 +7,8 @@ package devices import ( "fmt" "os" + "path/filepath" + "strconv" "strings" "syscall" "time" @@ -20,6 +22,7 @@ import ( "github.com/netapp/trident/utils/errors" execCmd "github.com/netapp/trident/utils/exec" "github.com/netapp/trident/utils/filesystem" + "github.com/netapp/trident/utils/models" ) const ( @@ -27,6 +30,8 @@ const ( luksCloseMaxWaitDuration = 2 * time.Minute luksCloseDeviceSafelyClosedExitCode = 0 luksCloseDeviceAlreadyClosedExitCode = 4 + + scsiDeviceStateRunning = "running" ) var ( @@ -85,6 +90,443 @@ func (c *DiskSizeGetter) GetDiskSize(ctx context.Context, devicePath string) (in return size, nil } +// deviceState gets the state of a SCSI device from the sysfs block SCSI device state file. +// This will not work for non-SCSI devices. +func (c *Client) deviceState(ctx context.Context, deviceName string) (string, error) { + fields := LogFields{"deviceName": deviceName} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.deviceState") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.deviceState") + + deviceName = strings.TrimPrefix(deviceName, DevPrefix) + if deviceName == "" { + return "", fmt.Errorf("device name is empty") + } + + // Read in the device state from the sysfs block SCSI device state file. + // filePath = "/sys/block//device/state" + const sysBlockDeviceStatePath = "/sys/block/%s/device/state" + filePath := filepath.Join(c.chrootPathPrefix, fmt.Sprintf(sysBlockDeviceStatePath, deviceName)) + out, err := c.osFs.ReadFile(filePath) + if err != nil { + return "", fmt.Errorf("failed to read device state for %s: %w", deviceName, err) + } + + return strings.TrimSpace(string(out)), nil +} + +// deviceSize gets the size of a device by name from the Kernel's virtual block device interface. +// It returns the size in bytes of a block device as seen by the kernel. +func (c *Client) deviceSize(ctx context.Context, deviceName string) (int64, error) { + fields := LogFields{"deviceName": deviceName} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.deviceSize") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.deviceSize") + + // Callers may supply the full device path ("/dev/dm-#", "/dev/sdX", etc.) or the device name ("dm-#", "sdX", etc.). + deviceName = strings.TrimPrefix(deviceName, DevPrefix) + if deviceName == "" { + return 0, fmt.Errorf("device name is empty") + } + + const ( + // sysBlockSizePath is the path to the sysfs block size file. + // This file reports the number of 512-byte sectors on the device. + sysBlockSizePath = "/sys/block/%s/size" + // kernelSectorSize is the sector size used by the kernel to express the capacity of a device in /sys/block//size. + // The kernel shouuld express this value in 512-byte units regardless of the device's physical or logical block size. + // This is a long-standing convention relied upon by userspace tools like lsblk, fdisk, and parted. + kernelSectorSize = int64(512) + ) + + // filePath = "/sys/block//size" + filePath := filepath.Join(c.chrootPathPrefix, fmt.Sprintf(sysBlockSizePath, deviceName)) + out, err := c.osFs.ReadFile(filePath) + if err != nil { + return 0, fmt.Errorf("failed to read device size for %s: %w", deviceName, err) + } + + sectorCount, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse device size for %s: %w", deviceName, err) + } + if sectorCount <= 0 { + return 0, fmt.Errorf("unexpected sector count %d for %s", sectorCount, deviceName) + } + + return sectorCount * kernelSectorSize, nil +} + +// isRunning reads a SCSI device state from the sysfs block SCSI device state file and +// returns whether the device is in a "running" state. +func (c *Client) isRunning(ctx context.Context, device string) bool { + fields := LogFields{"device": device} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.isRunning") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.isRunning") + + deviceState, err := c.deviceState(ctx, device) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Debug("Failed to get device state; treating device as unhealthy.") + return false + } + return deviceState == scsiDeviceStateRunning +} + +func (c *Client) discoverUnhealthyDevices(ctx context.Context, devices []string) []string { + fields := LogFields{"devices": devices} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.discoverUnhealthyDevices") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.discoverUnhealthyDevices") + + unhealthyDevices := make([]string, 0) + for _, device := range devices { + // Any non-running state is considered unhealthy. + if !c.isRunning(ctx, device) { + unhealthyDevices = append(unhealthyDevices, device) + } + } + + return unhealthyDevices +} + +// rescanDevice tells the kernel to rescan a single SCSI disk/block device. +// This tells the kernel to re-query the device size by issuing a SCSI 'READ CAPACITY' command. +func (c *Client) rescanDevice(ctx context.Context, deviceName string) error { + fields := LogFields{"deviceName": deviceName} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.rescanDevice") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.rescanDevice") + + // Callers may supply the full device path or the device name. + deviceName = strings.TrimPrefix(deviceName, DevPrefix) + if deviceName == "" { + return fmt.Errorf("device name is empty") + } + + const sysBlockDeviceRescanPath = "/sys/block/%s/device/rescan" + filePath := filepath.Join(c.chrootPathPrefix, fmt.Sprintf(sysBlockDeviceRescanPath, deviceName)) + fields["filepath"] = filePath + + file, err := c.osFs.OpenFile(filePath, os.O_WRONLY, 0) + if err != nil { + Logc(ctx).WithFields(fields).Warning("Could not open file for writing.") + return fmt.Errorf("failed to open file %s: %w", filePath, err) + } + + defer func() { + _ = file.Close() + }() + + written, err := file.WriteString("1") + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Warn("Could not write to file.") + return fmt.Errorf("failed to write to file %s: %w", filePath, err) + } else if written == 0 { + Logc(ctx).WithFields(fields).Warn("Zero bytes written to file.") + return fmt.Errorf("no data written to %s", filePath) + } + + return nil +} + +// rescanUndersizedDevices rescans any of the supplied devices that are smaller than supplied targetSizeBytes. +// It returns nil if no rescans were needed or at least one succeeded and an error if all rescans failed. +func (c *Client) rescanUndersizedDevices(ctx context.Context, devices []string, targetSizeBytes int64) error { + if len(devices) == 0 { + return errors.New("no devices provided to rescan") + } + if targetSizeBytes <= 0 { + return fmt.Errorf("invalid minimum size '%d': value must be greater than 0", targetSizeBytes) + } + fields := LogFields{ + "devices": devices, + "targetSizeBytes": targetSizeBytes, + } + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.rescanUndersizedDevices") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.rescanUndersizedDevices") + + var rescanErrs error + devicesUndersize := make([]string, 0) + devicesRescanned := make([]string, 0) + for _, device := range devices { + sizeBytes, err := c.deviceSize(ctx, device) + if err == nil && sizeBytes >= targetSizeBytes { + continue // Already at target size. + } + + // Read the SCSI device state for every undersized/unreadable device. + // This is cheap (one sysfs read) and provides immediate diagnostics: + devicesUndersize = append(devicesUndersize, device) + + // Rescan the device. Track failures separately from size-read errors. + if scanErr := c.rescanDevice(ctx, device); scanErr != nil { + rescanErrs = errors.Join(rescanErrs, fmt.Errorf("failed to rescan %s: %w", device, scanErr)) + continue + } + + devicesRescanned = append(devicesRescanned, device) + } + + // If no devices are undersized, return early. + if len(devicesUndersize) == 0 { + Logc(ctx).WithFields(fields).Debug("All devices at target size; no rescans needed.") + return nil + } + + // If some devices were undersized, but none were rescanned, return an error. + if len(devicesRescanned) == 0 { + Logc(ctx).WithFields(fields).WithFields(LogFields{ + "undersizedDevices": devicesUndersize, + }).WithError(rescanErrs).Warn("All device rescans failed; connection to storage may be unstable.") + return fmt.Errorf("no devices could be rescanned: %w", rescanErrs) + } + + // Log partial failures so operators can see which devices stayed down, + // even though at least one rescan succeeded and we're returning nil. + if rescanErrs != nil { + Logc(ctx).WithFields(fields).WithFields(LogFields{ + "undersizedDevices": devicesUndersize, + "devicesRescanned": devicesRescanned, + }).WithError(rescanErrs).Warn("Some device rescans failed; connection to storage may be unstable.") + } + + Logc(ctx).WithFields(LogFields{ + "undersizedDevices": devicesUndersize, + "devicesRescanned": devicesRescanned, + }).Debug("Devices rescanned.") + return nil +} + +// resizeMultipathMap issues a multipathd resize map command to the host. +// It sends "resize map " to the multipathd daemon which triggers resize_map(device) +// inside the multipathd daemon. +// If any paths are unhealthy, this command will fail with exit status 1. +func (c *Client) resizeMultipathMap(ctx context.Context, mapperName string) error { + fields := LogFields{"mapperName": mapperName} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.resizeMultipathMap") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.resizeMultipathMap") + + // Use the single-command form of the multipathd CLI: -k. + // This must be a single argv entry so the shell doesn't split it into separate arguments. + // "resize map " → resize_map(device) + const resizeCmd = "-kresize map %s" + resizeDeviceCmd := fmt.Sprintf(resizeCmd, mapperName) + out, err := c.command.ExecuteWithTimeout(ctx, "multipathd", 10*time.Second, true, resizeDeviceCmd) + if err != nil { + Logc(ctx).WithFields(LogFields{ + "output": string(out), + }).WithError(err).Error("Failed to resize multipath map.") + return fmt.Errorf("failed to resize multipath map %s: %w", mapperName, err) + } + + return nil +} + +// getDeviceMapperName reads the name of a device mapper from the sysfs block dm-# device name file. +// Linux utils like multipath-tools and cryptsetup use the device mapper name to identify the device mapper. +func (c *Client) getDeviceMapperName(ctx context.Context, device string) (string, error) { + fields := LogFields{"device": device} + Logc(ctx).WithFields(fields).Trace(">>>> devices_linux.getDeviceMapperName") + defer Logc(ctx).WithFields(fields).Trace("<<<< devices_linux.getDeviceMapperName") + + const sysBlockDMDeviceNamePath = "/sys/block/%s/dm/name" + dmNamePath := filepath.Join(c.chrootPathPrefix, fmt.Sprintf(sysBlockDMDeviceNamePath, device)) + exists, err := PathExists(c.osFs, dmNamePath) + if !exists || err != nil { + return "", errors.NotFoundError("multipath device '%s' name not found", device) + } + + dmNameRaw, err := c.osFs.ReadFile(dmNamePath) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(dmNameRaw)), nil +} + +// ExpandMultipathDevice polls the multipath device for the specified LUN until it reports a size >= targetSizeBytes. +// On each iteration it rescans any undersized SCSI paths and reloads the multipath device map, +// then confirms convergence by requiring consecutive stable size reads on the multipath device. +// It returns nil once convergence is confirmed, or an error if the context deadline is reached first. +// If the context has no deadline, a default timeout is applied to guarantee bounded execution. +func (c *Client) ExpandMultipathDevice( + ctx context.Context, getter models.SCSIDeviceInfoGetter, targetSizeBytes int64, +) error { + if getter == nil { + return errors.New("device info getter is nil") + } + + const ( + requiredStableReads = 3 + stableReadInterval = 3 * time.Second + defaultResizeTimeout = 60 * time.Second + ) + + // Validate the specified minimum size. + if targetSizeBytes <= 0 { + return errors.New("minimum size must be greater than 0") + } + + Logc(ctx).WithFields(LogFields{ + "targetSizeBytes": targetSizeBytes, + }).Debug(">>>> devices_linux.ExpandMultipathDevice") + defer Logc(ctx).Debug("<<<< devices_linux.ExpandMultipathDevice") + + // If the context doesn't have a timeout, add one. + if _, ok := ctx.Deadline(); !ok { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, defaultResizeTimeout) + defer cancel() + } + + var stableReads int + var lastKnownSizeBytes int64 + var lastKnownMpathName string + var lastKnownDeviceInfo *models.ScsiDeviceInfo + timer := time.NewTimer(0) // First attempt fires immediately. + defer timer.Stop() + + // This loop will continue until the context is done or the volume size has converged. + // The size converges when the multipath device size is at or above the target size for a few consecutive reads. + // If the context is done or the volume size has not converged after the default timeout, an error is returned. + for { + select { + case <-ctx.Done(): + return fmt.Errorf( + "multipath device '%s' size '%d' did not reach target size '%d' bytes: %w", + lastKnownMpathName, lastKnownSizeBytes, targetSizeBytes, ctx.Err(), + ) + + case <-timer.C: + // Schedule the next retry; the select will block until it fires or the context is done. + timer.Reset(stableReadInterval) + + // Discover SCSI devices and multipath device for this LUN. + // This must always happen to mitigate race conditions with freshly appearing paths and devices. + deviceInfo, err := getter(ctx) + if err != nil { + Logc(ctx).WithError(err).Warn("Failed to get device information; retrying.") + stableReads = 0 + continue + } else if deviceInfo == nil { + Logc(ctx).Warn("No device information found; retrying.") + stableReads = 0 + continue + } + + // If no multipath device is found, reset the stable read counter and try again. + if deviceInfo.MultipathDevice == "" { + Logc(ctx).Warn("No multipath device found for LUN; retrying.") + stableReads = 0 + continue + } else if len(deviceInfo.Devices) == 0 { + Logc(ctx).Warn("No underlying SCSI devices found for LUN; retrying.") + stableReads = 0 + continue + } + + // Underlying paths and dm-slaves could change between convergence loop iterations due to + // concurrently or late arriving iSCSI sessions, loss of an iSCSI session entirely, etc. + // Track the last known device info so we're always working with the latest. + if lastKnownDeviceInfo == nil { + Logc(ctx).WithField( + "deviceInfo", deviceInfo, + ).Debug("Discovered device info during multipath device expansion.") + lastKnownDeviceInfo = deviceInfo.Copy() + } + + // If any device info has changed reset the stable read counter and try again. + // This can happen due to a multipath dm-# device, new device paths due to + // sessions and paths being added or removed on the host in parallel. + if !lastKnownDeviceInfo.Equal(deviceInfo) { + Logc(ctx).WithFields(LogFields{ + "newDeviceInfo": deviceInfo, + "oldDeviceInfo": lastKnownDeviceInfo, + }).Info("Device info changed during multipath device expansion.") + + stableReads = 0 + lastKnownDeviceInfo = deviceInfo.Copy() + } + // Track this separately for observability. + lastKnownMpathName = deviceInfo.MultipathDevice + + // Discover if there are any unhealthy devices. + // If a single device is unhealthy, do not initiate rescans or resize the multipath map. + unhealthyDevices := c.discoverUnhealthyDevices(ctx, deviceInfo.Devices) + if len(unhealthyDevices) != 0 { + Logc(ctx).WithFields(LogFields{ + "lunID": deviceInfo.LUN, + "multipathDevice": deviceInfo.MultipathDevice, + "allDevices": deviceInfo.Devices, + "unhealthyDevices": unhealthyDevices, + }).Warn("Volume expansion cannot proceed while some devices are unhealthy; connection to storage may be unstable.") + return fmt.Errorf("volume expansion cannot proceed; some devices %v are unhealthy", unhealthyDevices) + } + + // Always rescan undersized dm-slaves. + // This call is idempotent and will only rescan devices that are undersized. + if err := c.rescanUndersizedDevices(ctx, deviceInfo.Devices, targetSizeBytes); err != nil { + Logc(ctx).WithError(err).Warn("Failed to read or rescan devices; retrying.") + stableReads = 0 + continue + } + + // Read the size of the multipath device. + mpathSizeBytes, err := c.deviceSize(ctx, deviceInfo.MultipathDevice) + if err != nil { + Logc(ctx).WithError(err).Warn("Failed to read multipath device size; retrying.") + stableReads = 0 + continue + } + // Track this separately for observability. + lastKnownSizeBytes = mpathSizeBytes + + // Multipath device is undersized — reconfigure the multipath device map. + if mpathSizeBytes < targetSizeBytes { + fields := LogFields{ + "lunID": deviceInfo.LUN, + "multipathDevice": deviceInfo.MultipathDevice, + "deviceSizeBytes": mpathSizeBytes, + "targetSizeBytes": targetSizeBytes, + } + Logc(ctx).WithFields(fields).Debug("Multipath device requires expansion.") + + // Get the device mapper name from the multipath device path. + // "/dev/dm-#" -> "3600a098038314865515d4c5a70644636" + mapperName, err := c.getDeviceMapperName(ctx, deviceInfo.MultipathDevice) + if err != nil { + Logc(ctx).WithFields(fields).WithError(err).Warn("Failed to get device mapper name; retrying.") + stableReads = 0 + continue + } + + // Resize the multipath device map. + if err = c.resizeMultipathMap(ctx, mapperName); err != nil { + Logc(ctx).WithFields(fields).WithError(err).Warn("Failed to resize multipath map; retrying.") + } + + stableReads = 0 + continue + } + + // Multipath device is at or above target size — count a stable read. + stableReads++ + fields := LogFields{ + "stableReads": stableReads, + "lunID": deviceInfo.LUN, + "multipathDevice": deviceInfo.MultipathDevice, + "deviceSizeBytes": mpathSizeBytes, + "targetSizeBytes": targetSizeBytes, + } + + // If stable reads are less than required, continue to the next iteration. Otherwise return success. + if stableReads < requiredStableReads { + Logc(ctx).WithFields(fields).Debug("Multipath device now at target size; re-reading size to confirm size convergence.") + continue + } + + Logc(ctx).WithFields(fields).Info("Multipath device expanded.") + return nil + } + } +} + // VerifyMultipathDeviceSize compares the size of the DM device with the size // of a device to ensure correct DM device has the correct size. func (c *Client) VerifyMultipathDeviceSize( diff --git a/utils/devices/devices_linux_test.go b/utils/devices/devices_linux_test.go index 127764775..bec5542f1 100644 --- a/utils/devices/devices_linux_test.go +++ b/utils/devices/devices_linux_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. // NOTE: This file should only contain functions for handling devices for linux flavor @@ -662,3 +662,1049 @@ func TestFlushDevice(t *testing.T) { ) } } + +func TestClient_deviceState(t *testing.T) { + tests := map[string]struct { + deviceName string + setupFs func(afero.Fs) + expectedState string + assertError assert.ErrorAssertionFunc + }{ + "happy path: running": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + }, + expectedState: "running", + assertError: assert.NoError, + }, + "transport-offline": { + deviceName: "sdc", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sdc/device", 0o755) + afero.WriteFile(fs, "/sys/block/sdc/device/state", []byte("transport-offline\n"), 0o444) + }, + expectedState: "transport-offline", + assertError: assert.NoError, + }, + "blocked": { + deviceName: "sdb", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sdb/device", 0o755) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("blocked\n"), 0o444) + }, + expectedState: "blocked", + assertError: assert.NoError, + }, + "trims whitespace": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte(" running \n"), 0o444) + }, + expectedState: "running", + assertError: assert.NoError, + }, + "state file does not exist": { + deviceName: "sda", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + state, err := client.deviceState(context.Background(), tc.deviceName) + tc.assertError(t, err) + assert.Equal(t, tc.expectedState, state) + }) + } +} + +// --- deviceSize tests --- + +func TestClient_deviceSize(t *testing.T) { + tests := map[string]struct { + deviceName string + setupFs func(afero.Fs) + expectedBytes int64 + assertError assert.ErrorAssertionFunc + }{ + "happy path: 100G": { + deviceName: "dm-0", + setupFs: func(fs afero.Fs) { + // 209715200 sectors * 512 = 107374182400 bytes (100 GiB) + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte("209715200\n"), 0o444) + }, + expectedBytes: 107374182400, + assertError: assert.NoError, + }, + "strips /dev/ prefix": { + deviceName: "/dev/dm-0", + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte("2048\n"), 0o444) + }, + expectedBytes: 2048 * 512, + assertError: assert.NoError, + }, + "empty device name": { + deviceName: "", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "device name is only /dev/": { + deviceName: "/dev/", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "sysfs file does not exist": { + deviceName: "dm-99", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "sysfs file contains non-numeric data": { + deviceName: "dm-0", + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte("not-a-number\n"), 0o444) + }, + assertError: assert.Error, + }, + "sysfs file contains zero sectors": { + deviceName: "dm-0", + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte("0\n"), 0o444) + }, + assertError: assert.Error, + }, + "sysfs file contains negative sectors": { + deviceName: "dm-0", + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte("-100\n"), 0o444) + }, + assertError: assert.Error, + }, + "trims whitespace": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/sda/size", []byte(" 1024 \n"), 0o444) + }, + expectedBytes: 1024 * 512, + assertError: assert.NoError, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + size, err := client.deviceSize(context.Background(), tc.deviceName) + tc.assertError(t, err) + assert.Equal(t, tc.expectedBytes, size) + }) + } +} + +// --- rescanDevice tests --- + +func TestClient_rescanDevice(t *testing.T) { + tests := map[string]struct { + deviceName string + setupFs func(afero.Fs) + assertError assert.ErrorAssertionFunc + verifyFs func(*testing.T, afero.Fs) + }{ + "happy path: writes 1 to rescan file": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + // Create the directory structure and the rescan file. + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/rescan", []byte(""), 0o200) + }, + assertError: assert.NoError, + verifyFs: func(t *testing.T, fs afero.Fs) { + content, err := afero.ReadFile(fs, "/sys/block/sda/device/rescan") + assert.NoError(t, err) + assert.Equal(t, "1", string(content)) + }, + }, + "strips /dev/ prefix": { + deviceName: "/dev/sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/rescan", []byte(""), 0o200) + }, + assertError: assert.NoError, + verifyFs: func(t *testing.T, fs afero.Fs) { + content, err := afero.ReadFile(fs, "/sys/block/sda/device/rescan") + assert.NoError(t, err) + assert.Equal(t, "1", string(content)) + }, + }, + "empty device name": { + deviceName: "", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "device name is only /dev/": { + deviceName: "/dev/", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "rescan file does not exist": { + deviceName: "sdb", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + err := client.rescanDevice(context.Background(), tc.deviceName) + tc.assertError(t, err) + if tc.verifyFs != nil { + tc.verifyFs(t, fs) + } + }) + } +} + +// --- rescanUndersizedDevices tests --- + +func TestClient_rescanUndersizedDevices(t *testing.T) { + const targetSizeBytes = int64(107374182400) // 100 GiB + + tests := map[string]struct { + devices []string + targetSizeBytes int64 + setupFs func(afero.Fs) + assertError assert.ErrorAssertionFunc + }{ + "nil devices": { + devices: nil, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "zero target size": { + devices: []string{"sda"}, + targetSizeBytes: 0, + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "negative target size": { + devices: []string{"sda"}, + targetSizeBytes: -1, + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "all devices already at target size": { + devices: []string{"sda", "sdb"}, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) { + // 209715200 sectors * 512 = 107374182400 bytes = targetSizeBytes + afero.WriteFile(fs, "/sys/block/sda/size", []byte("209715200\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sdb/size", []byte("209715200\n"), 0o444) + }, + assertError: assert.NoError, + }, + "one device undersized, rescan succeeds": { + devices: []string{"sda", "sdb"}, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/sda/size", []byte("209715200\n"), 0o444) + // sdb undersized with rescan file and state file + afero.WriteFile(fs, "/sys/block/sdb/size", []byte("1024\n"), 0o444) + fs.MkdirAll("/sys/block/sdb/device", 0o755) + afero.WriteFile(fs, "/sys/block/sdb/device/rescan", []byte(""), 0o200) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("running\n"), 0o444) + }, + assertError: assert.NoError, + }, + "all devices undersized, all rescans fail": { + devices: []string{"sda"}, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) { + // sda undersized, but no rescan file → rescan will fail + afero.WriteFile(fs, "/sys/block/sda/size", []byte("1024\n"), 0o444) + }, + assertError: assert.Error, + }, + "empty devices list": { + devices: []string{}, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + "device size unreadable but rescan succeeds": { + devices: []string{"sda", "sdb"}, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) { + // sda: no size file → treated as undersized, rescan file present + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/rescan", []byte(""), 0o200) + // sdb: at target size + afero.WriteFile(fs, "/sys/block/sdb/size", []byte("209715200\n"), 0o444) + }, + assertError: assert.NoError, + }, + "partial failure: some rescans succeed, some fail": { + devices: []string{"sda", "sdb"}, + targetSizeBytes: targetSizeBytes, + setupFs: func(fs afero.Fs) { + // sda: undersized, rescan file present → succeeds + afero.WriteFile(fs, "/sys/block/sda/size", []byte("1024\n"), 0o444) + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/rescan", []byte(""), 0o200) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + // sdb: undersized, no rescan file → fails + afero.WriteFile(fs, "/sys/block/sdb/size", []byte("1024\n"), 0o444) + fs.MkdirAll("/sys/block/sdb/device", 0o755) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("transport-offline\n"), 0o444) + }, + assertError: assert.NoError, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + err := client.rescanUndersizedDevices(context.Background(), tc.devices, tc.targetSizeBytes) + tc.assertError(t, err) + }) + } +} + +// --- isRunning tests --- + +func TestClient_isRunning(t *testing.T) { + tests := map[string]struct { + deviceName string + setupFs func(afero.Fs) + expected bool + }{ + "running — healthy": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + }, + expected: true, + }, + "blocked — unhealthy": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("blocked\n"), 0o444) + }, + expected: false, + }, + "transport-offline — unhealthy": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("transport-offline\n"), 0o444) + }, + expected: false, + }, + "offline — unhealthy": { + deviceName: "sda", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("offline\n"), 0o444) + }, + expected: false, + }, + "state file missing — treated as unhealthy": { + deviceName: "sda", + setupFs: func(fs afero.Fs) {}, + expected: false, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + client := &Client{osFs: afero.Afero{Fs: fs}} + result := client.isRunning(context.Background(), tc.deviceName) + assert.Equal(t, tc.expected, result) + }) + } +} + +// --- discoverUnhealthyDevices tests --- + +func TestClient_discoverUnhealthyDevices(t *testing.T) { + tests := map[string]struct { + devices []string + setupFs func(afero.Fs) + expected []string + }{ + "all devices healthy": { + devices: []string{"sda", "sdb"}, + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("running\n"), 0o444) + }, + expected: []string{}, + }, + "all devices unhealthy": { + devices: []string{"sda", "sdb"}, + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("blocked\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("transport-offline\n"), 0o444) + }, + expected: []string{"sda", "sdb"}, + }, + "mixed healthy and unhealthy": { + devices: []string{"sda", "sdb"}, + setupFs: func(fs afero.Fs) { + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("blocked\n"), 0o444) + }, + expected: []string{"sdb"}, + }, + "state file missing — treated as unhealthy": { + devices: []string{"sda"}, + setupFs: func(fs afero.Fs) {}, + // No state file → isRunning returns false. + expected: []string{"sda"}, + }, + "empty device list": { + devices: []string{}, + setupFs: func(fs afero.Fs) {}, + expected: []string{}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + client := &Client{osFs: afero.Afero{Fs: fs}} + result := client.discoverUnhealthyDevices(context.Background(), tc.devices) + assert.Equal(t, tc.expected, result) + }) + } +} + +// --- resizeMultipathMap tests --- + +func TestClient_resizeMultipathMap(t *testing.T) { + const mapperName = "3600a098038314865515d4c5a70644636" + + tests := map[string]struct { + mockSetup func(*mockexec.MockCommand) + assertError assert.ErrorAssertionFunc + }{ + "resize succeeds": { + mockSetup: func(m *mockexec.MockCommand) { + m.EXPECT().ExecuteWithTimeout( + gomock.Any(), "multipathd", 10*time.Second, true, + "-kresize map "+mapperName, + ).Return([]byte("ok\n"), nil) + }, + assertError: assert.NoError, + }, + "exec error": { + mockSetup: func(m *mockexec.MockCommand) { + m.EXPECT().ExecuteWithTimeout( + gomock.Any(), "multipathd", 10*time.Second, true, + "-kresize map "+mapperName, + ).Return([]byte(""), errors.New("exec failed")) + }, + assertError: assert.Error, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + tc.mockSetup(mockCmd) + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: afero.NewMemMapFs()}, + } + + err := client.resizeMultipathMap(context.Background(), mapperName) + tc.assertError(t, err) + }) + } +} + +// --- getDeviceMapperName tests --- + +func TestClient_getDeviceMapperName(t *testing.T) { + tests := map[string]struct { + device string + setupFs func(afero.Fs) + expectedName string + assertError assert.ErrorAssertionFunc + }{ + "happy path": { + device: "dm-0", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte("3600a098038314865515d4c5a70644636\n"), 0o444) + }, + expectedName: "3600a098038314865515d4c5a70644636", + assertError: assert.NoError, + }, + "trims whitespace": { + device: "dm-0", + setupFs: func(fs afero.Fs) { + fs.MkdirAll("/sys/block/dm-0/dm", 0o755) + afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(" mapname \n"), 0o444) + }, + expectedName: "mapname", + assertError: assert.NoError, + }, + "dm/name file does not exist": { + device: "dm-0", + setupFs: func(fs afero.Fs) {}, + assertError: assert.Error, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := afero.NewMemMapFs() + tc.setupFs(fs) + client := &Client{osFs: afero.Afero{Fs: fs}} + result, err := client.getDeviceMapperName(context.Background(), tc.device) + tc.assertError(t, err) + assert.Equal(t, tc.expectedName, result) + }) + } +} + +// --- ExpandMultipathDevice tests --- + +func TestClient_ExpandMultipathDevice(t *testing.T) { + const ( + targetSizeBytes = int64(107374182400) // 100 GiB + sectorCount = "209715200" // 100 GiB in 512-byte sectors + smallSectors = "1024" // much smaller than target + ) + + makeDeviceInfo := func(mpathDevice string, devices []string) *models.ScsiDeviceInfo { + return &models.ScsiDeviceInfo{ + ScsiDeviceAddress: models.ScsiDeviceAddress{LUN: "1"}, + MultipathDevice: mpathDevice, + Devices: devices, + } + } + + t.Run("nil getter", func(t *testing.T) { + client := &Client{} + err := client.ExpandMultipathDevice(context.Background(), nil, targetSizeBytes) + assert.Error(t, err) + }) + + t.Run("target size zero", func(t *testing.T) { + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + client := &Client{} + err := client.ExpandMultipathDevice(context.Background(), getter, 0) + assert.Error(t, err) + }) + + t.Run("target size negative", func(t *testing.T) { + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + client := &Client{} + err := client.ExpandMultipathDevice(context.Background(), getter, -100) + assert.Error(t, err) + }) + + t.Run("already at target size converges quickly", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + fs := afero.NewMemMapFs() + + // All sizes at target. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(sectorCount+"\n"), 0o444) + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: fs}, + } + + // Needs >6s: 3 stable reads × 3s interval, first fires immediately. + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.NoError(t, err) + }) + + t.Run("converges after resize", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + fs := afero.NewMemMapFs() + + const mapperName = "3600a098038314865515d4c5a70644636" + + // Path device undersized initially. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(smallSectors+"\n"), 0o444) + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/rescan", []byte(""), 0o200) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + // Multipath device undersized initially; dm/name needed for resizeMultipathMap. + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(smallSectors+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(mapperName+"\n"), 0o444) + + resizeCount := 0 + mockCmd.EXPECT().ExecuteWithTimeout( + gomock.Any(), "multipathd", 10*time.Second, true, "-kresize map "+mapperName, + ).DoAndReturn(func(_ context.Context, _ string, _ time.Duration, _ bool, _ ...string) ([]byte, error) { + resizeCount++ + // After first resize, update the sizes to target. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(sectorCount+"\n"), 0o444) + return []byte("ok\n"), nil + }).AnyTimes() + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: fs}, + } + + // Needs >9s: resize fires at t=0, then 3 stable reads × 3s = 9s total. + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.NoError(t, err) + assert.GreaterOrEqual(t, resizeCount, 1, "should have called multipathd resize at least once") + }) + + t.Run("getter always fails — times out", func(t *testing.T) { + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return nil, errors.New("discovery failed") + } + + client := &Client{ + osFs: afero.Afero{Fs: afero.NewMemMapFs()}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("getter returns nil device info — times out", func(t *testing.T) { + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return nil, nil + } + + client := &Client{ + osFs: afero.Afero{Fs: afero.NewMemMapFs()}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("no SCSI devices found for LUN — times out", func(t *testing.T) { + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{}), nil + } + + client := &Client{ + osFs: afero.Afero{Fs: afero.NewMemMapFs()}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("no multipath device — times out", func(t *testing.T) { + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("", []string{"sda"}), nil + } + + client := &Client{ + osFs: afero.Afero{Fs: afero.NewMemMapFs()}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("multipath device size unreadable — times out", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + fs := afero.NewMemMapFs() + + // Path device OK, but no dm-0 size file. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("device info change resets stable reads", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + fs := afero.NewMemMapFs() + + // All devices at target size from the start. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sdb/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("running\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(sectorCount+"\n"), 0o444) + + callCount := 0 + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + callCount++ + if callCount <= 2 { + // First two calls: single path. + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + // After that: a new path appears (e.g. late-arriving iSCSI session). + return makeDeviceInfo("dm-0", []string{"sda", "sdb"}), nil + } + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: fs}, + } + + // Needs >12s: info changes at call 3 (t=6s), resetting stable reads. + // Convergence then requires 3 more reads: t=6s, t=9s, t=12s. + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.NoError(t, err) + // The info change on call 3 resets stable reads, so convergence takes more than 3 getter calls. + assert.Greater(t, callCount, 3) + }) + + t.Run("resize never fixes size — times out", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + fs := afero.NewMemMapFs() + + const mapperName = "3600a098038314865515d4c5a70644636" + + // Path and multipath devices permanently undersized. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(smallSectors+"\n"), 0o444) + fs.MkdirAll("/sys/block/sda/device", 0o755) + afero.WriteFile(fs, "/sys/block/sda/device/rescan", []byte(""), 0o200) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(smallSectors+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(mapperName+"\n"), 0o444) + + // Resize succeeds but never actually grows the device size. + mockCmd.EXPECT().ExecuteWithTimeout( + gomock.Any(), "multipathd", 10*time.Second, true, "-kresize map "+mapperName, + ).Return([]byte("ok\n"), nil).AnyTimes() + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("all paths unhealthy — immediate error", func(t *testing.T) { + fs := afero.NewMemMapFs() + + // sda has no state file → isRunning returns false → unhealthy. + fs.MkdirAll("/sys/block/sda/device", 0o755) + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + // Should be an immediate error, not a context deadline. + assert.NotErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("some paths unhealthy — immediate error", func(t *testing.T) { + fs := afero.NewMemMapFs() + + // sda: healthy, at target size. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + + // sdb: unhealthy (blocked). + afero.WriteFile(fs, "/sys/block/sdb/device/state", []byte("blocked\n"), 0o444) + + // dm-0 at target size. + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(sectorCount+"\n"), 0o444) + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda", "sdb"}), nil + } + + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + // Any unhealthy path must produce an immediate hard error, not a timeout. + assert.NotErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("rescanUndersizedDevices fails — retries — times out", func(t *testing.T) { + fs := afero.NewMemMapFs() + + // sda: undersized, state = running, but NO rescan file → rescanDevice fails. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(smallSectors+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + // (no /sys/block/sda/device/rescan file) + + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(smallSectors+"\n"), 0o444) + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("getDeviceMapperName fails — retries — times out", func(t *testing.T) { + fs := afero.NewMemMapFs() + + // sda at target size — no rescan needed. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + + // dm-0 undersized but NO dm/name file → getDeviceMapperName returns NotFoundError. + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(smallSectors+"\n"), 0o444) + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + + t.Run("resizeMultipathMap fails — warns and retries — times out", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockCmd := mockexec.NewMockCommand(ctrl) + fs := afero.NewMemMapFs() + + const mapperName = "3600a098038314865515d4c5a70644636" + + // sda at target size — no rescan needed. + afero.WriteFile(fs, "/sys/block/sda/size", []byte(sectorCount+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/sda/device/state", []byte("running\n"), 0o444) + + // dm-0 undersized; dm/name present so getDeviceMapperName succeeds. + afero.WriteFile(fs, "/sys/block/dm-0/size", []byte(smallSectors+"\n"), 0o444) + afero.WriteFile(fs, "/sys/block/dm-0/dm/name", []byte(mapperName+"\n"), 0o444) + + // Resize always fails. + mockCmd.EXPECT().ExecuteWithTimeout( + gomock.Any(), "multipathd", 10*time.Second, true, "-kresize map "+mapperName, + ).Return(nil, fmt.Errorf("multipathd: resize failed")).AnyTimes() + + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return makeDeviceInfo("dm-0", []string{"sda"}), nil + } + + client := &Client{ + command: mockCmd, + osFs: afero.Afero{Fs: fs}, + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := client.ExpandMultipathDevice(ctx, getter, targetSizeBytes) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) +} + +// TestVerifyMultipathDeviceSerial covers the serial-number-based multipath device verification path, +// which calls GetDeviceMapperUUID and is therefore Linux-only. +func TestVerifyMultipathDeviceSerial(t *testing.T) { + tests := map[string]struct { + getFs func() afero.Fs + publishInfo *models.VolumePublishInfo + deviceInfo *models.ScsiDeviceInfo + expectError bool + }{ + "Ghost Device": { + publishInfo: &models.VolumePublishInfo{ + DevicePath: "", + VolumeAccessInfo: models.VolumeAccessInfo{ + IscsiAccessInfo: models.IscsiAccessInfo{ + IscsiLunSerial: "yocwB?Wl7x2l", + }, + }, + }, + deviceInfo: &models.ScsiDeviceInfo{ + MultipathDevice: "/dev/dm-0", + DevicePaths: []string{"/dev/sda"}, + }, + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Defines LUN serial matching the published serial, so the serial path is taken. + afero.WriteFile(fs, "/dev/sda/vpd_pg80", []byte{ + 0, 128, 0, 12, 121, 111, 99, 119, 66, 63, 87, 108, + 55, 120, 50, 108, + }, 0o755) + // UUID file for dm-1 contains the hex encoding of the serial. + afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("mpath-3600a0980796f6377423f576c3778326c"), 0o755) + // Non-empty slaves dir — not a ghost device in the traditional sense, + // but the test verifies that VerifyMultipathDevice succeeds. + fs.Mkdir("/sys/block/dm-1/slaves/sdb", 0o755) + return fs + }, + expectError: false, + }, + "Not Ghost Device": { + publishInfo: &models.VolumePublishInfo{ + DevicePath: "", + VolumeAccessInfo: models.VolumeAccessInfo{ + IscsiAccessInfo: models.IscsiAccessInfo{ + IscsiLunSerial: "yocwB?Wl7x2l", + }, + }, + }, + deviceInfo: &models.ScsiDeviceInfo{ + MultipathDevice: "/dev/dm-0", + DevicePaths: []string{"/dev/sda"}, + }, + getFs: func() afero.Fs { + fs := afero.NewMemMapFs() + afero.WriteFile(fs, "/dev/sda/vpd_pg80", []byte{ + 0, 128, 0, 12, 121, 111, 99, 119, 66, 63, 87, 108, + 55, 120, 50, 108, + }, 0o755) + afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("mpath-3600a0980796f6377423f576c3778326c"), 0o755) + // Empty slaves dir — this is a ghost device (no paths attached). + fs.Mkdir("/sys/block/dm-1/slaves/", 0o755) + return fs + }, + expectError: false, + }, + } + + for name, params := range tests { + t.Run(name, func(t *testing.T) { + deviceClient := NewDetailed(nil, params.getFs(), nil) + _, err := deviceClient.VerifyMultipathDevice(context.TODO(), params.publishInfo, + nil, params.deviceInfo) + if params.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/utils/devices/devices_test.go b/utils/devices/devices_test.go index 3f2d3db17..c41ffd875 100644 --- a/utils/devices/devices_test.go +++ b/utils/devices/devices_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package devices @@ -498,7 +498,7 @@ func TestVerifyMultipathDevice(t *testing.T) { }, expectError: false, }, - "CompareWithPublishedSerialNumber GetMultipathDeviceUUID Error": { + "CompareWithPublishedSerialNumber GetDeviceMapperUUID Error": { publishInfo: &models.VolumePublishInfo{ DevicePath: "", VolumeAccessInfo: models.VolumeAccessInfo{ @@ -565,57 +565,6 @@ func TestVerifyMultipathDevice(t *testing.T) { }, expectError: true, }, - "CompareWithPublishedSerialNumber Ghost Device": { - publishInfo: &models.VolumePublishInfo{ - DevicePath: "", - VolumeAccessInfo: models.VolumeAccessInfo{ - IscsiAccessInfo: models.IscsiAccessInfo{ - IscsiLunSerial: "yocwB?Wl7x2l", - }, - }, - }, - deviceInfo: &models.ScsiDeviceInfo{ - MultipathDevice: "/dev/dm-0", - DevicePaths: []string{"/dev/sda"}, - }, - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - // Defines LUN serial - afero.WriteFile(fs, "/dev/sda/vpd_pg80", []byte{ - 0, 128, 0, 12, 121, 111, 99, 119, 66, 63, 87, 108, - 55, 120, 50, 108, - }, 0o755) - afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("mpath-3600a0980796f6377423f576c3778326c"), 0o755) - fs.Mkdir("/sys/block/dm-1/slaves/sdb", 0o755) - return fs - }, - expectError: false, - }, - "CompareWithPublishedSerialNumber Not Ghost Device": { - publishInfo: &models.VolumePublishInfo{ - DevicePath: "", - VolumeAccessInfo: models.VolumeAccessInfo{ - IscsiAccessInfo: models.IscsiAccessInfo{ - IscsiLunSerial: "yocwB?Wl7x2l", - }, - }, - }, - deviceInfo: &models.ScsiDeviceInfo{ - MultipathDevice: "/dev/dm-0", - DevicePaths: []string{"/dev/sda"}, - }, - getFs: func() afero.Fs { - fs := afero.NewMemMapFs() - afero.WriteFile(fs, "/dev/sda/vpd_pg80", []byte{ - 0, 128, 0, 12, 121, 111, 99, 119, 66, 63, 87, 108, - 55, 120, 50, 108, - }, 0o755) - afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", []byte("mpath-3600a0980796f6377423f576c3778326c"), 0o755) - fs.Mkdir("/sys/block/dm-1/slaves/", 0o755) - return fs - }, - expectError: false, - }, "CompareWithAllPublishInfos Happy Path": { publishInfo: &models.VolumePublishInfo{ DevicePath: "", diff --git a/utils/devices/devices_windows.go b/utils/devices/devices_windows.go index 974c082fd..bbe14593f 100644 --- a/utils/devices/devices_windows.go +++ b/utils/devices/devices_windows.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. // NOTE: This file should only contain functions for handling devices for windows flavor @@ -9,6 +9,7 @@ import ( . "github.com/netapp/trident/logging" "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/models" ) // FlushOneDevice unused stub function @@ -54,7 +55,7 @@ func (c *Client) EnsureLUKSDeviceClosed(ctx context.Context, luksDevicePath stri func (c *Client) GetDeviceFSType(ctx context.Context, device string) (string, error) { Logc(ctx).Debug(">>>> devices_windows.GetDeviceFSType") defer Logc(ctx).Debug("<<<< devices_windows.GetDeviceFSType") - return "", errors.UnsupportedError("GetDeviceFSTypeis not supported for windows") + return "", errors.UnsupportedError("GetDeviceFSType is not supported for windows") } func (c *Client) EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx context.Context, luksDevicePath string) error { @@ -68,3 +69,11 @@ func (c *Client) CloseLUKSDevice(ctx context.Context, devicePath string) error { defer Logc(ctx).Debug("<<<< devices_windows.CloseLUKSDevice") return errors.UnsupportedError("CloseLUKSDevice is not supported for windows") } + +func (c *Client) ExpandMultipathDevice( + ctx context.Context, _ models.SCSIDeviceInfoGetter, _ int64, +) error { + Logc(ctx).Debug(">>>> devices_windows.ExpandMultipathDevice") + defer Logc(ctx).Debug("<<<< devices_windows.ExpandMultipathDevice") + return errors.UnsupportedError("ExpandMultipathDevice is not supported for windows") +} diff --git a/utils/iscsi/iscsi.go b/utils/iscsi/iscsi.go index b1110058c..4b29e1dde 100644 --- a/utils/iscsi/iscsi.go +++ b/utils/iscsi/iscsi.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package iscsi @@ -95,7 +95,7 @@ type ISCSI interface { volID, sessionNumber string, reasonInvalid models.PortalInvalid, ) PreChecks(ctx context.Context) error - RescanDevices(ctx context.Context, targetIQN string, lunID int32, minSize int64) error + ExpandVolume(ctx context.Context, publishInfo *models.VolumePublishInfo, targetSizeBytes int64) error IsAlreadyAttached(ctx context.Context, lunID int, targetIqn string) bool RemoveLUNFromSessions(ctx context.Context, publishInfo *models.VolumePublishInfo, sessions *models.ISCSISessions) RemovePortalsFromSession(ctx context.Context, publishInfo *models.VolumePublishInfo, sessions *models.ISCSISessions) @@ -597,212 +597,72 @@ func (client *Client) AddSession( } } -// filterDevicesBySize builds a map of disk devices to their size, filtered by a minimum size requirement. -// If any errors occur when checking the size of a device, it captures the error and moves onto the next device. -func (client *Client) filterDevicesBySize( - ctx context.Context, deviceInfo *models.ScsiDeviceInfo, minSize int64, -) (map[string]int64, error) { - var errs error - deviceSizeMap := make(map[string]int64, 0) - for _, diskDevice := range deviceInfo.Devices { - size, err := client.devices.GetDiskSize(ctx, devices.DevPrefix+diskDevice) - if err != nil { - errs = errors.Join(errs, err) - // Only consider devices whose size can be gathered. - continue - } - - if size < minSize { - // Only consider devices that are undersized. - deviceSizeMap[diskDevice] = size - } - } - - if errs != nil { - return nil, errs - } - return deviceSizeMap, nil -} - -// rescanDevices accepts a map of disk devices to sizes and initiates a rescan for each device. -// If any rescan fails it captures the error and moves onto the next rescanning the next device. -func (client *Client) rescanDevices(ctx context.Context, deviceSizeMap map[string]int64) error { - var errs error - for diskDevice := range deviceSizeMap { - if err := client.rescanDisk(ctx, diskDevice); err != nil { - errs = errors.Join(errs, fmt.Errorf("failed to rescan disk %s: %s", diskDevice, err)) - } - } - - if errs != nil { - return errs - } - return nil -} - -func (client *Client) RescanDevices(ctx context.Context, targetIQN string, lunID int32, minSize int64) error { - GenerateRequestContextForLayer(ctx, LogLayerUtils) - - fields := LogFields{"targetIQN": targetIQN, "lunID": lunID} - Logc(ctx).WithFields(fields).Debug(">>>> iscsi.RescanDevices") - defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.RescanDevices") - - hostSessionMap := client.iscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIQN) +// IsAlreadyAttached checks if there is already an established iSCSI session to the specified LUN. +func (client *Client) IsAlreadyAttached(ctx context.Context, lunID int, targetIqn string) bool { + hostSessionMap := client.iscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIqn) if len(hostSessionMap) == 0 { - return fmt.Errorf("error getting iSCSI device information: no host session found") - } - deviceInfo, err := client.GetDeviceInfoForLUN(ctx, hostSessionMap, int(lunID), targetIQN, false) - if err != nil { - return fmt.Errorf("error getting iSCSI device information: %s", err) - } - - // Get all disk devices that require a rescan. - devicesBySize, err := client.filterDevicesBySize(ctx, deviceInfo, minSize) - if err != nil { - Logc(ctx).WithError(err).Error("Failed to read disk size for devices.") - return err - } - - if len(devicesBySize) != 0 { - fields = LogFields{ - "lunID": lunID, - "devices": devicesBySize, - "minSize": minSize, - } - - Logc(ctx).WithFields(fields).Debug("Found devices that require a rescan.") - if err := client.rescanDevices(ctx, devicesBySize); err != nil { - Logc(ctx).WithError(err).Error("Failed to initiate rescanning for devices.") - return err - } - - // Sleep for a second to give the SCSI subsystem time to rescan the devices. - time.Sleep(time.Second) - - // Reread the devices to check if any are undersized. - devicesBySize, err = client.filterDevicesBySize(ctx, deviceInfo, minSize) - if err != nil { - Logc(ctx).WithError(err).Error("Failed to read disk size for devices after rescan.") - return err - } - - if len(devicesBySize) != 0 { - Logc(ctx).WithFields(fields).Error("Some devices are still undersized after rescan.") - return errors.New("devices are still undersized after rescan") - } + return false } - if deviceInfo.MultipathDevice != "" { - multipathDevice := deviceInfo.MultipathDevice - size, err := client.devices.GetDiskSize(ctx, devices.DevPrefix+multipathDevice) - if err != nil { - return err - } - - fields = LogFields{"size": size, "minSize": minSize} - if size < minSize { - Logc(ctx).WithFields(fields).Debug("Reloading the multipath device.") - if err := client.reloadMultipathDevice(ctx, multipathDevice); err != nil { - return err - } - time.Sleep(time.Second) - - size, err := client.devices.GetDiskSize(ctx, devices.DevPrefix+multipathDevice) - if err != nil { - return err - } + paths := client.iscsiUtils.GetSysfsBlockDirsForLUN(lunID, hostSessionMap) - if size < minSize { - Logc(ctx).Error("Multipath device not large enough after resize.") - return fmt.Errorf("multipath device not large enough after resize: %d < %d", size, minSize) - } - } else { - Logc(ctx).WithFields(fields).Debug("Not reloading the multipath device because the size is greater than or equal to the minimum size.") - } + devices, err := client.iscsiUtils.GetDevicesForLUN(paths) + if nil != err { + return false } - return nil + // return true even if a single device exists + return 0 < len(devices) } -// rescanDisk causes the kernel to rescan a single iSCSI disk/block device. -// This is how size changes are found when expanding a volume. -func (client *Client) rescanDisk(ctx context.Context, deviceName string) error { - fields := LogFields{"deviceName": deviceName} - Logc(ctx).WithFields(fields).Debug(">>>> iscsi.rescanDisk") - defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.rescanDisk") - - client.devices.ListAllDevices(ctx) - filename := fmt.Sprintf(client.chrootPathPrefix+"/sys/block/%s/device/rescan", deviceName) - Logc(ctx).WithField("filename", filename).Debug("Opening file for writing.") - - f, err := client.os.OpenFile(filename, os.O_WRONLY, 0) - if err != nil { - Logc(ctx).WithField("file", filename).Warning("Could not open file for writing.") - return err +// ExpandVolume extracts the LUN ID and target IQN from the publish info and delegates to ExpandMultipathDevice +// to ensure the host-side multipath device reflects the expected size. This is a public entry point +// for CSI node volume expansion; the actual convergence logic lives in ExpandMultipathDevice. +func (client *Client) ExpandVolume( + ctx context.Context, publishInfo *models.VolumePublishInfo, targetSizeBytes int64, +) error { + if publishInfo == nil { + return errors.New("nil publish info") } - - defer func() { - _ = f.Close() - }() - - written, err := f.WriteString("1") - if err != nil { - Logc(ctx).WithFields(LogFields{ - "file": filename, - "error": err, - }).Warning("Could not write to file.") - return err - } else if written == 0 { - Logc(ctx).WithField("file", filename).Warning("Zero bytes written to file.") - return fmt.Errorf("no data written to %s", filename) + if targetSizeBytes <= 0 { + return errors.New("target size must be greater than 0") } - client.devices.ListAllDevices(ctx) - return nil -} - -func (client *Client) reloadMultipathDevice(ctx context.Context, multipathDevice string) error { - fields := LogFields{"multipathDevice": multipathDevice} - Logc(ctx).WithFields(fields).Debug(">>>> iscsi.reloadMultipathDevice") - defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.reloadMultipathDevice") - - if multipathDevice == "" { - return errors.New("cannot reload an empty multipathDevice") + lunID := int(publishInfo.IscsiLunNumber) + targetIQN := publishInfo.IscsiTargetIQN + fields := LogFields{ + "lunID": lunID, + "targetIQN": targetIQN, + "targetSizeBytes": targetSizeBytes, } + Logc(ctx).WithFields(fields).Debug(">>>> iscsi.ExpandVolume") + defer Logc(ctx).WithFields(fields).Debug("<<<< iscsi.ExpandVolume") - _, err := client.command.ExecuteWithTimeout(ctx, "multipath", 10*time.Second, true, "-r", - devices.DevPrefix+multipathDevice) - if err != nil { - Logc(ctx).WithFields(LogFields{ - "device": multipathDevice, - "error": err, - }).Error("Failed to reload multipathDevice.") - return fmt.Errorf("failed to reload multipathDevice %s: %s", multipathDevice, err) + getter := func(ctx context.Context) (*models.ScsiDeviceInfo, error) { + return client.getDeviceInfoForLUNAndTarget(ctx, lunID, targetIQN) } - - Logc(ctx).WithFields(fields).Debug("Multipath device reloaded.") - return nil + return client.devices.ExpandMultipathDevice(ctx, getter, targetSizeBytes) } -// IsAlreadyAttached checks if there is already an established iSCSI session to the specified LUN. -func (client *Client) IsAlreadyAttached(ctx context.Context, lunID int, targetIqn string) bool { - hostSessionMap := client.iscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIqn) +func (client *Client) getDeviceInfoForLUNAndTarget( + ctx context.Context, lunID int, targetIQN string, +) (*models.ScsiDeviceInfo, error) { + hostSessionMap := client.iscsiUtils.GetISCSIHostSessionMapForTarget(ctx, targetIQN) if len(hostSessionMap) == 0 { - return false + return nil, fmt.Errorf("error getting iSCSI device information: no host session found") } - paths := client.iscsiUtils.GetSysfsBlockDirsForLUN(lunID, hostSessionMap) - - devices, err := client.iscsiUtils.GetDevicesForLUN(paths) - if nil != err { - return false + deviceInfo, err := client.GetDeviceInfoForLUN(ctx, hostSessionMap, lunID, targetIQN, false) + if err != nil { + return nil, err + } else if deviceInfo == nil { + return nil, fmt.Errorf("failed to get device information from host") } - // return true even if a single device exists - return 0 < len(devices) + return deviceInfo, nil } -// getDeviceInfoForLUN finds iSCSI devices using /dev/disk/by-path values. This method should be +// GetDeviceInfoForLUN finds iSCSI devices using /dev/disk/by-path values. This method should be // called after calling waitForDeviceScan so that the device paths are known to exist. func (client *Client) GetDeviceInfoForLUN( ctx context.Context, hostSessionMap map[int]int, lunID int, iSCSINodeName string, needFSType bool, @@ -975,7 +835,7 @@ func (client *Client) waitForMultipathDeviceForDevices(ctx context.Context, devi return "", errors.New("multipath device not found when it is expected") } else { - Logc(ctx).WithField("multipathDevice", multipathDevice).Debug("Multipath device found.") + Logc(ctx).WithField("multipathDevice", multipathDevice).Info("Multipath device found.") } return multipathDevice, nil diff --git a/utils/iscsi/iscsi_linux_test.go b/utils/iscsi/iscsi_linux_test.go index 8b7eb3659..57eafd33b 100644 --- a/utils/iscsi/iscsi_linux_test.go +++ b/utils/iscsi/iscsi_linux_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package iscsi diff --git a/utils/iscsi/iscsi_test.go b/utils/iscsi/iscsi_test.go index 8756a6ba6..e06c1be1a 100644 --- a/utils/iscsi/iscsi_test.go +++ b/utils/iscsi/iscsi_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package iscsi @@ -2205,605 +2205,6 @@ func TestClient_AddSession(t *testing.T) { } } -func TestClient_filterDevicesBySize(t *testing.T) { - mockCtrl := gomock.NewController(t) - mockDevices := mock_devices.NewMockDevices(mockCtrl) - - client := NewDetailed( - "", - nil, - nil, - nil, - mockDevices, - nil, - nil, - nil, - afero.Afero{}, - nil, - ) - - ctx := context.TODO() - deviceInfo := &models.ScsiDeviceInfo{ - Devices: []string{"sda", "sdb"}, - } - minSize := int64(10) - - // Negative case. - mockDevices.EXPECT().GetDiskSize(ctx, "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(ctx, "/dev/sdb").Return(int64(0), errors.New("failed to open disk")) - deviceSizeMap, err := client.filterDevicesBySize(ctx, deviceInfo, minSize) - assert.Error(t, err) - assert.Nil(t, deviceSizeMap) - assert.NotEqual(t, len(deviceInfo.Devices), len(deviceSizeMap)) - - // Positive case #1: Only one device needs a resize. - mockDevices.EXPECT().GetDiskSize(ctx, "/dev/sda").Return(minSize, nil) - mockDevices.EXPECT().GetDiskSize(ctx, "/dev/sdb").Return(int64(1), nil) - deviceSizeMap, err = client.filterDevicesBySize(ctx, deviceInfo, minSize) - assert.NoError(t, err) - assert.NotNil(t, deviceSizeMap) - assert.NotEqual(t, len(deviceInfo.Devices), len(deviceSizeMap)) - - // Positive case #2: All devices need to resize. - mockDevices.EXPECT().GetDiskSize(ctx, "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(ctx, "/dev/sdb").Return(int64(1), nil) - deviceSizeMap, err = client.filterDevicesBySize(ctx, deviceInfo, minSize) - assert.NoError(t, err) - assert.NotNil(t, deviceSizeMap) - assert.Equal(t, len(deviceInfo.Devices), len(deviceSizeMap)) -} - -func TestClient_rescanDevices(t *testing.T) { - mockCtrl := gomock.NewController(t) - mockDevices := mock_devices.NewMockDevices(mockCtrl) - - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - client := NewDetailed( - "", - nil, - nil, - nil, - mockDevices, - nil, - nil, - nil, - afero.Afero{Fs: fs}, - nil, - ) - - ctx := context.TODO() - deviceSizeMap := map[string]int64{ - "sda": 1, - "sdb": 1, - } - - // Should fail because a device path does not exist. - mockDevices.EXPECT().ListAllDevices(ctx).AnyTimes() - err = client.rescanDevices(ctx, deviceSizeMap) - assert.Error(t, err) - - // Add the missing device path. - _, err = fs.Create("/sys/block/sdb/device/rescan") - assert.NoError(t, err) - - // Should succeed now that the device path exists. - err = client.rescanDevices(ctx, deviceSizeMap) - assert.NoError(t, err) -} - -func TestClient_RescanDevices(t *testing.T) { - type parameters struct { - targetIQN string - lunID int32 - minSize int64 - - getReconcileUtils func(controller *gomock.Controller) IscsiReconcileUtils - getDeviceClient func(controller *gomock.Controller) devices.Devices - getCommandClient func(controller *gomock.Controller) tridentexec.Command - getFileSystemUtils func() afero.Fs - assertError assert.ErrorAssertionFunc - } - - const targetIQN = "iqn.2010-01.com.netapp:target-1" - - tests := map[string]parameters{ - "error getting device information": { - targetIQN: targetIQN, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - return NewReconcileUtils() - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - return fs - }, - assertError: assert.Error, - }, - "error getting iscsi disk size": { - targetIQN: targetIQN, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), errors.New("some error")) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - return fs - }, - assertError: assert.Error, - }, - "failed to rescan disk": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(1) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - return fs - }, - assertError: assert.Error, - }, - "failure to resize the disk": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil).Times(2) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - return fs - }, - assertError: assert.Error, - }, - "error validating if disk is resized": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), errors.New("some error")) - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - return fs - }, - assertError: assert.Error, - }, - "disk resized successfully": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) - - // This will be called twice because we read from each disk twice during an expansion. - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil).Times(1) - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil).Times(1) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - return fs - }, - assertError: assert.NoError, - }, - "failure getting multipath device size": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), errors.New("some error")) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/sys/block/sda/holders/dm-0") - assert.NoError(t, err) - return fs - }, - assertError: assert.Error, - }, - "multipath device size already greater than min size": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/sys/block/sda/holders/dm-0") - assert.NoError(t, err) - return fs - }, - assertError: assert.NoError, - }, - "failure reloading multipaths map": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipath", 10*time.Second, true, "-r", - "/dev/dm-0").Return(nil, errors.New("some error")) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/sys/block/sda/holders/dm-0") - assert.NoError(t, err) - return fs - }, - assertError: assert.Error, - }, - "error determining the size of the multipath device after reload": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), errors.New("some error")) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipath", 10*time.Second, true, "-r", - "/dev/dm-0").Return(nil, nil) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/sys/block/sda/holders/dm-0") - assert.NoError(t, err) - return fs - }, - assertError: assert.Error, - }, - "multipath device too small even after reloading multipath map": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil).Times(2) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0") - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipath", 10*time.Second, true, "-r", - "/dev/dm-0").Return(nil, nil) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/sys/block/sda/holders/dm-0") - assert.NoError(t, err) - return fs - }, - assertError: assert.Error, - }, - "multipath device successfully resized": { - targetIQN: targetIQN, - minSize: 1, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(1), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(0), nil) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(1), nil) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipath", 10*time.Second, true, "-r", - "/dev/dm-0").Return(nil, nil) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - - _, err = fs.Create("/sys/block/sda/holders/dm-0") - assert.NoError(t, err) - return fs - }, - assertError: assert.NoError, - }, - "happy path": { - minSize: 10, - targetIQN: targetIQN, - getReconcileUtils: func(controller *gomock.Controller) IscsiReconcileUtils { - mockReconcileUtils := mock_iscsi.NewMockIscsiReconcileUtils(controller) - mockReconcileUtils.EXPECT().GetISCSIHostSessionMapForTarget(context.TODO(), - targetIQN).Return(map[int]int{0: 0}) - mockReconcileUtils.EXPECT().GetSysfsBlockDirsForLUN(0, gomock.Any()).Return([]string{"/dev/sda"}) - mockReconcileUtils.EXPECT().GetDevicesForLUN([]string{"/dev/sda"}).Return([]string{"sda"}, nil) - return mockReconcileUtils - }, - getDeviceClient: func(controller *gomock.Controller) devices.Devices { - mockDevices := mock_devices.NewMockDevices(controller) - mockDevices.EXPECT().FindMultipathDeviceForDevice(context.TODO(), "sda").Return("dm-0").Times(1) - - // This will be called twice because we read from each disk twice during an expansion. - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(0), nil).Times(1) - mockDevices.EXPECT().ListAllDevices(context.TODO()).Times(2) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/sda").Return(int64(10), nil).Times(1) - mockDevices.EXPECT().GetDiskSize(context.TODO(), "/dev/dm-0").Return(int64(10), nil) - return mockDevices - }, - getCommandClient: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - getFileSystemUtils: func() afero.Fs { - fs := afero.NewMemMapFs() - _, err := fs.Create("/sys/block/sda/device/rescan") - assert.NoError(t, err) - return fs - }, - assertError: assert.NoError, - }, - } - - for name, params := range tests { - t.Run(name, func(t *testing.T) { - controller := gomock.NewController(t) - - client := NewDetailed("", params.getCommandClient(controller), DefaultSelfHealingExclusion, nil, - params.getDeviceClient(controller), nil, nil, params.getReconcileUtils(controller), - afero.Afero{Fs: params.getFileSystemUtils()}, nil) - - err := client.RescanDevices(context.TODO(), params.targetIQN, params.lunID, params.minSize) - if params.assertError != nil { - params.assertError(t, err) - } - }) - } -} - -func TestClient_reloadMultipathDevice(t *testing.T) { - type parameters struct { - multipathDeviceName string - getCommand func(controller *gomock.Controller) tridentexec.Command - assertError assert.ErrorAssertionFunc - } - - const multipathDeviceName = "dm-0" - const moultipathDevicePath = "/dev/" + multipathDeviceName - - tests := map[string]parameters{ - "no multipath device provided": { - multipathDeviceName: "", - getCommand: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - return mockCommand - }, - assertError: assert.Error, - }, - "error executing multipath map reload": { - multipathDeviceName: multipathDeviceName, - getCommand: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipath", 10*time.Second, true, "-r", - moultipathDevicePath).Return(nil, errors.New("some error")) - return mockCommand - }, - assertError: assert.Error, - }, - "happy path": { - multipathDeviceName: multipathDeviceName, - getCommand: func(controller *gomock.Controller) tridentexec.Command { - mockCommand := mockexec.NewMockCommand(controller) - mockCommand.EXPECT().ExecuteWithTimeout(context.TODO(), "multipath", 10*time.Second, true, "-r", - moultipathDevicePath).Return(nil, nil) - return mockCommand - }, - assertError: assert.NoError, - }, - } - - for name, params := range tests { - t.Run(name, func(t *testing.T) { - ctrl := gomock.NewController(t) - client := NewDetailed("", params.getCommand(ctrl), nil, nil, nil, nil, nil, nil, afero.Afero{}, nil) - - err := client.reloadMultipathDevice(context.TODO(), params.multipathDeviceName) - if params.assertError != nil { - params.assertError(t, err) - } - }) - } -} - func TestClient_IsAlreadyAttached(t *testing.T) { type parameters struct { lunID int diff --git a/utils/models/types.go b/utils/models/types.go index 8249ccefb..9bf496979 100644 --- a/utils/models/types.go +++ b/utils/models/types.go @@ -1,8 +1,9 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package models import ( + "context" "fmt" "strings" "time" @@ -11,6 +12,7 @@ import ( "github.com/netapp/trident/internal/crypto" . "github.com/netapp/trident/logging" + "github.com/netapp/trident/pkg/collection" "github.com/netapp/trident/pkg/convert" "github.com/netapp/trident/pkg/network" "github.com/netapp/trident/utils/errors" @@ -87,7 +89,73 @@ type ScsiDeviceInfo struct { CHAPInfo IscsiChapInfo } -// +func (s *ScsiDeviceInfo) String() string { + fields := []string{ + fmt.Sprintf("address: %s", s.ScsiDeviceAddress.String()), + fmt.Sprintf("devices: [%s]", strings.Join(s.Devices, ", ")), + fmt.Sprintf("devicePaths: [%s]", strings.Join(s.DevicePaths, ", ")), + fmt.Sprintf("multipathDevice: %s", s.MultipathDevice), + fmt.Sprintf("filesystem: %s", s.Filesystem), + fmt.Sprintf("IQN: %s", s.IQN), + fmt.Sprintf("WWNN: %s", s.WWNN), + fmt.Sprintf("sessionNumber: %d", s.SessionNumber), + fmt.Sprintf("chapInfo: %s", s.CHAPInfo.String()), + } + return strings.Join(fields, ", ") +} + +func (s *ScsiDeviceInfo) Copy() *ScsiDeviceInfo { + devices := make([]string, len(s.Devices)) + copy(devices, s.Devices) + + paths := make([]string, len(s.DevicePaths)) + copy(paths, s.DevicePaths) + + return &ScsiDeviceInfo{ + ScsiDeviceAddress: s.ScsiDeviceAddress, + Devices: devices, + DevicePaths: paths, + MultipathDevice: s.MultipathDevice, + Filesystem: s.Filesystem, + IQN: s.IQN, + WWNN: s.WWNN, + SessionNumber: s.SessionNumber, + CHAPInfo: s.CHAPInfo, + } +} + +func (s *ScsiDeviceInfo) Equal(other *ScsiDeviceInfo) bool { + if s.ScsiDeviceAddress != other.ScsiDeviceAddress { + return false + } + if s.MultipathDevice != other.MultipathDevice { + return false + } + if s.Filesystem != other.Filesystem { + return false + } + if s.IQN != other.IQN { + return false + } + if s.WWNN != other.WWNN { + return false + } + if s.SessionNumber != other.SessionNumber { + return false + } + if s.CHAPInfo != other.CHAPInfo { + return false + } + if !collection.EqualValues(s.Devices, other.Devices) { + return false + } + if !collection.EqualValues(s.DevicePaths, other.DevicePaths) { + return false + } + return true +} + +type SCSIDeviceInfoGetter func(ctx context.Context) (*ScsiDeviceInfo, error) // ScsiDeviceAddress is a data structure for representing a SCSI device address type ScsiDeviceAddress struct { @@ -97,6 +165,10 @@ type ScsiDeviceAddress struct { LUN string } +func (s ScsiDeviceAddress) String() string { + return fmt.Sprintf("%s:%s:%s:%s", s.Host, s.Channel, s.Target, s.LUN) +} + const ( ScanAllSCSIDeviceAddress = "-" ScanSCSIDeviceAddressZero = "0" diff --git a/utils/models/types_test.go b/utils/models/types_test.go index fdd1d0160..a0ad84104 100644 --- a/utils/models/types_test.go +++ b/utils/models/types_test.go @@ -100,6 +100,349 @@ func TestNodeConstructExternal(t *testing.T) { assert.True(t, reflect.DeepEqual(expectedNode, result), "External node does not match.") } +func TestScsiDeviceAddress_String(t *testing.T) { + tests := map[string]struct { + address ScsiDeviceAddress + expected string + }{ + "fully populated": { + address: ScsiDeviceAddress{ + Host: "1", + Channel: "0", + Target: "0", + LUN: "0", + }, + expected: "1:0:0:0", + }, + "zero value": { + address: ScsiDeviceAddress{}, + expected: ":::", + }, + "multi-digit values": { + address: ScsiDeviceAddress{ + Host: "12", + Channel: "0", + Target: "3", + LUN: "42", + }, + expected: "12:0:3:42", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.expected, tc.address.String()) + }) + } +} + +func TestScsiDeviceInfo_String(t *testing.T) { + t.Run("fully populated struct returns non-empty string", func(t *testing.T) { + info := &ScsiDeviceInfo{ + ScsiDeviceAddress: ScsiDeviceAddress{ + Host: "1", + Channel: "0", + Target: "0", + LUN: "0", + }, + Devices: []string{"sda", "sdb"}, + DevicePaths: []string{"/dev/sda", "/dev/sdb"}, + MultipathDevice: "dm-0", + Filesystem: "ext4", + IQN: "iqn.2010-01.com.netapp:target", + WWNN: "0x5000cca", + SessionNumber: 3, + } + result := info.String() + assert.NotEmpty(t, result) + }) + + t.Run("zero value struct returns non-empty string", func(t *testing.T) { + info := &ScsiDeviceInfo{} + result := info.String() + assert.NotEmpty(t, result) + }) +} + +func TestScsiDeviceInfo_Copy(t *testing.T) { + t.Run("all fields are copied", func(t *testing.T) { + original := &ScsiDeviceInfo{ + ScsiDeviceAddress: ScsiDeviceAddress{ + Host: "1", + Channel: "0", + Target: "0", + LUN: "0", + }, + Devices: []string{"sda", "sdb"}, + DevicePaths: []string{"/dev/sda", "/dev/sdb"}, + MultipathDevice: "dm-0", + Filesystem: "ext4", + IQN: "iqn.2010-01.com.netapp:target", + WWNN: "0x5000cca", + SessionNumber: 3, + CHAPInfo: IscsiChapInfo{ + UseCHAP: true, + IscsiUsername: "user", + IscsiInitiatorSecret: "secret", + IscsiTargetUsername: "tuser", + IscsiTargetSecret: "tsecret", + }, + } + + copied := original.Copy() + + assert.NotNil(t, copied) + assert.True(t, original.Equal(copied)) + assert.Equal(t, original.ScsiDeviceAddress, copied.ScsiDeviceAddress) + assert.Equal(t, original.MultipathDevice, copied.MultipathDevice) + assert.Equal(t, original.Filesystem, copied.Filesystem) + assert.Equal(t, original.IQN, copied.IQN) + assert.Equal(t, original.WWNN, copied.WWNN) + assert.Equal(t, original.SessionNumber, copied.SessionNumber) + assert.Equal(t, original.CHAPInfo, copied.CHAPInfo) + assert.Equal(t, original.Devices, copied.Devices) + assert.Equal(t, original.DevicePaths, copied.DevicePaths) + }) + + t.Run("copy is not the same pointer", func(t *testing.T) { + original := &ScsiDeviceInfo{ + Devices: []string{"sda"}, + DevicePaths: []string{"/dev/sda"}, + } + + copied := original.Copy() + assert.False(t, original == copied) + }) + + t.Run("modifying copy slices does not affect original", func(t *testing.T) { + original := &ScsiDeviceInfo{ + ScsiDeviceAddress: ScsiDeviceAddress{Host: "1", Channel: "0", Target: "0", LUN: "0"}, + Devices: []string{"sda", "sdb"}, + DevicePaths: []string{"/dev/sda", "/dev/sdb"}, + MultipathDevice: "dm-0", + } + + copied := original.Copy() + + // Mutate the copy's slices. + copied.Devices[0] = "sdc" + copied.DevicePaths[0] = "/dev/sdc" + copied.MultipathDevice = "dm-1" + + // Original should be unaffected. + assert.Equal(t, "sda", original.Devices[0]) + assert.Equal(t, "/dev/sda", original.DevicePaths[0]) + assert.Equal(t, "dm-0", original.MultipathDevice) + }) + + t.Run("copy with empty slices", func(t *testing.T) { + original := &ScsiDeviceInfo{ + Devices: []string{}, + DevicePaths: []string{}, + } + + copied := original.Copy() + assert.NotNil(t, copied) + assert.Empty(t, copied.Devices) + assert.Empty(t, copied.DevicePaths) + }) + + t.Run("copy with nil slices", func(t *testing.T) { + original := &ScsiDeviceInfo{} + copied := original.Copy() + assert.NotNil(t, copied) + assert.Empty(t, copied.Devices) + assert.Empty(t, copied.DevicePaths) + }) +} + +func TestScsiDeviceInfo_Equal(t *testing.T) { + base := func() *ScsiDeviceInfo { + return &ScsiDeviceInfo{ + ScsiDeviceAddress: ScsiDeviceAddress{Host: "1", Channel: "0", Target: "0", LUN: "0"}, + Devices: []string{"sda", "sdb"}, + DevicePaths: []string{"/dev/sda", "/dev/sdb"}, + MultipathDevice: "dm-0", + Filesystem: "ext4", + IQN: "iqn.test", + WWNN: "0x5000", + SessionNumber: 1, + CHAPInfo: IscsiChapInfo{UseCHAP: true, IscsiUsername: "user"}, + } + } + + tests := map[string]struct { + a *ScsiDeviceInfo + b *ScsiDeviceInfo + equal bool + }{ + "identical structs": { + a: base(), + b: base(), + equal: true, + }, + "devices in different order": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.Devices = []string{"sdb", "sda"} + return s + }(), + equal: true, + }, + "device paths in different order": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.DevicePaths = []string{"/dev/sdb", "/dev/sda"} + return s + }(), + equal: true, + }, + "different address": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.ScsiDeviceAddress.Host = "2" + return s + }(), + equal: false, + }, + "different multipath device": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.MultipathDevice = "dm-1" + return s + }(), + equal: false, + }, + "different filesystem": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.Filesystem = "xfs" + return s + }(), + equal: false, + }, + "different IQN": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.IQN = "iqn.other" + return s + }(), + equal: false, + }, + "different WWNN": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.WWNN = "0x6000" + return s + }(), + equal: false, + }, + "different session number": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.SessionNumber = 99 + return s + }(), + equal: false, + }, + "different CHAP info": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.CHAPInfo = IscsiChapInfo{UseCHAP: false} + return s + }(), + equal: false, + }, + "extra device in one": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.Devices = append(s.Devices, "sdc") + return s + }(), + equal: false, + }, + "extra device path in one": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.DevicePaths = append(s.DevicePaths, "/dev/sdc") + return s + }(), + equal: false, + }, + "one has empty devices other has populated": { + a: base(), + b: func() *ScsiDeviceInfo { + s := base() + s.Devices = []string{} + return s + }(), + equal: false, + }, + "both have empty slices": { + a: &ScsiDeviceInfo{ + Devices: []string{}, + DevicePaths: []string{}, + }, + b: &ScsiDeviceInfo{ + Devices: []string{}, + DevicePaths: []string{}, + }, + equal: true, + }, + "nil slices equal empty slices": { + a: &ScsiDeviceInfo{ + Devices: nil, + DevicePaths: nil, + }, + b: &ScsiDeviceInfo{ + Devices: []string{}, + DevicePaths: []string{}, + }, + equal: true, + }, + "both have nil slices": { + a: &ScsiDeviceInfo{}, + b: &ScsiDeviceInfo{}, + equal: true, + }, + "copy equals original": { + a: func() *ScsiDeviceInfo { + return &ScsiDeviceInfo{ + ScsiDeviceAddress: ScsiDeviceAddress{Host: "1", Channel: "0", Target: "0", LUN: "0"}, + Devices: []string{"sda"}, + MultipathDevice: "dm-0", + } + }(), + b: func() *ScsiDeviceInfo { + s := &ScsiDeviceInfo{ + ScsiDeviceAddress: ScsiDeviceAddress{Host: "1", Channel: "0", Target: "0", LUN: "0"}, + Devices: []string{"sda"}, + MultipathDevice: "dm-0", + } + return s.Copy() + }(), + equal: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.equal, tc.a.Equal(tc.b)) + }) + } +} + func TestISCSIAction(t *testing.T) { assert.Equal(t, NoAction.String(), "no action", "String output mismatch") assert.Equal(t, Scan.String(), "LUN scanning", "String output mismatch") From 79445cfc947d0cb7c545265055b4c536aef7cac1 Mon Sep 17 00:00:00 2001 From: Clinton Knight Date: Thu, 12 Mar 2026 16:39:56 -0700 Subject: [PATCH 24/30] Allow clone across storage classes if backend is common Co-authored-by: VinayKumarHavanur <54576364+VinayKumarHavanur@users.noreply.github.com> --- core/concurrent_core.go | 19 +- core/concurrent_core_test.go | 165 ++++++++++++++++ core/orchestrator_core.go | 22 ++- core/orchestrator_core_test.go | 182 ++++++++++++++++++ .../controller_helpers/kubernetes/helper.go | 14 -- 5 files changed, 369 insertions(+), 33 deletions(-) diff --git a/core/concurrent_core.go b/core/concurrent_core.go index 9b326bc23..89848f267 100644 --- a/core/concurrent_core.go +++ b/core/concurrent_core.go @@ -2349,10 +2349,16 @@ func (o *ConcurrentTridentOrchestrator) cloneVolume( return nil, errors.NotFoundError("backend for source volume %s not found", volConfig.CloneSourceVolume) } - // Check if the storage class of source and clone volume is different, only if the orchestrator is not in Docker plugin mode. In Docker plugin mode, the storage class of source and clone volume will be different at times. + // Check if the source volume's backend is honored by the target storage class, only if the orchestrator + // is not in Docker plugin mode. In Docker plugin mode, the storage class of source and clone volume + // will be different at times. if !isDockerPluginMode() && volConfig.StorageClass != sourceVolume.Config.StorageClass { - return nil, errors.MismatchedStorageClassError("clone volume %s from source volume %s with "+ - "different storage classes is not allowed", volConfig.Name, volConfig.CloneSourceVolume) + poolMap := o.GetStorageClassPoolMap() + if !poolMap.BackendMatchesStorageClass(ctx, backend.Name(), volConfig.StorageClass) { + return nil, errors.MismatchedStorageClassError("clone volume %s from source volume %s with "+ + "different storage classes that have no common backends is not allowed", + volConfig.Name, volConfig.CloneSourceVolume) + } } if volConfig.Size != "" { @@ -2539,13 +2545,6 @@ func (o *ConcurrentTridentOrchestrator) cloneVolumeRetry( Logc(ctx).WithFields(logFields).Debug("Cloning volume.") - // Check if the storage class of source and clone volume is different, only if the orchestrator is not in Docker - // plugin mode. In Docker plugin mode, the storage class of source and clone volume will be different at times. - if !isDockerPluginMode() && cloneConfig.StorageClass != sourceVolConfig.StorageClass { - return nil, errors.MismatchedStorageClassError("clone volume %s from source volume %s "+ - "with different storage classes is not allowed", cloneConfig.Name, cloneConfig.CloneSourceVolume) - } - // Create the volume createVolume := func() error { if volume, err = backend.CloneVolume(ctx, sourceVolConfig, cloneConfig, pool, false); err != nil { diff --git a/core/concurrent_core_test.go b/core/concurrent_core_test.go index b4981237f..20984fe65 100644 --- a/core/concurrent_core_test.go +++ b/core/concurrent_core_test.go @@ -5785,6 +5785,171 @@ func TestCloneVolumeConcurrentCore(t *testing.T) { assert.Nil(t, volume) }, }, + { + name: "CloneVolumeFromDifferentStorageClassSameBackendSuccess", + bootstrapErr: nil, + setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) { + mockBackend := getMockBackend(mockCtrl, "testBackend", "backend-uuid") + + fakePool := storage.NewStoragePool(nil, "pool1") + fakePool.AddStorageClass("sc-source") + fakePool.AddStorageClass("sc-dest") + fakePool.SetBackend(mockBackend) + + mockBackend.EXPECT().StoragePools().Return( + func() *sync.Map { + m := sync.Map{} + m.Store("pool1", fakePool) + return &m + }(), + ).AnyTimes() + mockBackend.EXPECT().CreatePrepare(gomock.Any(), gomock.Any(), gomock.Any()) + mockBackend.EXPECT().CloneVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), false).Return( + &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "cloneVolume", + Name: "cloneVolume", + Size: "1073741824", + VolumeMode: config.Filesystem, + CloneSourceVolume: "sourceVolume", + CloneSourceVolumeInternal: "sourceVolume", + StorageClass: "sc-dest", + }, + BackendUUID: "backend-uuid", + }, nil).Times(1) + + sourceVolumeDiffSC := &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "sourceVolume", + Name: "sourceVolume", + Size: "1073741824", + VolumeMode: config.Filesystem, + StorageClass: "sc-source", + }, + BackendUUID: "backend-uuid", + } + + scSource := storageclass.New(&storageclass.Config{ + Name: "sc-source", + AdditionalPools: map[string][]string{"testBackend": {"pool1"}}, + }) + scDest := storageclass.New(&storageclass.Config{ + Name: "sc-dest", + AdditionalPools: map[string][]string{"testBackend": {"pool1"}}, + }) + + addBackendsToCache(t, mockBackend) + addVolumesToCache(t, sourceVolumeDiffSC) + addStorageClassesToCache(t, scSource, scDest) + + o.RebuildStorageClassPoolMap(testCtx) + + mockStoreClient.EXPECT().GetVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockStoreClient.EXPECT().AddVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockStoreClient.EXPECT().UpdateVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockStoreClient.EXPECT().DeleteVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockStoreClient.EXPECT().AddVolume(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + volumeConfig: &storage.VolumeConfig{ + InternalName: "cloneVolume", + Name: "cloneVolume", + Size: "1073741824", + VolumeMode: config.Filesystem, + CloneSourceVolume: "sourceVolume", + CloneSourceVolumeInternal: "sourceVolume", + StorageClass: "sc-dest", + }, + verifyError: func(err error) { + assert.NoError(t, err) + }, + verifyResult: func(result *storage.VolumeExternal) { + require.NotNil(t, result) + assert.Equal(t, "cloneVolume", result.Config.Name) + + // Additionally verify the volume is added to the cache + volume := getVolumeByNameFromCache(t, "cloneVolume") + assert.NotNil(t, volume) + }, + }, + { + name: "CloneVolumeFromDifferentStorageClassDifferentBackendFailed", + bootstrapErr: nil, + setupMocks: func(mockCtrl *gomock.Controller, mockStoreClient *mockpersistentstore.MockStoreClient, o *ConcurrentTridentOrchestrator) { + mockBackend1 := getMockBackend(mockCtrl, "testBackend1", "backend-uuid-1") + mockBackend2 := getMockBackend(mockCtrl, "testBackend2", "backend-uuid-2") + + fakePool1 := storage.NewStoragePool(nil, "pool1") + fakePool1.AddStorageClass("sc-source") + fakePool1.SetBackend(mockBackend1) + + fakePool2 := storage.NewStoragePool(nil, "pool2") + fakePool2.AddStorageClass("sc-dest") + fakePool2.SetBackend(mockBackend2) + + mockBackend1.EXPECT().StoragePools().Return( + func() *sync.Map { + m := sync.Map{} + m.Store("pool1", fakePool1) + return &m + }(), + ).AnyTimes() + mockBackend2.EXPECT().StoragePools().Return( + func() *sync.Map { + m := sync.Map{} + m.Store("pool2", fakePool2) + return &m + }(), + ).AnyTimes() + + sourceVolumeDiffBackend := &storage.Volume{ + Config: &storage.VolumeConfig{ + InternalName: "sourceVolume", + Name: "sourceVolume", + Size: "1073741824", + VolumeMode: config.Filesystem, + StorageClass: "sc-source", + }, + BackendUUID: "backend-uuid-1", + } + + scSource := storageclass.New(&storageclass.Config{ + Name: "sc-source", + AdditionalPools: map[string][]string{"testBackend1": {"pool1"}}, + }) + scDest := storageclass.New(&storageclass.Config{ + Name: "sc-dest", + AdditionalPools: map[string][]string{"testBackend2": {"pool2"}}, + }) + + addBackendsToCache(t, mockBackend1, mockBackend2) + addVolumesToCache(t, sourceVolumeDiffBackend) + addStorageClassesToCache(t, scSource, scDest) + + o.RebuildStorageClassPoolMap(testCtx) + + mockStoreClient.EXPECT().GetVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes() + mockStoreClient.EXPECT().AddVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil).Times(1) + mockStoreClient.EXPECT().DeleteVolumeTransaction(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + volumeConfig: &storage.VolumeConfig{ + InternalName: "cloneVolume", + Name: "cloneVolume", + Size: "1073741824", + VolumeMode: config.Filesystem, + CloneSourceVolume: "sourceVolume", + CloneSourceVolumeInternal: "sourceVolume", + StorageClass: "sc-dest", + }, + verifyError: func(err error) { + assert.True(t, errors.IsMismatchedStorageClassError(err)) + }, + verifyResult: func(result *storage.VolumeExternal) { + require.Nil(t, result) + // Additionally verify the volume is not added to the cache + volume := getVolumeByNameFromCache(t, "cloneVolume") + assert.Nil(t, volume) + }, + }, } for _, tt := range tests { diff --git a/core/orchestrator_core.go b/core/orchestrator_core.go index 3050930c8..1f63a291b 100644 --- a/core/orchestrator_core.go +++ b/core/orchestrator_core.go @@ -2374,10 +2374,20 @@ func (o *TridentOrchestrator) cloneVolumeInitial( return nil, errors.NotFoundError("source volume not found: %s", volumeConfig.CloneSourceVolume) } - // Check if the storage class of source and clone volume is different, only if the orchestrator is not in Docker plugin mode. In Docker plugin mode, the storage class of source and clone volume will be different. + // Check if the source volume's backend is honored by the target storage class, only if the orchestrator + // is not in Docker plugin mode. In Docker plugin mode, the storage class of source and clone volume + // will be different. if !isDockerPluginMode() && volumeConfig.StorageClass != sourceVolume.Config.StorageClass { - return nil, errors.MismatchedStorageClassError("clone volume %s from source volume %s with"+ - " different storage classes is not allowed", volumeConfig.Name, volumeConfig.CloneSourceVolume) + srcBackend, srcBackendFound := o.backends[sourceVolume.BackendUUID] + dstSC, dstSCFound := o.storageClasses[volumeConfig.StorageClass] + // If the source backend is not found in the cache, or the destination storage class is not found in the cache, + // or the destination storage class is not added to the source backend, + // then return an error as cloning across different storage classes that have no common backends is not allowed. + if !srcBackendFound || !dstSCFound || !dstSC.IsAddedToBackend(srcBackend, volumeConfig.StorageClass) { + return nil, errors.MismatchedStorageClassError("clone volume %s from source volume %s with"+ + " different storage classes that have no common backends is not allowed", + volumeConfig.Name, volumeConfig.CloneSourceVolume) + } } Logc(ctx).WithFields(LogFields{ @@ -2615,12 +2625,6 @@ func (o *TridentOrchestrator) cloneVolumeRetry( return nil, errors.NotFoundError("source volume not found: %s", cloneConfig.CloneSourceVolume) } - // Check if the storage class of source and clone volume is different, only if the orchestrator is not in Docker plugin mode. In Docker plugin mode, the storage class of source and clone volume will be different at times. - if !isDockerPluginMode() && cloneConfig.StorageClass != sourceVolume.Config.StorageClass { - return nil, errors.MismatchedStorageClassError("clone volume %s from source volume %s with "+ - "different storage classes is not allowed", cloneConfig.Name, cloneConfig.CloneSourceVolume) - } - backend, found = o.backends[txn.VolumeCreatingConfig.BackendUUID] if !found { // Should never get here but just to be safe diff --git a/core/orchestrator_core_test.go b/core/orchestrator_core_test.go index cf6078896..3f92ed632 100644 --- a/core/orchestrator_core_test.go +++ b/core/orchestrator_core_test.go @@ -1619,6 +1619,188 @@ func TestCloneVolumeWithMismatchedStorageClass(t *testing.T) { cleanup(t, orchestrator) } +func TestCloneVolumeWithDifferentStorageClassSameBackend(t *testing.T) { + ctx := context.Background() + mockPools := tu.GetFakePools() + orchestrator := getOrchestrator(t, false) + + // Add a backend with a pool that satisfies both storage classes + backendConfig, err := fakedriver.NewFakeStorageDriverConfigJSON( + "fast-backend", + config.File, + map[string]*fake.StoragePool{ + tu.FastSmall: mockPools[tu.FastSmall], + }, + []fake.Volume{}, + ) + if err != nil { + t.Fatalf("Unable to generate backend config JSON: %v", err) + } + _, err = orchestrator.AddBackend(ctx, backendConfig, "") + if err != nil { + t.Fatalf("Unable to add backend: %v", err) + } + + // Add two storage classes that both match the same backend via AdditionalPools + storageClasses := []storageClassTest{ + { + config: &storageclass.Config{ + Name: "sc-source", + AdditionalPools: map[string][]string{"fast-backend": {tu.FastSmall}}, + }, + expected: []*tu.PoolMatch{{Backend: "fast-backend", Pool: tu.FastSmall}}, + }, + { + config: &storageclass.Config{ + Name: "sc-dest", + AdditionalPools: map[string][]string{"fast-backend": {tu.FastSmall}}, + }, + expected: []*tu.PoolMatch{{Backend: "fast-backend", Pool: tu.FastSmall}}, + }, + } + for _, sc := range storageClasses { + if _, err := orchestrator.AddStorageClass(ctx, sc.config); err != nil { + t.Fatalf("Unable to add storage class %s: %v", sc.config.Name, err) + } + } + + // Create source volume with sc-source + sourceConfig := tu.GenerateVolumeConfig("source-vol", 1, "sc-source", config.File) + _, err = orchestrator.AddVolume(ctx, sourceConfig) + if err != nil { + t.Fatalf("Unable to add source volume: %v", err) + } + + // Clone with a different storage class (sc-dest) that shares the same backend + cloneConfig := &storage.VolumeConfig{ + Name: "clone-vol", + StorageClass: "sc-dest", + CloneSourceVolume: "source-vol", + VolumeMode: config.Filesystem, + } + cloneResult, err := orchestrator.CloneVolume(ctx, cloneConfig) + // Verify success - different SC but same backend should be allowed + if err != nil { + t.Errorf("Expected clone to succeed with different storage class on same backend, got error: %v", err) + } + if cloneResult == nil { + t.Fatal("Expected clone result, got nil") + } + + // Verify clone was created and resides on the same backend + orchestrator.mutex.Lock() + sourceVol, found := orchestrator.volumes["source-vol"] + if !found { + t.Fatal("Source volume not found in cache") + } + cloneVol, found := orchestrator.volumes["clone-vol"] + if !found { + t.Fatal("Clone volume not found in cache") + } + if cloneVol.BackendUUID != sourceVol.BackendUUID { + t.Errorf("Clone placed on unexpected backend: %s (expected %s)", cloneVol.BackendUUID, sourceVol.BackendUUID) + } + orchestrator.mutex.Unlock() + + cleanup(t, orchestrator) +} + +func TestCloneVolumeWithDifferentStorageClassDifferentBackend(t *testing.T) { + ctx := context.Background() + mockPools := tu.GetFakePools() + orchestrator := getOrchestrator(t, false) + + // Add first backend (fast) with a fast pool + backendConfig1, err := fakedriver.NewFakeStorageDriverConfigJSON( + "fast-backend", + config.File, + map[string]*fake.StoragePool{ + tu.FastSmall: mockPools[tu.FastSmall], + }, + []fake.Volume{}, + ) + if err != nil { + t.Fatalf("Unable to generate backend config JSON: %v", err) + } + _, err = orchestrator.AddBackend(ctx, backendConfig1, "") + if err != nil { + t.Fatalf("Unable to add first backend: %v", err) + } + + // Add second backend (slow) with a slow pool + backendConfig2, err := fakedriver.NewFakeStorageDriverConfigJSON( + "slow-backend", + config.File, + map[string]*fake.StoragePool{ + tu.SlowSnapshots: mockPools[tu.SlowSnapshots], + }, + []fake.Volume{}, + ) + if err != nil { + t.Fatalf("Unable to generate backend config JSON: %v", err) + } + _, err = orchestrator.AddBackend(ctx, backendConfig2, "") + if err != nil { + t.Fatalf("Unable to add second backend: %v", err) + } + + // Add two storage classes, each matching a different backend exclusively + storageClasses := []storageClassTest{ + { + config: &storageclass.Config{ + Name: "sc-fast", + AdditionalPools: map[string][]string{"fast-backend": {tu.FastSmall}}, + }, + expected: []*tu.PoolMatch{{Backend: "fast-backend", Pool: tu.FastSmall}}, + }, + { + config: &storageclass.Config{ + Name: "sc-slow", + AdditionalPools: map[string][]string{"slow-backend": {tu.SlowSnapshots}}, + }, + expected: []*tu.PoolMatch{{Backend: "slow-backend", Pool: tu.SlowSnapshots}}, + }, + } + for _, sc := range storageClasses { + if _, err := orchestrator.AddStorageClass(ctx, sc.config); err != nil { + t.Fatalf("Unable to add storage class %s: %v", sc.config.Name, err) + } + } + + // Create source volume on sc-fast (goes to fast-backend) + sourceConfig := tu.GenerateVolumeConfig("source-vol", 1, "sc-fast", config.File) + _, err = orchestrator.AddVolume(ctx, sourceConfig) + if err != nil { + t.Fatalf("Unable to add source volume: %v", err) + } + + // Attempt to clone with sc-slow (only has slow-backend, not fast-backend) + cloneConfig := &storage.VolumeConfig{ + Name: "clone-vol", + StorageClass: "sc-slow", + CloneSourceVolume: "source-vol", + VolumeMode: config.Filesystem, + } + _, err = orchestrator.CloneVolume(ctx, cloneConfig) + + // Verify error - different SCs with different backends should fail + if err == nil { + t.Error("Expected error when cloning with different storage class on different backend, but got none") + } else if !errors.IsMismatchedStorageClassError(err) { + t.Errorf("Expected MismatchedStorageClassError, got: %v", err) + } + + // Verify clone was not created + orchestrator.mutex.Lock() + _, found := orchestrator.volumes["clone-vol"] + if found { + t.Error("Clone volume was created despite different backends") + } + orchestrator.mutex.Unlock() + + cleanup(t, orchestrator) +} + func addBackend( t *testing.T, orchestrator *TridentOrchestrator, backendName string, backendProtocol config.Protocol, ) { diff --git a/frontend/csi/controller_helpers/kubernetes/helper.go b/frontend/csi/controller_helpers/kubernetes/helper.go index 87b8bfc26..538075511 100644 --- a/frontend/csi/controller_helpers/kubernetes/helper.go +++ b/frontend/csi/controller_helpers/kubernetes/helper.go @@ -449,20 +449,6 @@ func (h *helper) getCloneSourceInfo(ctx context.Context, clonePVC *v1.Persistent } } - // Check that both source and clone PVCs have the same storage class - // NOTE: For VolumeContentSource this check is performed by CSI - if getStorageClassForPVC(sourcePVC) != getStorageClassForPVC(clonePVC) { - Logc(ctx).WithFields(LogFields{ - "clonePVCName": clonePVC.Name, - "clonePVCNamespace": clonePVC.Namespace, - "clonePVCStorageClass": getStorageClassForPVC(clonePVC), - "sourcePVCName": sourcePVC.Name, - "sourcePVCNamespace": sourcePVC.Namespace, - "sourcePVCStorageClass": getStorageClassForPVC(sourcePVC), - }).Error("Cloning from a PVC requires both PVCs have the same storage class.") - return "", fmt.Errorf("cloning from a PVC requires both PVCs have the same storage class") - } - // Check that the source PVC has an associated PV sourcePVName := sourcePVC.Spec.VolumeName if sourcePVName == "" { From b684041cc7b4e15b4cdd081033f21bc9fc56f465 Mon Sep 17 00:00:00 2001 From: Clinton Knight Date: Tue, 24 Mar 2026 15:18:56 -0400 Subject: [PATCH 25/30] Fix indentation in tridentactionmirrorupdates k8s CRD YAML Co-authored-by: JRuumis --- cli/k8s_client/yaml_factory.go | 116 ++++++++++++++++----------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/cli/k8s_client/yaml_factory.go b/cli/k8s_client/yaml_factory.go index 174ffce27..632327443 100644 --- a/cli/k8s_client/yaml_factory.go +++ b/cli/k8s_client/yaml_factory.go @@ -2254,64 +2254,64 @@ spec: ` const tridentActionMirrorUpdateCRDYAMLv1 = ` - apiVersion: apiextensions.k8s.io/v1 - kind: CustomResourceDefinition - metadata: - name: tridentactionmirrorupdates.trident.netapp.io - spec: - group: trident.netapp.io - versions: - - name: v1 - served: true - storage: true - schema: - openAPIV3Schema: - type: object - x-kubernetes-preserve-unknown-fields: true - additionalPrinterColumns: - - description: Namespace - jsonPath: .metadata.namespace - name: Namespace - type: string - priority: 0 - - description: State - jsonPath: .status.state - name: State - type: string - priority: 0 - - description: CompletionTime - jsonPath: .status.completionTime - name: CompletionTime - type: date - priority: 0 - - description: Message - jsonPath: .status.message - name: Message - type: string - priority: 1 - - description: LocalVolumeHandle - jsonPath: .status.localVolumeHandle - name: LocalVolumeHandle - type: string - priority: 1 - - description: RemoteVolumeHandle - jsonPath: .status.remoteVolumeHandle - name: RemoteVolumeHandle - type: string - priority: 1 - scope: Namespaced - names: - plural: tridentactionmirrorupdates - singular: tridentactionmirrorupdate - kind: TridentActionMirrorUpdate - shortNames: - - tamu - - tamupdate - - tamirrorupdate - categories: - - trident - - trident-external - ` +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: tridentactionmirrorupdates.trident.netapp.io +spec: + group: trident.netapp.io + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + additionalPrinterColumns: + - description: Namespace + jsonPath: .metadata.namespace + name: Namespace + type: string + priority: 0 + - description: State + jsonPath: .status.state + name: State + type: string + priority: 0 + - description: CompletionTime + jsonPath: .status.completionTime + name: CompletionTime + type: date + priority: 0 + - description: Message + jsonPath: .status.message + name: Message + type: string + priority: 1 + - description: LocalVolumeHandle + jsonPath: .status.localVolumeHandle + name: LocalVolumeHandle + type: string + priority: 1 + - description: RemoteVolumeHandle + jsonPath: .status.remoteVolumeHandle + name: RemoteVolumeHandle + type: string + priority: 1 + scope: Namespaced + names: + plural: tridentactionmirrorupdates + singular: tridentactionmirrorupdate + kind: TridentActionMirrorUpdate + shortNames: + - tamu + - tamupdate + - tamirrorupdate + categories: + - trident + - trident-external +` const tridentSnapshotInfoCRDYAMLv1 = ` apiVersion: apiextensions.k8s.io/v1 From 11e3131b35ff3fc839eb5b2e15be2d62d812de9f Mon Sep 17 00:00:00 2001 From: Tori Revilla <52927195+torirevilla@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:13:06 -0400 Subject: [PATCH 26/30] Use volume config fstype during publish --- storage_drivers/ontap/ontap_asa.go | 4 +- storage_drivers/ontap/ontap_asa_test.go | 15 +-- storage_drivers/ontap/ontap_common.go | 44 ++++--- storage_drivers/ontap/ontap_common_test.go | 124 ++++++++++++++++-- storage_drivers/ontap/ontap_san.go | 4 +- storage_drivers/ontap/ontap_san_economy.go | 5 +- .../ontap/ontap_san_economy_test.go | 21 ++- storage_drivers/ontap/ontap_san_test.go | 16 +-- 8 files changed, 166 insertions(+), 67 deletions(-) diff --git a/storage_drivers/ontap/ontap_asa.go b/storage_drivers/ontap/ontap_asa.go index 98f7cb740..f9faedce9 100644 --- a/storage_drivers/ontap/ontap_asa.go +++ b/storage_drivers/ontap/ontap_asa.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -790,7 +790,7 @@ func (d *ASAStorageDriver) Publish( } nodeName = iSCSINodeName } - err = PublishLUN(ctx, d.API, &d.Config, d.ips, publishInfo, lunPath, igroupName, nodeName) + err = PublishLUN(ctx, d.API, &d.Config, d.ips, publishInfo, lunPath, igroupName, nodeName, volConfig) if err != nil { return fmt.Errorf("error publishing %s driver: %v", d.Name(), err) } diff --git a/storage_drivers/ontap/ontap_asa_test.go b/storage_drivers/ontap/ontap_asa_test.go index e5dd96e7b..b5d1aee4c 100644 --- a/storage_drivers/ontap/ontap_asa_test.go +++ b/storage_drivers/ontap/ontap_asa_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -748,6 +748,7 @@ func TestCreateASA(t *testing.T) { assert.Equal(t, storagePool.InternalAttributes()[QosPolicy], volConfig.QosPolicy) assert.Equal(t, storagePool.InternalAttributes()[LUKSEncryption], volConfig.LUKSEncryption) assert.Equal(t, storagePool.InternalAttributes()[FileSystemType], volConfig.FileSystem) + assert.Equal(t, storagePool.InternalAttributes()[FormatOptions], volConfig.FormatOptions) assert.Equal(t, "true", volConfig.SkipRecoveryQueue, "SkipRecoveryQueue does not match") }, }, @@ -1496,6 +1497,8 @@ func TestPublishASA(t *testing.T) { initializeFunction := func() { volConfig = getASAVolumeConfig() volConfig.InternalName = "testVol" + // Set on Create; PublishLUN uses volConfig.FormatOptions and skips LunGetAttribute when non-empty. + volConfig.FormatOptions = "formatOptions" driver.Config.IgroupName = "testIgroup" @@ -1528,8 +1531,6 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Return("nodeName", nil).Times(1) mockAPI.EXPECT().IscsiInterfaceGet(ctx, driver.Config.SVM).Return([]string{"iscsiInterfaces"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("ext4", nil).Times(1) - mockAPI.EXPECT().LunGetAttribute(ctx, volConfig.InternalName, "formatOptions").Return("formatOptions", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, driver.Config.IgroupName, volConfig.InternalName).Return(1123, nil).Times(1) }, @@ -1550,8 +1551,6 @@ func TestPublishASA(t *testing.T) { }).Times(1) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Return("nodeName", nil).Times(1) mockAPI.EXPECT().IscsiInterfaceGet(ctx, driver.Config.SVM).Return([]string{"iscsiInterfaces"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("lunFSType", nil).Times(1) - mockAPI.EXPECT().LunGetAttribute(ctx, volConfig.InternalName, "formatOptions").Return("formatOptions", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, getNodeSpecificIgroupName(publishInfo.HostName, publishInfo.TridentUUID), volConfig.InternalName).Return(1123, nil).Times(1) }, @@ -1595,8 +1594,6 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Return("nodeName", nil).Times(1) mockAPI.EXPECT().IscsiInterfaceGet(ctx, driver.Config.SVM).Return([]string{"iscsiInterfaces"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("lunFSType", nil).Times(1) - mockAPI.EXPECT().LunGetAttribute(ctx, volConfig.InternalName, "formatOptions").Return("formatOptions", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(nil, errors.New("error")).Times(1) }, verify: func(t *testing.T, err error) { @@ -1618,8 +1615,6 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().FcpNodeGetNameRequest(ctx).Return("10:00:00:00:00:00:00:01", nil).Times(1) mockAPI.EXPECT().FcpInterfaceGet(ctx, driver.Config.SVM).Return([]string{"10:00:00:00:00:00:00:01"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("ext4", nil).Times(1) - mockAPI.EXPECT().LunGetAttribute(ctx, volConfig.InternalName, "formatOptions").Return("formatOptions", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureIgroupAdded(ctx, driver.Config.IgroupName, "10:00:00:00:00:00:00:01").Return(nil).AnyTimes() mockAPI.EXPECT().EnsureLunMapped(ctx, driver.Config.IgroupName, volConfig.InternalName).Return(1123, nil).AnyTimes() @@ -1690,8 +1685,6 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().FcpNodeGetNameRequest(ctx).Return("10:00:00:00:00:00:00:01", nil).Times(1) mockAPI.EXPECT().FcpInterfaceGet(ctx, driver.Config.SVM).Return([]string{"10:00:00:00:00:00:00:01"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("ext4", nil).Times(1) - mockAPI.EXPECT().LunGetAttribute(ctx, volConfig.InternalName, "formatOptions").Return("formatOptions", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, driver.Config.IgroupName, volConfig.InternalName).Return(1123, nil).AnyTimes() }, diff --git a/storage_drivers/ontap/ontap_common.go b/storage_drivers/ontap/ontap_common.go index 0c8ff58c8..dfa4fb0d8 100644 --- a/storage_drivers/ontap/ontap_common.go +++ b/storage_drivers/ontap/ontap_common.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -933,10 +933,14 @@ func getNodeSpecificFCPIgroupName(nodeName, tridentUUID string) string { // mounted, so it should limit itself to updating access rules, initiator groups, etc. that require // some host identity (but not locality) as well as storage controller API access. // This function assumes that the list of data LIF IP addresses does not change between driver initialization -// and publish +// and publish. +// volConfig supplies per-volume settings from create (or import): FileSystem and FormatOptions. When FileSystem is +// non-empty it is used as-is; when empty, fstype is read from the LUN via LunGetFSType, or drivers.DefaultFileSystemType +// if that fails or returns empty. When FormatOptions is non-empty it is used as-is; when empty, format options are +// read from the LUN attribute "formatOptions" (config.FileSystemType is not consulted for fstype here). func PublishLUN( ctx context.Context, clientAPI api.OntapAPI, config *drivers.OntapStorageDriverConfig, ips []string, - publishInfo *tridentmodels.VolumePublishInfo, lunPath, igroupName, nodeName string, + publishInfo *tridentmodels.VolumePublishInfo, lunPath, igroupName, nodeName string, volConfig *storage.VolumeConfig, ) error { lunMutex.Lock(lunPath) defer lunMutex.Unlock(lunPath) @@ -979,27 +983,31 @@ func PublishLUN( } } - // Get the fstype - fstype := drivers.DefaultFileSystemType - lunFSType, err := clientAPI.LunGetFSType(ctx, lunPath) - if err != nil || lunFSType == "" { - if err != nil { - Logc(ctx).Warnf("failed to get fstype for LUN: %v", err) + // Resolve fstype: prefer volume config (orchestrator), then LUN attribute, then driver default. + fstype := volConfig.FileSystem + if fstype == "" { + fstype, err = clientAPI.LunGetFSType(ctx, lunPath) + if err != nil || fstype == "" { + if err != nil { + Logc(ctx).WithError(err).Error("failed to get fstype for LUN") + } + Logc(ctx).WithFields(LogFields{ + "LUN": lunPath, + "fstype": fstype, + }).Error("LUN attribute fstype not found, using default.") + fstype = drivers.DefaultFileSystemType } - Logc(ctx).WithFields(LogFields{ - "LUN": lunPath, - "fstype": fstype, - }).Warn("LUN attribute fstype not found, using default.") - } else { - fstype = lunFSType } // Get the format options // An example of how formatOption may look like: // "-E stride=256,stripe_width=16 -F -b 2435965" - formatOptions, err := clientAPI.LunGetAttribute(ctx, lunPath, "formatOptions") - if err != nil { - Logc(ctx).Warnf("Failed to get format options for LUN: %v", err) + formatOptions := volConfig.FormatOptions + if formatOptions == "" { + formatOptions, err = clientAPI.LunGetAttribute(ctx, lunPath, "formatOptions") + if err != nil { + Logc(ctx).WithError(err).Error("Failed to get format options for LUN") + } } // Get LUN Serial Number diff --git a/storage_drivers/ontap/ontap_common_test.go b/storage_drivers/ontap/ontap_common_test.go index 43bb7eace..3a2e2a05b 100644 --- a/storage_drivers/ontap/ontap_common_test.go +++ b/storage_drivers/ontap/ontap_common_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -5907,6 +5907,11 @@ func TestPublishLun(t *testing.T) { config.SANType = sa.ISCSI + publishVolCfg := func(fileSystem, formatOptions string) *storage.VolumeConfig { + return &storage.VolumeConfig{FileSystem: fileSystem, FormatOptions: formatOptions} + } + volNoFsOrFmt := publishVolCfg("", "") + publishInfo := &tridentmodels.VolumePublishInfo{ BackendUUID: "fakeBackendUUID", Localhost: false, @@ -5923,7 +5928,7 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) - err := PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err := PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) @@ -5932,7 +5937,7 @@ func TestPublishLun(t *testing.T) { publishInfo.Localhost = false publishInfo.HostIQN = []string{} - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.Error(t, err) @@ -5947,11 +5952,12 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) + assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) - // Test 4 - LunGetFSType returns error + // Test 4 - LunGetAttribute returns error (fstype still from LUN) mockAPI = mockapi.NewMockOntapAPI(mockCtrl) publishInfo.HostIQN = []string{"host_iqn"} mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("fsType", nil) @@ -5962,9 +5968,10 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) + assert.Equal(t, "fsType", publishInfo.FilesystemType) // Test 5 - No target node found mockAPI = mockapi.NewMockOntapAPI(mockCtrl) @@ -5974,7 +5981,7 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.Error(t, err) @@ -5992,7 +5999,7 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, gomock.Any()).Return(errors.New("EnsureIgroupAdded returned error")) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.Error(t, err) @@ -6004,7 +6011,7 @@ func TestPublishLun(t *testing.T) { errors.New("EnsureLunMapped returned error")) mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, gomock.Any()).Return(nil) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.Error(t, err) @@ -6013,7 +6020,7 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, errors.New("LunGetByName returned error")) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.Error(t, err) @@ -6022,7 +6029,7 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLunNoSerial, nil) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.Error(t, err) @@ -6043,9 +6050,102 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) - err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) assert.Equal(t, tempFormatOptions, publishInfo.FormatOptions) + + // Test 11 - volume FileSystem (volConfig) is "xfs", LunGetFSType should NOT be called + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + config.FileSystemType = "" + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg("xfs", "")) + + assert.NoError(t, err) + assert.Equal(t, "xfs", publishInfo.FilesystemType) + assert.Contains(t, publishInfo.MountOptions, "nouuid") + + // Test 11b - FileSystem and FormatOptions from vol (as after Create); no LunGetFSType or LunGetAttribute + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + volFmtOpts := "-b 4096 -T stride=256" + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg("xfs", volFmtOpts)) + assert.NoError(t, err) + assert.Equal(t, "xfs", publishInfo.FilesystemType) + assert.Equal(t, volFmtOpts, publishInfo.FormatOptions) + assert.Contains(t, publishInfo.MountOptions, "nouuid") + + // Test 12 - volume fstype empty, LunGetFSType returns empty string with no error, use default + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + config.FileSystemType = "" + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("", nil) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) + + assert.NoError(t, err) + assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) + + // Test 13 - volume fstype empty, LunGetFSType returns error, use default + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + config.FileSystemType = "" + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("", errors.New("LunGetFSType returned error")) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) + + assert.NoError(t, err) + assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) } func TestValidateSANDriver(t *testing.T) { diff --git a/storage_drivers/ontap/ontap_san.go b/storage_drivers/ontap/ontap_san.go index 93a6beb69..ade9390ac 100644 --- a/storage_drivers/ontap/ontap_san.go +++ b/storage_drivers/ontap/ontap_san.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -1028,7 +1028,7 @@ func (d *SANStorageDriver) Publish( nodeName = iSCSINodeName } - err = PublishLUN(ctx, d.API, &d.Config, d.ips, publishInfo, lunPath, igroupName, nodeName) + err = PublishLUN(ctx, d.API, &d.Config, d.ips, publishInfo, lunPath, igroupName, nodeName, volConfig) if err != nil { return fmt.Errorf("error publishing %s driver: %v", d.Name(), err) } diff --git a/storage_drivers/ontap/ontap_san_economy.go b/storage_drivers/ontap/ontap_san_economy.go index 2ac3a08fa..916801373 100644 --- a/storage_drivers/ontap/ontap_san_economy.go +++ b/storage_drivers/ontap/ontap_san_economy.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -577,6 +577,7 @@ func (d *SANEconomyStorageDriver) Create( volConfig.QosPolicy = qosPolicy volConfig.AdaptiveQosPolicy = adaptiveQosPolicy volConfig.LUKSEncryption = luksEncryption + volConfig.FileSystem = fstype volConfig.FormatOptions = formatOptions createErrors := make([]error, 0) @@ -1278,7 +1279,7 @@ func (d *SANEconomyStorageDriver) Publish( return err } - err = PublishLUN(ctx, d.API, &d.Config, d.ips, publishInfo, extantLUNPath, igroupName, iSCSINodeName) + err = PublishLUN(ctx, d.API, &d.Config, d.ips, publishInfo, extantLUNPath, igroupName, iSCSINodeName, volConfig) if err != nil { return fmt.Errorf("error publishing LUN %s: %w", name, err) } diff --git a/storage_drivers/ontap/ontap_san_economy_test.go b/storage_drivers/ontap/ontap_san_economy_test.go index d43e0813f..8303f9d9d 100644 --- a/storage_drivers/ontap/ontap_san_economy_test.go +++ b/storage_drivers/ontap/ontap_san_economy_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -616,6 +616,7 @@ func TestOntapSanEconomyVolumeCreate(t *testing.T) { SecurityStyle: "mixed", Encryption: "false", TieringPolicy: "none", + FormatOptions: "-b 4096", QosPolicy: "fake-qos-policy", AdaptiveQosPolicy: "", LUKSEncryption: "false", @@ -655,6 +656,7 @@ func TestOntapSanEconomyVolumeCreate(t *testing.T) { assert.Equal(t, "", volConfig.AdaptiveQosPolicy) assert.Equal(t, "false", volConfig.LUKSEncryption) assert.Equal(t, "xfs", volConfig.FileSystem) + assert.Equal(t, "-b 4096", volConfig.FormatOptions) // The flexvol pool is randomized, so we can only check the prefix and suffix of the internalID assert.True(t, strings.HasPrefix(volConfig.InternalID, "/svm/SVM1/flexvol/")) @@ -2529,6 +2531,7 @@ func TestOntapSanEconomyVolumePublish(t *testing.T) { Size: "1g", Encryption: "false", FileSystem: "xfs", + FormatOptions: "-b 4096", ImportNotManaged: true, } publishInfo := &models.VolumePublishInfo{ @@ -2548,8 +2551,6 @@ func TestOntapSanEconomyVolumePublish(t *testing.T) { mockAPI.EXPECT().IgroupCreate(ctx, gomock.Any(), "iscsi", "linux").Return(nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/volumeName/lunName") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/volumeName/lunName", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/volumeName/lunName").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -2578,6 +2579,7 @@ func TestOntapSanEconomyVolumePublish_InternalID(t *testing.T) { Size: "1g", Encryption: "false", FileSystem: "xfs", + FormatOptions: "-b 4096", ImportNotManaged: true, } publishInfo := &models.VolumePublishInfo{ @@ -2597,8 +2599,6 @@ func TestOntapSanEconomyVolumePublish_InternalID(t *testing.T) { mockAPI.EXPECT().IgroupCreate(ctx, gomock.Any(), "iscsi", "linux").Return(nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/volumeName/lunName") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/volumeName/lunName", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/volumeName/lunName").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -2618,10 +2618,11 @@ func TestOntapSanEconomyVolumePublishSLMError(t *testing.T) { d.Config.SANType = sa.ISCSI volConfig := &storage.VolumeConfig{ - InternalName: "lunName", - Size: "1g", - Encryption: "false", - FileSystem: "xfs", + InternalName: "lunName", + Size: "1g", + Encryption: "false", + FileSystem: "xfs", + FormatOptions: "-b 4096", } publishInfo := &models.VolumePublishInfo{ HostName: "bar", @@ -2640,8 +2641,6 @@ func TestOntapSanEconomyVolumePublishSLMError(t *testing.T) { gomock.Any()).Times(1).Return(api.Luns{api.Lun{Size: "1g", Name: "lunName", VolumeName: "volumeName"}}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/volumeName/lunName") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/volumeName/lunName", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/volumeName/lunName").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) diff --git a/storage_drivers/ontap/ontap_san_test.go b/storage_drivers/ontap/ontap_san_test.go index d007fed8e..2853272a8 100644 --- a/storage_drivers/ontap/ontap_san_test.go +++ b/storage_drivers/ontap/ontap_san_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package ontap @@ -359,6 +359,7 @@ func TestOntapSANVolumeCreate(t *testing.T) { SecurityStyle: "mixed", Encryption: "false", TieringPolicy: "none", + FormatOptions: "-b 4096", SkipRecoveryQueue: "true", QosPolicy: "fake-qos-policy", AdaptiveQosPolicy: "", @@ -384,6 +385,7 @@ func TestOntapSANVolumeCreate(t *testing.T) { assert.Equal(t, "", volConfig.AdaptiveQosPolicy) assert.Equal(t, "true", volConfig.LUKSEncryption) assert.Equal(t, "xfs", volConfig.FileSystem) + assert.Equal(t, "-b 4096", volConfig.FormatOptions) } // TestOntapSanVolumeCreate_InvalidSkipRecoveryQueue tests volume creation with invalid recovery queue configuration @@ -680,6 +682,7 @@ func TestOntapSanVolumePublishManaged(t *testing.T) { volConfig := getVolumeConfig() volConfig.InternalName = "lunName" + volConfig.FormatOptions = "-b 4096" publishInfo := &models.VolumePublishInfo{ HostName: "bar", @@ -697,8 +700,6 @@ func TestOntapSanVolumePublishManaged(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Times(1).Return(&api.Volume{AccessType: VolTypeRW}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/lunName/lun0", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -727,6 +728,7 @@ func TestOntapSanVolumePublishUnmanaged(t *testing.T) { volConfig := getVolumeConfig() volConfig.InternalName = "lunName" + volConfig.FormatOptions = "-b 4096" publishInfo := &models.VolumePublishInfo{ HostName: "bar", @@ -744,8 +746,6 @@ func TestOntapSanVolumePublishUnmanaged(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Times(1).Return(&api.Volume{AccessType: VolTypeRW}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/lunName/lun0", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -774,6 +774,7 @@ func TestOntapSanVolumePublishSLMError(t *testing.T) { volConfig := getVolumeConfig() volConfig.InternalName = "lunName" + volConfig.FormatOptions = "-b 4096" publishInfo := &models.VolumePublishInfo{ HostName: "bar", @@ -791,8 +792,6 @@ func TestOntapSanVolumePublishSLMError(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Times(1).Return(&api.Volume{AccessType: VolTypeRW}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/lunName/lun0", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -3771,6 +3770,7 @@ func TestOntapSanVolumePublishisFlexvolRW(t *testing.T) { volConfig := getVolumeConfig() volConfig.InternalName = "lunName" + volConfig.FormatOptions = "-b 4096" publishInfo := &models.VolumePublishInfo{ HostName: "bar", @@ -3836,8 +3836,6 @@ func TestOntapSanVolumePublishisFlexvolRW(t *testing.T) { mockAPI.EXPECT().IgroupCreate(ctx, gomock.Any(), "iscsi", "linux").Return(nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) - mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0") - mockAPI.EXPECT().LunGetAttribute(ctx, "/vol/lunName/lun0", "formatOptions") mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) err := driver.Publish(ctx, &volConfig, publishInfo) From 6df563addb57ddccfb8c494190b5d1c77447cd05 Mon Sep 17 00:00:00 2001 From: reederc42 Date: Mon, 13 Apr 2026 13:51:45 -0700 Subject: [PATCH 27/30] Disables LUKS passphrase tracking to avoid controller lock --- frontend/csi/utils.go | 54 +++++++++++++++++--------------- frontend/csi/utils_test.go | 64 -------------------------------------- 2 files changed, 29 insertions(+), 89 deletions(-) diff --git a/frontend/csi/utils.go b/frontend/csi/utils.go index 271320205..7489d1174 100644 --- a/frontend/csi/utils.go +++ b/frontend/csi/utils.go @@ -258,8 +258,8 @@ func performProtocolSpecificReconciliation(ctx context.Context, trackingInfo *mo // of any possibly in use passphrases. If forceUpdate is true, the Trident controller will be notified of the current // passphrase name, regardless of a rotation. func ensureLUKSVolumePassphrase( - ctx context.Context, restClient controllerAPI.TridentController, luksDevice luks.Device, - volumeId string, secrets map[string]string, forceUpdate bool, + ctx context.Context, _ controllerAPI.TridentController, luksDevice luks.Device, + volumeId string, secrets map[string]string, _ bool, ) error { luksPassphraseName, luksPassphrase, previousLUKSPassphraseName, previousLUKSPassphrase := luks.GetLUKSPassphrasesFromSecretMap(secrets) @@ -279,13 +279,14 @@ func ensureLUKSVolumePassphrase( Logc(ctx).WithFields(LogFields{ "volume": volumeId, }).Debugf("Current LUKS passphrase name '%s'.", luksPassphraseName) - if forceUpdate { - luksPassphraseNames := []string{luksPassphraseName} - err = restClient.UpdateVolumeLUKSPassphraseNames(ctx, volumeId, luksPassphraseNames) - if err != nil { - return fmt.Errorf("could not update current passphrase name for LUKS volume; %v", err) - } - } + // Disabled in all supported versions until 26.06.0. Users must track LUKS passphrases for volumes. + // if forceUpdate { + // luksPassphraseNames := []string{luksPassphraseName} + // err = restClient.UpdateVolumeLUKSPassphraseNames(ctx, volumeId, luksPassphraseNames) + // if err != nil { + // return fmt.Errorf("could not update current passphrase name for LUKS volume; %v", err) + // } + // } return nil } @@ -307,12 +308,13 @@ func ensureLUKSVolumePassphrase( "volume": volumeId, }).Debugf("Current LUKS passphrase name '%s'.", previousLUKSPassphraseName) + // Disabled in all supported versions until 26.06.0. Users must track LUKS passphrases for volumes. // Send up current and previous passphrase names, if rotation fails - luksPassphraseNames := []string{luksPassphraseName, previousLUKSPassphraseName} - err = restClient.UpdateVolumeLUKSPassphraseNames(ctx, volumeId, luksPassphraseNames) - if err != nil { - return fmt.Errorf("could not update passphrase names for LUKS volume, skipping passphrase rotation; %v", err) - } + // luksPassphraseNames := []string{luksPassphraseName, previousLUKSPassphraseName} + // err = restClient.UpdateVolumeLUKSPassphraseNames(ctx, volumeId, luksPassphraseNames) + // if err != nil { + // return fmt.Errorf("could not update passphrase names for LUKS volume, skipping passphrase rotation; %v", err) + // } // Rotate Logc(ctx).WithFields(LogFields{ @@ -331,16 +333,18 @@ func ensureLUKSVolumePassphrase( } Logc(ctx).Infof("Rotated LUKS passphrase") - isCurrent, err := luksDevice.CheckPassphrase(ctx, luksPassphrase) - if err != nil { - return fmt.Errorf("could not check current passphrase for LUKS volume; %v", err) - } else if isCurrent { - // Send only current passphrase up - luksPassphraseNames = []string{luksPassphraseName} - err = restClient.UpdateVolumeLUKSPassphraseNames(ctx, volumeId, luksPassphraseNames) - if err != nil { - return fmt.Errorf("could not update passphrase names for LUKS volume after rotation; %v", err) - } - } + // isCurrent, err := luksDevice.CheckPassphrase(ctx, luksPassphrase) + // if err != nil { + // return fmt.Errorf("could not check current passphrase for LUKS volume; %v", err) + // Disabled in all supported versions until 26.06.0. Users must track LUKS passphrases for volumes. + // } else if isCurrent { + // // Send only current passphrase up + // luksPassphraseNames = []string{luksPassphraseName} + // err = restClient.UpdateVolumeLUKSPassphraseNames(ctx, volumeId, luksPassphraseNames) + // if err != nil { + // return fmt.Errorf("could not update passphrase names for LUKS volume after rotation; %v", err) + // } + // } + // } return nil } diff --git a/frontend/csi/utils_test.go b/frontend/csi/utils_test.go index c4e8604b4..9867dfa20 100644 --- a/frontend/csi/utils_test.go +++ b/frontend/csi/utils_test.go @@ -135,7 +135,6 @@ func TestEnsureLUKSVolumePassphrase(t *testing.T) { "luks-passphrase": "passphraseA", } mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseA").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"A"}).Return(nil) err = ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, true) assert.NoError(t, err) mockCtrl.Finish() @@ -153,10 +152,7 @@ func TestEnsureLUKSVolumePassphrase(t *testing.T) { } mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(false, nil) mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseA").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B", "A"}).Return(nil) mockLUKSDevice.EXPECT().RotatePassphrase(gomock.Any(), "test-vol", "passphraseA", "passphraseB").Return(nil) - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B"}).Return(nil) err = ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, false) assert.NoError(t, err) mockCtrl.Finish() @@ -196,24 +192,6 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { assert.Error(t, err) mockCtrl.Finish() - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Sending pre-rotation passphrases to trident controller fails - mockCtrl = gomock.NewController(t) - mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) - secrets = map[string]string{ - "luks-passphrase-name": "B", - "luks-passphrase": "passphraseB", - "previous-luks-passphrase-name": "A", - "previous-luks-passphrase": "passphraseA", - } - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(false, nil) - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseA").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B", "A"}).Return(fmt.Errorf("test error")) - err = ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, false) - assert.Error(t, err) - mockCtrl.Finish() - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Negative case: Passphrase rotation fails mockCtrl = gomock.NewController(t) @@ -227,52 +205,10 @@ func TestEnsureLUKSVolumePassphrase_Error(t *testing.T) { } mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(false, nil) mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseA").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B", "A"}).Return(nil) mockLUKSDevice.EXPECT().RotatePassphrase(gomock.Any(), "test-vol", "passphraseA", "passphraseB").Return(fmt.Errorf("test error")) err = ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, false) assert.Error(t, err) mockCtrl.Finish() - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Verifying passphrase rotation fails - mockCtrl = gomock.NewController(t) - mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) - secrets = map[string]string{ - "luks-passphrase-name": "B", - "luks-passphrase": "passphraseB", - "previous-luks-passphrase-name": "A", - "previous-luks-passphrase": "passphraseA", - } - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(false, nil) - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseA").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B", "A"}).Return(nil) - mockLUKSDevice.EXPECT().RotatePassphrase(gomock.Any(), "test-vol", "passphraseA", "passphraseB").Return(nil) - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(true, fmt.Errorf("test error")) - err = ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, false) - assert.Error(t, err) - mockCtrl.Finish() - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Negative case: Sending post-rotation passphrases to trident controller fails - mockCtrl = gomock.NewController(t) - mockClient = mockControllerAPI.NewMockTridentController(mockCtrl) - mockLUKSDevice = mock_luks.NewMockDevice(mockCtrl) - secrets = map[string]string{ - "luks-passphrase-name": "B", - "luks-passphrase": "passphraseB", - "previous-luks-passphrase-name": "A", - "previous-luks-passphrase": "passphraseA", - } - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(false, nil) - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseA").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B", "A"}).Return(nil) - mockLUKSDevice.EXPECT().RotatePassphrase(gomock.Any(), "test-vol", "passphraseA", "passphraseB").Return(nil) - mockLUKSDevice.EXPECT().CheckPassphrase(gomock.Any(), "passphraseB").Return(true, nil) - mockClient.EXPECT().UpdateVolumeLUKSPassphraseNames(gomock.Any(), "test-vol", []string{"B"}).Return(fmt.Errorf("test error")) - err = ensureLUKSVolumePassphrase(context.TODO(), mockClient, mockLUKSDevice, "test-vol", secrets, false) - assert.Error(t, err) - mockCtrl.Finish() } func TestEnsureLUKSVolumePassphrase_InvalidSecret(t *testing.T) { From 800f09ae46e6bcbc05b8fa85f236b1bba686cb7a Mon Sep 17 00:00:00 2001 From: jharrod Date: Mon, 13 Apr 2026 15:58:05 -0600 Subject: [PATCH 28/30] Filter out multipath partitions unstage Fix failing iSCSI unstages due to multipath partitions (ghost device error). --- utils/devices/devices.go | 23 ++++-- utils/devices/devices_linux_test.go | 119 ++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/utils/devices/devices.go b/utils/devices/devices.go index 42b62d8e6..20fec4f9b 100644 --- a/utils/devices/devices.go +++ b/utils/devices/devices.go @@ -667,12 +667,23 @@ func (c *Client) GetMultipathDeviceBySerial(ctx context.Context, hexSerial strin continue } - if strings.Contains(uuid, hexSerial) { - Logc(ctx).WithFields(LogFields{ - "UUID": hexSerial, - "multipathDevice": dmDeviceName, - }).Debug("Found multipath device by UUID.") - return dmDeviceName, nil + // Find the matching UUID while filtering out child partitions (e.g. part1-mpath-3600a098038314461522451712f316969) + trimmedUUID := strings.TrimSpace(uuid) + if strings.Contains(trimmedUUID, hexSerial) { + if strings.HasPrefix(trimmedUUID, "mpath-") { + Logc(ctx).WithFields(LogFields{ + "serial": hexSerial, + "UUID": trimmedUUID, + "multipathDevice": dmDeviceName, + }).Debug("Found multipath device by UUID.") + return dmDeviceName, nil + } else { + Logc(ctx).WithFields(LogFields{ + "serial": hexSerial, + "UUID": trimmedUUID, + "multipathDevice": dmDeviceName, + }).Debug("DM Device contains LUN serial, but is not a top-level multipath device.") + } } } diff --git a/utils/devices/devices_linux_test.go b/utils/devices/devices_linux_test.go index bec5542f1..01a66d79d 100644 --- a/utils/devices/devices_linux_test.go +++ b/utils/devices/devices_linux_test.go @@ -1708,3 +1708,122 @@ func TestVerifyMultipathDeviceSerial(t *testing.T) { }) } } + +func TestGetMultipathDeviceBySerial(t *testing.T) { + const hexSerial = "3600a098038314461522451712f316969" + + tests := map[string]struct { + setupFs func() afero.Fs + hexSerial string + expectedDevice string + expectError bool + expectNotFound bool + }{ + "ErrorWhenSysBlockUnreadable": { + setupFs: func() afero.Fs { + // Empty fs — /sys/block/ does not exist. + return afero.NewMemMapFs() + }, + hexSerial: hexSerial, + expectError: true, + }, + "NotFoundWhenNoDmDevicesExist": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Only non-dm block devices present. + fs.MkdirAll("/sys/block/sda", 0o755) + fs.MkdirAll("/sys/block/sdb", 0o755) + return fs + }, + hexSerial: hexSerial, + expectNotFound: true, + }, + "NotFoundWhenNoDmDeviceHasMatchingUUID": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("mpath-0000000000000000000000000000000000"), 0o644) + return fs + }, + hexSerial: hexSerial, + expectNotFound: true, + }, + "ReturnsDeviceWhenUUIDContainsSerial": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", + []byte("mpath-"+hexSerial), 0o644) + return fs + }, + hexSerial: hexSerial, + expectedDevice: "dm-0", + }, + "SkipsNonDmBlockDevices": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // sda has the serial in its name (shouldn't be examined). + fs.MkdirAll("/sys/block/sda", 0o755) + afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", + []byte("mpath-"+hexSerial), 0o644) + return fs + }, + hexSerial: hexSerial, + expectedDevice: "dm-1", + }, + "SkipsDmDeviceWithMissingUUIDFile": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // dm-0 has no uuid file; dm-1 has a matching uuid. + fs.MkdirAll("/sys/block/dm-0", 0o755) + afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", + []byte("mpath-"+hexSerial), 0o644) + return fs + }, + hexSerial: hexSerial, + expectedDevice: "dm-1", + }, + "SkipsPartitionDevicesEvenWhenUUIDContainsSerial": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // Partition entry — uuid contains "part", must be skipped. + afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", + []byte("part1-mpath-"+hexSerial), 0o644) + afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", + []byte("mpath-"+hexSerial), 0o644) + return fs + }, + hexSerial: hexSerial, + expectedDevice: "dm-1", + }, + "ReturnsFirstMatchingDeviceWhenMultipleDmDevicesExist": { + setupFs: func() afero.Fs { + fs := afero.NewMemMapFs() + // dm-0 does not match; dm-1 matches. + afero.WriteFile(fs, "/sys/block/dm-0/dm/uuid", []byte("mpath-wrongDevice"), 0o644) + afero.WriteFile(fs, "/sys/block/dm-1/dm/uuid", + []byte("mpath-"+hexSerial), 0o644) + return fs + }, + hexSerial: hexSerial, + expectedDevice: "dm-1", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + client := NewDetailed(nil, tc.setupFs(), nil) + got, err := client.GetMultipathDeviceBySerial(context.Background(), tc.hexSerial) + + switch { + case tc.expectError && !tc.expectNotFound: + assert.Error(t, err) + assert.False(t, errors.IsNotFoundError(err)) + case tc.expectNotFound: + assert.Error(t, err) + assert.True(t, errors.IsNotFoundError(err)) + default: + assert.NoError(t, err) + assert.Equal(t, tc.expectedDevice, got) + } + }) + } +} From efe85f8be2d60c4d0a11dd951021433d408cb27c Mon Sep 17 00:00:00 2001 From: Zachary Ward Date: Thu, 30 Apr 2026 12:33:42 -0600 Subject: [PATCH 29/30] [25.10]: Fix for ONTAP SAN block-mode import bypassing fstype mismatch detection --- storage_drivers/ontap/ontap_asa_test.go | 5 + storage_drivers/ontap/ontap_common.go | 19 ++- storage_drivers/ontap/ontap_common_test.go | 128 +++++++++++++++++- .../ontap/ontap_san_economy_test.go | 3 + storage_drivers/ontap/ontap_san_test.go | 4 + 5 files changed, 150 insertions(+), 9 deletions(-) diff --git a/storage_drivers/ontap/ontap_asa_test.go b/storage_drivers/ontap/ontap_asa_test.go index b5d1aee4c..648ba9325 100644 --- a/storage_drivers/ontap/ontap_asa_test.go +++ b/storage_drivers/ontap/ontap_asa_test.go @@ -1531,6 +1531,7 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Return("nodeName", nil).Times(1) mockAPI.EXPECT().IscsiInterfaceGet(ctx, driver.Config.SVM).Return([]string{"iscsiInterfaces"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("xfs", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, driver.Config.IgroupName, volConfig.InternalName).Return(1123, nil).Times(1) }, @@ -1551,6 +1552,7 @@ func TestPublishASA(t *testing.T) { }).Times(1) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Return("nodeName", nil).Times(1) mockAPI.EXPECT().IscsiInterfaceGet(ctx, driver.Config.SVM).Return([]string{"iscsiInterfaces"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("xfs", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, getNodeSpecificIgroupName(publishInfo.HostName, publishInfo.TridentUUID), volConfig.InternalName).Return(1123, nil).Times(1) }, @@ -1594,6 +1596,7 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Return("nodeName", nil).Times(1) mockAPI.EXPECT().IscsiInterfaceGet(ctx, driver.Config.SVM).Return([]string{"iscsiInterfaces"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("xfs", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(nil, errors.New("error")).Times(1) }, verify: func(t *testing.T, err error) { @@ -1615,6 +1618,7 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().FcpNodeGetNameRequest(ctx).Return("10:00:00:00:00:00:00:01", nil).Times(1) mockAPI.EXPECT().FcpInterfaceGet(ctx, driver.Config.SVM).Return([]string{"10:00:00:00:00:00:00:01"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("xfs", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureIgroupAdded(ctx, driver.Config.IgroupName, "10:00:00:00:00:00:00:01").Return(nil).AnyTimes() mockAPI.EXPECT().EnsureLunMapped(ctx, driver.Config.IgroupName, volConfig.InternalName).Return(1123, nil).AnyTimes() @@ -1685,6 +1689,7 @@ func TestPublishASA(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, volConfig.InternalName).Return(flexVol, nil).Times(1) mockAPI.EXPECT().FcpNodeGetNameRequest(ctx).Return("10:00:00:00:00:00:00:01", nil).Times(1) mockAPI.EXPECT().FcpInterfaceGet(ctx, driver.Config.SVM).Return([]string{"10:00:00:00:00:00:00:01"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, volConfig.InternalName).Return("xfs", nil).Times(1) mockAPI.EXPECT().LunGetByName(ctx, volConfig.InternalName).Return(lun, nil).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, driver.Config.IgroupName, volConfig.InternalName).Return(1123, nil).AnyTimes() }, diff --git a/storage_drivers/ontap/ontap_common.go b/storage_drivers/ontap/ontap_common.go index dfa4fb0d8..ae6497bca 100644 --- a/storage_drivers/ontap/ontap_common.go +++ b/storage_drivers/ontap/ontap_common.go @@ -983,20 +983,25 @@ func PublishLUN( } } - // Resolve fstype: prefer volume config (orchestrator), then LUN attribute, then driver default. + // Prefer the LUN attribute fstype (authoritative, records what is on disk) over volConfig + // to catch import mismatches. Fall back to volConfig, then driver default if absent. fstype := volConfig.FileSystem - if fstype == "" { - fstype, err = clientAPI.LunGetFSType(ctx, lunPath) - if err != nil || fstype == "" { - if err != nil { - Logc(ctx).WithError(err).Error("failed to get fstype for LUN") - } + lunFstype, lunErr := clientAPI.LunGetFSType(ctx, lunPath) + if lunErr != nil || lunFstype == "" { + if lunErr != nil { + Logc(ctx).WithError(lunErr).Error("failed to get fstype for LUN") + } + if fstype == "" { Logc(ctx).WithFields(LogFields{ "LUN": lunPath, "fstype": fstype, }).Error("LUN attribute fstype not found, using default.") fstype = drivers.DefaultFileSystemType } + // else: keep volConfig value ("raw", "ext4", "xfs", etc.) — LUN has no attribute + } else { + // LUN attribute is present; it takes precedence over volConfig. + fstype = lunFstype } // Get the format options diff --git a/storage_drivers/ontap/ontap_common_test.go b/storage_drivers/ontap/ontap_common_test.go index 3a2e2a05b..b94f11a00 100644 --- a/storage_drivers/ontap/ontap_common_test.go +++ b/storage_drivers/ontap/ontap_common_test.go @@ -37,6 +37,7 @@ import ( "github.com/netapp/trident/storage_drivers/ontap/api/rest/client/svm" "github.com/netapp/trident/storage_drivers/ontap/api/rest/models" "github.com/netapp/trident/utils/errors" + "github.com/netapp/trident/utils/filesystem" tridentmodels "github.com/netapp/trident/utils/models" ) @@ -6054,7 +6055,8 @@ func TestPublishLun(t *testing.T) { assert.NoError(t, err) assert.Equal(t, tempFormatOptions, publishInfo.FormatOptions) - // Test 11 - volume FileSystem (volConfig) is "xfs", LunGetFSType should NOT be called + // Test 11 - volConfig is "xfs" and LUN attribute also returns "xfs" (normal Trident-created volume). + // LunGetFSType is now always called; the LUN attribute matches volConfig so the result is unchanged. mockAPI = mockapi.NewMockOntapAPI(mockCtrl) publishInfo = &tridentmodels.VolumePublishInfo{ BackendUUID: "fakeBackendUUID", @@ -6064,6 +6066,7 @@ func TestPublishLun(t *testing.T) { HostIQN: []string{"host_iqn"}, } config.FileSystemType = "" + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("xfs", nil) mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("formatOptions", nil) mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) @@ -6078,7 +6081,8 @@ func TestPublishLun(t *testing.T) { assert.Equal(t, "xfs", publishInfo.FilesystemType) assert.Contains(t, publishInfo.MountOptions, "nouuid") - // Test 11b - FileSystem and FormatOptions from vol (as after Create); no LunGetFSType or LunGetAttribute + // Test 11b - volConfig has both FileSystem and FormatOptions (as set after Create); LunGetFSType is + // called (always) and returns matching "xfs". LunGetAttribute is skipped because FormatOptions is set. mockAPI = mockapi.NewMockOntapAPI(mockCtrl) publishInfo = &tridentmodels.VolumePublishInfo{ BackendUUID: "fakeBackendUUID", @@ -6088,6 +6092,7 @@ func TestPublishLun(t *testing.T) { HostIQN: []string{"host_iqn"}, } volFmtOpts := "-b 4096 -T stride=256" + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) @@ -6146,6 +6151,125 @@ func TestPublishLun(t *testing.T) { assert.NoError(t, err) assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) + + // Test 14 - volConfig "raw", LUN attribute returns "ext4": LUN attribute wins. + // Surfaces the real on-disk fstype so the node-side mismatch guard can fire. + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("ext4", nil) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg(filesystem.Raw, "")) + + assert.NoError(t, err) + assert.Equal(t, "ext4", publishInfo.FilesystemType) + + // Test 15 - volConfig "xfs", LUN attribute returns "ext4": LUN attribute wins (fstype mismatch on import). + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("ext4", nil) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg("xfs", "")) + + assert.NoError(t, err) + assert.Equal(t, "ext4", publishInfo.FilesystemType) + + // Test 16 - volConfig "raw", LUN has no attribute (externally-created LUN). + // LunGetFSType returns empty: fall back to volConfig "raw", preserving raw block intent. + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("", nil) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg(filesystem.Raw, "")) + + assert.NoError(t, err) + assert.Equal(t, filesystem.Raw, publishInfo.FilesystemType) + + // Test 17 - volConfig "raw", LunGetFSType returns error (externally-created LUN, API failure). + // Fall back to volConfig "raw", preserving raw block intent. + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("", errors.New("LunGetFSType returned error")) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg(filesystem.Raw, "")) + + assert.NoError(t, err) + assert.Equal(t, filesystem.Raw, publishInfo.FilesystemType) + + // Test 18 - volConfig "ext4", LUN has no attribute (externally-created LUN). + // LunGetFSType returns empty: fall back to volConfig "ext4". + mockAPI = mockapi.NewMockOntapAPI(mockCtrl) + publishInfo = &tridentmodels.VolumePublishInfo{ + BackendUUID: "fakeBackendUUID", + Localhost: false, + Unmanaged: true, + Nodes: nodeList, + HostIQN: []string{"host_iqn"}, + } + mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("", nil) + mockAPI.EXPECT().LunGetAttribute(ctx, lunPath, "formatOptions").Return("", nil) + mockAPI.EXPECT().LunGetByName(ctx, lunPath).Return(dummyLun, nil) + mockAPI.EXPECT().EnsureIgroupAdded(ctx, igroupName, publishInfo.HostIQN[0]) + mockAPI.EXPECT().EnsureLunMapped(ctx, igroupName, lunPath).Return(1111, nil) + mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) + mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) + + err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, + publishVolCfg("ext4", "")) + + assert.NoError(t, err) + assert.Equal(t, "ext4", publishInfo.FilesystemType) } func TestValidateSANDriver(t *testing.T) { diff --git a/storage_drivers/ontap/ontap_san_economy_test.go b/storage_drivers/ontap/ontap_san_economy_test.go index 8303f9d9d..9bc831e51 100644 --- a/storage_drivers/ontap/ontap_san_economy_test.go +++ b/storage_drivers/ontap/ontap_san_economy_test.go @@ -2551,6 +2551,7 @@ func TestOntapSanEconomyVolumePublish(t *testing.T) { mockAPI.EXPECT().IgroupCreate(ctx, gomock.Any(), "iscsi", "linux").Return(nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/volumeName/lunName").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/volumeName/lunName").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -2599,6 +2600,7 @@ func TestOntapSanEconomyVolumePublish_InternalID(t *testing.T) { mockAPI.EXPECT().IgroupCreate(ctx, gomock.Any(), "iscsi", "linux").Return(nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/volumeName/lunName").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/volumeName/lunName").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -2641,6 +2643,7 @@ func TestOntapSanEconomyVolumePublishSLMError(t *testing.T) { gomock.Any()).Times(1).Return(api.Luns{api.Lun{Size: "1g", Name: "lunName", VolumeName: "volumeName"}}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/volumeName/lunName").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/volumeName/lunName").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) diff --git a/storage_drivers/ontap/ontap_san_test.go b/storage_drivers/ontap/ontap_san_test.go index 2853272a8..bd4078a7b 100644 --- a/storage_drivers/ontap/ontap_san_test.go +++ b/storage_drivers/ontap/ontap_san_test.go @@ -700,6 +700,7 @@ func TestOntapSanVolumePublishManaged(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Times(1).Return(&api.Volume{AccessType: VolTypeRW}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -746,6 +747,7 @@ func TestOntapSanVolumePublishUnmanaged(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Times(1).Return(&api.Volume{AccessType: VolTypeRW}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -792,6 +794,7 @@ func TestOntapSanVolumePublishSLMError(t *testing.T) { mockAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Times(1).Return(&api.Volume{AccessType: VolTypeRW}, nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) mockAPI.EXPECT().EnsureIgroupAdded(ctx, gomock.Any(), gomock.Any()).Times(1) mockAPI.EXPECT().EnsureLunMapped(ctx, gomock.Any(), gomock.Any()).Times(1).Return(1, nil) @@ -3836,6 +3839,7 @@ func TestOntapSanVolumePublishisFlexvolRW(t *testing.T) { mockAPI.EXPECT().IgroupCreate(ctx, gomock.Any(), "iscsi", "linux").Return(nil) mockAPI.EXPECT().IscsiNodeGetNameRequest(ctx).Times(1).Return("node1", nil) mockAPI.EXPECT().IscsiInterfaceGet(ctx, gomock.Any()).Return([]string{"iscsi_if"}, nil).Times(1) + mockAPI.EXPECT().LunGetFSType(ctx, "/vol/lunName/lun0").Return("xfs", nil) mockAPI.EXPECT().LunGetByName(ctx, "/vol/lunName/lun0").Return(dummyLun, nil) err := driver.Publish(ctx, &volConfig, publishInfo) From c4690d9e3edbdd5a3deb30de32ca36cb5ae8c82b Mon Sep 17 00:00:00 2001 From: Tori Revilla <52927195+torirevilla@users.noreply.github.com> Date: Thu, 28 May 2026 11:49:31 -0400 Subject: [PATCH 30/30] No controller fallback for fstype PublishLUN will not default to a filesystem type. Import cases will continue to work by inspecting the volume to determine fstype before formatting. --- .github/workflows/github-actions.yml | 13 +++ .../controller_helpers/kubernetes/helper.go | 8 +- frontend/csi/controller_server.go | 3 +- frontend/csi/controller_server_test.go | 3 + frontend/csi/node_server.go | 5 +- storage_drivers/ontap/ontap_common.go | 6 +- storage_drivers/ontap/ontap_common_test.go | 14 +-- utils/fcp/fcp.go | 14 ++- utils/filesystem/utils.go | 32 ++++++- utils/filesystem/utils_test.go | 91 +++++++++++++++++++ utils/iscsi/iscsi.go | 12 +++ utils/models/types.go | 1 + utils/nvme/nvme.go | 13 ++- 13 files changed, 199 insertions(+), 16 deletions(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 13e06e9e7..e7272aa35 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -94,6 +94,19 @@ jobs: mkdir ${{ runner.temp }}\${{ runner.os }}-coverage-binary.out go test -v ./... -covermode=count -- -test.gocoverdir=${{ runner.temp }}\${{ runner.os }}-coverage-binary.out go tool covdata textfmt -i=${{ runner.temp }}\${{ runner.os }}-coverage-binary.out -o ${{ runner.os }}-coverage.out + - if: runner.os == 'Linux' + name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@v1.3.1 + with: + # this might remove tools that are actually needed, + # if set to "true" but frees about 6 GB + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: false + swap-storage: true - if: runner.os != 'Windows' name: Run the tests (not on Windows) run: | diff --git a/frontend/csi/controller_helpers/kubernetes/helper.go b/frontend/csi/controller_helpers/kubernetes/helper.go index 538075511..2d187d874 100644 --- a/frontend/csi/controller_helpers/kubernetes/helper.go +++ b/frontend/csi/controller_helpers/kubernetes/helper.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package kubernetes @@ -140,6 +140,9 @@ func (h *helper) GetVolumeConfig( Logc(ctx).WithError(err).Error("Invalid storage class parameters for LUKS volume import.") return nil, err } + if getAnnotation(annotations, AnnFileSystem) == "" { + return nil, fmt.Errorf("imported LUKS encrypted volume must provide file system type annotation") + } } } @@ -862,6 +865,9 @@ func getVolumeConfig( if _, err = strconv.ParseBool(luksEncryption); err != nil { Logc(ctx).WithError(err).Warning("Unable to parse luks annotation into bool.") } + if getAnnotation(annotations, AnnFileSystem) == "" { + Logc(ctx).Warning("Imported LUKS encrypted volume must provide file system type annotation.") + } } } diff --git a/frontend/csi/controller_server.go b/frontend/csi/controller_server.go index d9734a127..e37b69424 100644 --- a/frontend/csi/controller_server.go +++ b/frontend/csi/controller_server.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package csi @@ -408,6 +408,7 @@ func (p *Plugin) ControllerPublishVolume( case tridentconfig.Block: publishInfo["LUKSEncryption"] = volumePublishInfo.LUKSEncryption publishInfo["sharedTarget"] = strconv.FormatBool(volumePublishInfo.SharedTarget) + publishInfo["volumeMode"] = string(volume.Config.VolumeMode) if volumePublishInfo.SANType == sa.NVMe { // fill in only NVMe specific fields in publishInfo diff --git a/frontend/csi/controller_server_test.go b/frontend/csi/controller_server_test.go index 7e09ae53d..2a3119510 100644 --- a/frontend/csi/controller_server_test.go +++ b/frontend/csi/controller_server_test.go @@ -1073,6 +1073,7 @@ func TestControllerPublishVolume(t *testing.T) { "SANType": sa.NVMe, "sharedTarget": "false", "nvmeTargetIPs": "", + "volumeMode": "", }, }, expErrCode: codes.OK, @@ -1110,6 +1111,7 @@ func TestControllerPublishVolume(t *testing.T) { "SANType": sa.ISCSI, "sharedTarget": "false", "useCHAP": "false", + "volumeMode": "", }, }, expErrCode: codes.OK, @@ -1141,6 +1143,7 @@ func TestControllerPublishVolume(t *testing.T) { "protocol": "block", "sharedTarget": "false", "useCHAP": "false", + "volumeMode": "", }, }, expErrCode: codes.OK, diff --git a/frontend/csi/node_server.go b/frontend/csi/node_server.go index ab5e3baec..43518c5bf 100644 --- a/frontend/csi/node_server.go +++ b/frontend/csi/node_server.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package csi @@ -1327,6 +1327,7 @@ func (p *Plugin) nodeStageFCPVolume( publishInfo.FCPLunSerial = req.PublishContext["fcpLunSerial"] publishInfo.FCPIgroup = req.PublishContext["fcpIgroup"] publishInfo.SANType = req.PublishContext["SANType"] + publishInfo.VolumeMode = req.PublishContext["volumeMode"] volumeId, stagingTargetPath, err := p.getVolumeIdAndStagingPath(req) if err != nil { @@ -1765,6 +1766,7 @@ func (p *Plugin) nodeStageISCSIVolume( publishInfo.IscsiLunSerial = req.PublishContext["iscsiLunSerial"] publishInfo.IscsiInterface = req.PublishContext["iscsiInterface"] publishInfo.IscsiIgroup = req.PublishContext["iscsiIgroup"] + publishInfo.VolumeMode = req.PublishContext["volumeMode"] if useCHAP { publishInfo.IscsiUsername = req.PublishContext["iscsiUsername"] @@ -2986,6 +2988,7 @@ func (p *Plugin) nodeStageNVMeVolume( publishInfo.NVMeTargetIPs = strings.Split(req.PublishContext["nvmeTargetIPs"], ",") publishInfo.SANType = req.PublishContext["SANType"] publishInfo.FormatOptions = req.PublishContext["formatOptions"] + publishInfo.VolumeMode = req.PublishContext["volumeMode"] err := p.nvmeHandler.AttachNVMeVolumeRetry( ctx, req.VolumeContext["internalName"], "", publishInfo, req.GetSecrets(), nvme.NVMeAttachTimeout, diff --git a/storage_drivers/ontap/ontap_common.go b/storage_drivers/ontap/ontap_common.go index ae6497bca..224350dd6 100644 --- a/storage_drivers/ontap/ontap_common.go +++ b/storage_drivers/ontap/ontap_common.go @@ -984,7 +984,7 @@ func PublishLUN( } // Prefer the LUN attribute fstype (authoritative, records what is on disk) over volConfig - // to catch import mismatches. Fall back to volConfig, then driver default if absent. + // to catch import mismatches. Fall back to volConfig. fstype := volConfig.FileSystem lunFstype, lunErr := clientAPI.LunGetFSType(ctx, lunPath) if lunErr != nil || lunFstype == "" { @@ -995,14 +995,14 @@ func PublishLUN( Logc(ctx).WithFields(LogFields{ "LUN": lunPath, "fstype": fstype, - }).Error("LUN attribute fstype not found, using default.") - fstype = drivers.DefaultFileSystemType + }).Warning("LUN attribute fstype not found.") } // else: keep volConfig value ("raw", "ext4", "xfs", etc.) — LUN has no attribute } else { // LUN attribute is present; it takes precedence over volConfig. fstype = lunFstype } + publishInfo.VolumeMode = string(volConfig.VolumeMode) // Get the format options // An example of how formatOption may look like: diff --git a/storage_drivers/ontap/ontap_common_test.go b/storage_drivers/ontap/ontap_common_test.go index b94f11a00..a6571d40f 100644 --- a/storage_drivers/ontap/ontap_common_test.go +++ b/storage_drivers/ontap/ontap_common_test.go @@ -5929,7 +5929,7 @@ func TestPublishLun(t *testing.T) { mockAPI.EXPECT().LunMapGetReportingNodes(ctx, igroupName, lunPath).Return([]string{"Node1"}, nil) mockAPI.EXPECT().GetSLMDataLifs(ctx, ips, []string{"Node1"}).Return([]string{}, nil) - err := PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) + err := PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, publishVolCfg("xfs", "")) assert.NoError(t, err) @@ -5942,7 +5942,7 @@ func TestPublishLun(t *testing.T) { assert.Error(t, err) - // Test 3 - LunGetFSType returns error + // Test 3 - LunGetFSType returns error, fstype will fall back to fstype in vol config mockAPI = mockapi.NewMockOntapAPI(mockCtrl) publishInfo.HostIQN = []string{"host_iqn"} mockAPI.EXPECT().LunGetFSType(ctx, lunPath).Return("", errors.New("LunGetFSType returned error")) @@ -5956,7 +5956,7 @@ func TestPublishLun(t *testing.T) { err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) - assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) + assert.Equal(t, "", publishInfo.FilesystemType) // Test 4 - LunGetAttribute returns error (fstype still from LUN) mockAPI = mockapi.NewMockOntapAPI(mockCtrl) @@ -6106,7 +6106,7 @@ func TestPublishLun(t *testing.T) { assert.Equal(t, volFmtOpts, publishInfo.FormatOptions) assert.Contains(t, publishInfo.MountOptions, "nouuid") - // Test 12 - volume fstype empty, LunGetFSType returns empty string with no error, use default + // Test 12 - volume fstype empty, LunGetFSType returns empty string with no error, expect empty mockAPI = mockapi.NewMockOntapAPI(mockCtrl) publishInfo = &tridentmodels.VolumePublishInfo{ BackendUUID: "fakeBackendUUID", @@ -6127,9 +6127,9 @@ func TestPublishLun(t *testing.T) { err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) - assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) + assert.Equal(t, "", publishInfo.FilesystemType) - // Test 13 - volume fstype empty, LunGetFSType returns error, use default + // Test 13 - volume fstype empty, LunGetFSType returns error, will be empty mockAPI = mockapi.NewMockOntapAPI(mockCtrl) publishInfo = &tridentmodels.VolumePublishInfo{ BackendUUID: "fakeBackendUUID", @@ -6150,7 +6150,7 @@ func TestPublishLun(t *testing.T) { err = PublishLUN(ctx, mockAPI, config, ips, publishInfo, lunPath, igroupName, iSCSINodeName, volNoFsOrFmt) assert.NoError(t, err) - assert.Equal(t, drivers.DefaultFileSystemType, publishInfo.FilesystemType) + assert.Equal(t, "", publishInfo.FilesystemType) // Test 14 - volConfig "raw", LUN attribute returns "ext4": LUN attribute wins. // Surfaces the real on-disk fstype so the node-side mismatch guard can fire. diff --git a/utils/fcp/fcp.go b/utils/fcp/fcp.go index d619b8149..f5faacaa8 100644 --- a/utils/fcp/fcp.go +++ b/utils/fcp/fcp.go @@ -1,4 +1,4 @@ -// Copyright 2024 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package fcp @@ -507,6 +507,7 @@ func (client *Client) AttachVolume( devicePath = luksDevice.MappedDevicePath() } + // No filesystem work is required for raw block; return early. if publishInfo.FilesystemType == filesystem.Raw { return mpathSize, nil } @@ -521,6 +522,17 @@ func (client *Client) AttachVolume( } } + fstype, err := filesystem.DetermineFSType(publishInfo.FilesystemType, existingFstype, publishInfo.VolumeMode) + if err != nil { + return mpathSize, fmt.Errorf("LUN %s, device %s is formatted with unknown filesystem type", name, devicePath) + } + publishInfo.FilesystemType = fstype + + // No filesystem work is required for raw block; return early. + if publishInfo.FilesystemType == filesystem.Raw { + return mpathSize, nil + } + if existingFstype == "" { if !isLUKSDevice { if unformatted, err := client.deviceClient.IsDeviceUnformatted(ctx, devicePath); err != nil { diff --git a/utils/filesystem/utils.go b/utils/filesystem/utils.go index 6892501bb..371a8fbc4 100644 --- a/utils/filesystem/utils.go +++ b/utils/filesystem/utils.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package filesystem @@ -6,6 +6,8 @@ import ( "fmt" "regexp" "strings" + + "github.com/netapp/trident/config" ) var permsRegex = regexp.MustCompile(`^[0-7]{4}$`) @@ -27,3 +29,31 @@ func ValidateOctalUnixPermissions(perms string) error { } return nil } + +// DetermineFSType resolves the effective filesystem type to use for a volume. +// Priority order: +// 1. publishInfoType — use it as-is when provided (explicit caller intent). +// 2. existingType — adopt the type already on disk when it is known (non-empty, +// non-UnknownFstype), so that subsequent format/mount logic stays consistent. +// 3. volumeMode fallback — when no usable on-disk type is available, default to +// ext4 for Filesystem volumes or raw for Block volumes. A Filesystem volume +// whose on-disk type is UnknownFstype still defaults to ext4 but also returns +// an error because the LUN's existing format is unrecognisable. +func DetermineFSType(publishInfoType, existingType, volumeMode string) (string, error) { + fsType := publishInfoType + if publishInfoType == "" { + switch { + case existingType != "" && existingType != UnknownFstype: + // Adopt whatever is on disk so subsequent format/mount logic uses it. + fsType = existingType + case volumeMode == string(config.Filesystem): + if existingType == UnknownFstype { + return "", fmt.Errorf("LUN is formatted with unknown filesystem type") + } + fsType = Ext4 + case volumeMode == string(config.RawBlock): + fsType = Raw + } + } + return fsType, nil +} diff --git a/utils/filesystem/utils_test.go b/utils/filesystem/utils_test.go index 66d637818..d9a358b3a 100644 --- a/utils/filesystem/utils_test.go +++ b/utils/filesystem/utils_test.go @@ -6,6 +6,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/netapp/trident/config" ) func TestVerifyFilesystemSupport(t *testing.T) { @@ -66,3 +68,92 @@ func TestValidateOctalUnixPermissions(t *testing.T) { assert.Equal(t, test.errNotNil, err != nil) } } + +func TestDetermineFSType(t *testing.T) { + fsVolumeMode := string(config.Filesystem) + blockVolumeMode := string(config.RawBlock) + + tests := []struct { + name string + publishInfoType string + existingType string + volumeMode string + expectedFSType string + errNotNil bool + }{ + // publishInfoType takes precedence regardless of other inputs. + { + name: "publishInfoType provided — returned directly", + publishInfoType: Xfs, + existingType: "", + volumeMode: fsVolumeMode, + expectedFSType: Xfs, + }, + { + name: "publishInfoType overrides existing on-disk type", + publishInfoType: Ext4, + existingType: Xfs, + volumeMode: fsVolumeMode, + expectedFSType: Ext4, + }, + + // No publishInfoType — adopt the known on-disk type. + { + name: "existing xfs adopted when publishInfoType absent", + existingType: Xfs, + volumeMode: fsVolumeMode, + expectedFSType: Xfs, + }, + { + name: "existing ext3 adopted when publishInfoType absent", + existingType: Ext3, + volumeMode: fsVolumeMode, + expectedFSType: Ext3, + }, + + // No publishInfoType, no usable existing type — fall back by volumeMode. + { + name: "filesystem volume with empty existing type defaults to ext4", + existingType: "", + volumeMode: fsVolumeMode, + expectedFSType: Ext4, + }, + { + name: "raw-block volume with empty existing type defaults to raw", + existingType: "", + volumeMode: blockVolumeMode, + expectedFSType: Raw, + }, + { + name: "raw-block volume with unknown existing type defaults to raw", + existingType: UnknownFstype, + volumeMode: blockVolumeMode, + expectedFSType: Raw, + }, + + // Filesystem volume with an unknown on-disk type: default to ext4 but surface an error. + { + name: "filesystem volume with unknown existing type returns ext4 and error", + existingType: UnknownFstype, + volumeMode: fsVolumeMode, + expectedFSType: "", + errNotNil: true, + }, + + // No publishInfoType, no matching volumeMode — nothing to fall back to. + { + name: "empty inputs yield empty fsType and no error", + existingType: "", + volumeMode: "", + expectedFSType: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fsType, err := DetermineFSType(tt.publishInfoType, tt.existingType, tt.volumeMode) + assert.Equal(t, tt.expectedFSType, fsType) + assert.Equal(t, tt.errNotNil, err != nil) + }) + } +} diff --git a/utils/iscsi/iscsi.go b/utils/iscsi/iscsi.go index 4b29e1dde..9ea589856 100644 --- a/utils/iscsi/iscsi.go +++ b/utils/iscsi/iscsi.go @@ -434,6 +434,7 @@ func (client *Client) AttachVolume( return mpathSize, errors.New("device should be a LUKS device but is not LUKS formatted") } + // No filesystem work is required for raw block; return early. if publishInfo.FilesystemType == filesystem.Raw { return mpathSize, nil } @@ -448,6 +449,17 @@ func (client *Client) AttachVolume( } } + fstype, err := filesystem.DetermineFSType(publishInfo.FilesystemType, existingFstype, publishInfo.VolumeMode) + if err != nil { + return mpathSize, fmt.Errorf("LUN %s, device %s is formatted with unknown filesystem type", name, devicePath) + } + publishInfo.FilesystemType = fstype + + // No filesystem work is required for raw block; return early. + if publishInfo.FilesystemType == filesystem.Raw { + return mpathSize, nil + } + if existingFstype == "" { if !isLUKSDevice { if unformatted, err := client.devices.IsDeviceUnformatted(ctx, devicePath); err != nil { diff --git a/utils/models/types.go b/utils/models/types.go index 9bf496979..c3b120c21 100644 --- a/utils/models/types.go +++ b/utils/models/types.go @@ -216,6 +216,7 @@ type VolumePublishInfo struct { TridentUUID string `json:"tridentUUID,omitempty"` // NOTE: Added in 22.07 release LUKSEncryption string `json:"LUKSEncryption,omitempty"` SANType string `json:"SANType,omitempty"` + VolumeMode string `json:"volumeMode,omitempty"` VolumeAccessInfo } diff --git a/utils/nvme/nvme.go b/utils/nvme/nvme.go index 380f47d28..c76c6e442 100644 --- a/utils/nvme/nvme.go +++ b/utils/nvme/nvme.go @@ -1,4 +1,4 @@ -// Copyright 2025 NetApp, Inc. All Rights Reserved. +// Copyright 2026 NetApp, Inc. All Rights Reserved. package nvme @@ -379,6 +379,17 @@ func (nh *NVMeHandler) NVMeMountVolume( } } + fstype, err := filesystem.DetermineFSType(publishInfo.FilesystemType, existingFstype, publishInfo.VolumeMode) + if err != nil { + return fmt.Errorf("LUN %s, device %s is formatted with unknown filesystem type", name, devicePath) + } + publishInfo.FilesystemType = fstype + + // No filesystem work is required for raw block; return early. + if publishInfo.FilesystemType == filesystem.Raw { + return nil + } + if existingFstype == "" { if !isLUKSDevice { if unformatted, err := nh.devicesClient.IsDeviceUnformatted(ctx, devicePath); err != nil {