Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
09600095
Commit
09600095
authored
Sep 22, 2015
by
markturansky
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
allow pre-binding of persistent volumes to pvclaims
parent
efedcb6c
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
115 additions
and
12 deletions
+115
-12
persistentvolume_index_test.go
...ontroller/persistentvolume/persistentvolume_index_test.go
+75
-0
types.go
pkg/controller/persistentvolume/types.go
+40
-12
No files found.
pkg/controller/persistentvolume/persistentvolume_index_test.go
View file @
09600095
...
...
@@ -484,3 +484,78 @@ func createTestVolumes() []*api.PersistentVolume {
},
}
}
func
TestFindingPreboundVolumes
(
t
*
testing
.
T
)
{
pv1
:=
&
api
.
PersistentVolume
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"pv1"
,
Annotations
:
map
[
string
]
string
{},
},
Spec
:
api
.
PersistentVolumeSpec
{
Capacity
:
api
.
ResourceList
{
api
.
ResourceName
(
api
.
ResourceStorage
)
:
resource
.
MustParse
(
"1Gi"
)},
PersistentVolumeSource
:
api
.
PersistentVolumeSource
{
HostPath
:
&
api
.
HostPathVolumeSource
{}},
AccessModes
:
[]
api
.
PersistentVolumeAccessMode
{
api
.
ReadWriteOnce
},
},
}
pv5
:=
&
api
.
PersistentVolume
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"pv5"
,
Annotations
:
map
[
string
]
string
{},
},
Spec
:
api
.
PersistentVolumeSpec
{
Capacity
:
api
.
ResourceList
{
api
.
ResourceName
(
api
.
ResourceStorage
)
:
resource
.
MustParse
(
"5Gi"
)},
PersistentVolumeSource
:
api
.
PersistentVolumeSource
{
HostPath
:
&
api
.
HostPathVolumeSource
{}},
AccessModes
:
[]
api
.
PersistentVolumeAccessMode
{
api
.
ReadWriteOnce
},
},
}
pv8
:=
&
api
.
PersistentVolume
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"pv8"
,
Annotations
:
map
[
string
]
string
{},
},
Spec
:
api
.
PersistentVolumeSpec
{
Capacity
:
api
.
ResourceList
{
api
.
ResourceName
(
api
.
ResourceStorage
)
:
resource
.
MustParse
(
"8Gi"
)},
PersistentVolumeSource
:
api
.
PersistentVolumeSource
{
HostPath
:
&
api
.
HostPathVolumeSource
{}},
AccessModes
:
[]
api
.
PersistentVolumeAccessMode
{
api
.
ReadWriteOnce
},
},
}
claim
:=
&
api
.
PersistentVolumeClaim
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"claim01"
,
Namespace
:
"myns"
,
},
Spec
:
api
.
PersistentVolumeClaimSpec
{
AccessModes
:
[]
api
.
PersistentVolumeAccessMode
{
api
.
ReadWriteOnce
},
Resources
:
api
.
ResourceRequirements
{
Requests
:
api
.
ResourceList
{
api
.
ResourceName
(
api
.
ResourceStorage
)
:
resource
.
MustParse
(
"1Gi"
)}},
},
}
index
:=
NewPersistentVolumeOrderedIndex
()
index
.
Add
(
pv1
)
index
.
Add
(
pv5
)
index
.
Add
(
pv8
)
// expected exact match on size
volume
,
_
:=
index
.
FindBestMatchForClaim
(
claim
)
if
volume
.
Name
!=
pv1
.
Name
{
t
.
Errorf
(
"Expected %s but got volume %s instead"
,
pv1
.
Name
,
volume
.
Name
)
}
// pretend the exact match is pre-bound. should get the next size up.
pv1
.
Annotations
[
createdForKey
]
=
"some/other/claim"
volume
,
_
=
index
.
FindBestMatchForClaim
(
claim
)
if
volume
.
Name
!=
pv5
.
Name
{
t
.
Errorf
(
"Expected %s but got volume %s instead"
,
pv5
.
Name
,
volume
.
Name
)
}
// pretend the exact match is available but the largest volume is pre-bound to the claim.
delete
(
pv1
.
Annotations
,
createdForKey
)
pv8
.
Annotations
[
createdForKey
]
=
"myns/claim01"
volume
,
_
=
index
.
FindBestMatchForClaim
(
claim
)
if
volume
.
Name
!=
pv8
.
Name
{
t
.
Errorf
(
"Expected %s but got volume %s instead"
,
pv8
.
Name
,
volume
.
Name
)
}
}
pkg/controller/persistentvolume/types.go
View file @
09600095
...
...
@@ -25,6 +25,13 @@ import (
"k8s.io/kubernetes/pkg/client/cache"
)
const
(
// A PV created specifically for one claim must contain this annotation in order to bind to the claim.
// The value must be the name of the claim being bound to.
// This is an experimental feature and likely to change in the future.
createdForKey
=
"persistent-volume-provisioning.experimental.kubernetes.io/created-for"
)
// persistentVolumeOrderedIndex is a cache.Store that keeps persistent volumes indexed by AccessModes and ordered by storage capacity.
type
persistentVolumeOrderedIndex
struct
{
cache
.
Indexer
...
...
@@ -73,8 +80,8 @@ func (pvIndex *persistentVolumeOrderedIndex) ListByAccessModes(modes []api.Persi
type
matchPredicate
func
(
compareThis
,
toThis
*
api
.
PersistentVolume
)
bool
// Find returns the nearest PV from the ordered list or nil if a match is not found
func
(
pvIndex
*
persistentVolumeOrderedIndex
)
Find
(
pv
*
api
.
PersistentVolume
,
matchPredicate
matchPredicate
)
(
*
api
.
PersistentVolume
,
error
)
{
// the '
pv
' argument is a synthetic PV with capacity and accessmodes set according to the user's PersistentVolumeClaim.
func
(
pvIndex
*
persistentVolumeOrderedIndex
)
Find
(
searchPV
*
api
.
PersistentVolume
,
matchPredicate
matchPredicate
)
(
*
api
.
PersistentVolume
,
error
)
{
// the '
searchPV
' argument is a synthetic PV with capacity and accessmodes set according to the user's PersistentVolumeClaim.
// the synthetic pv arg is, therefore, a request for a storage resource.
//
// PVs are indexed by their access modes to allow easier searching. Each index is the string representation of a set of access modes.
...
...
@@ -85,7 +92,7 @@ func (pvIndex *persistentVolumeOrderedIndex) Find(pv *api.PersistentVolume, matc
//
// Searches are performed against a set of access modes, so we can attempt not only the exact matching modes but also
// potential matches (the GCEPD example above).
allPossibleModes
:=
pvIndex
.
allPossibleMatchingAccessModes
(
pv
.
Spec
.
AccessModes
)
allPossibleModes
:=
pvIndex
.
allPossibleMatchingAccessModes
(
searchPV
.
Spec
.
AccessModes
)
for
_
,
modes
:=
range
allPossibleModes
{
volumes
,
err
:=
pvIndex
.
ListByAccessModes
(
modes
)
...
...
@@ -93,16 +100,32 @@ func (pvIndex *persistentVolumeOrderedIndex) Find(pv *api.PersistentVolume, matc
return
nil
,
err
}
// volumes are sorted by size but some may be bound.
// remove bound volumes for easy binary search by size
// volumes are sorted by size but some may be bound or earmarked for a specific claim.
// filter those volumes for easy binary search by size
// return the exact pre-binding match, if found
unboundVolumes
:=
[]
*
api
.
PersistentVolume
{}
for
_
,
v
:=
range
volumes
{
if
v
.
Spec
.
ClaimRef
==
nil
{
unboundVolumes
=
append
(
unboundVolumes
,
v
)
for
_
,
volume
:=
range
volumes
{
// check for current binding
if
volume
.
Spec
.
ClaimRef
!=
nil
{
continue
}
// check for pre-bind where the volume is intended for one specific claim
if
createdFor
,
ok
:=
volume
.
Annotations
[
createdForKey
];
ok
{
if
createdFor
!=
searchPV
.
Annotations
[
createdForKey
]
{
// the volume is pre-bound and does not match the search criteria.
continue
}
else
{
// exact annotation match! No search required.
return
volume
,
nil
}
}
// volume isn't currently bound or pre-bound.
unboundVolumes
=
append
(
unboundVolumes
,
volume
)
}
i
:=
sort
.
Search
(
len
(
unboundVolumes
),
func
(
i
int
)
bool
{
return
matchPredicate
(
pv
,
unboundVolumes
[
i
])
})
i
:=
sort
.
Search
(
len
(
unboundVolumes
),
func
(
i
int
)
bool
{
return
matchPredicate
(
searchPV
,
unboundVolumes
[
i
])
})
if
i
<
len
(
unboundVolumes
)
{
return
unboundVolumes
[
i
],
nil
}
...
...
@@ -110,9 +133,14 @@ func (pvIndex *persistentVolumeOrderedIndex) Find(pv *api.PersistentVolume, matc
return
nil
,
nil
}
//
F
indByAccessModesAndStorageCapacity is a convenience method that calls Find w/ requisite matchPredicate for storage
func
(
pvIndex
*
persistentVolumeOrderedIndex
)
FindByAccessModesAndStorageCapacity
(
modes
[]
api
.
PersistentVolumeAccessMode
,
qty
resource
.
Quantity
)
(
*
api
.
PersistentVolume
,
error
)
{
//
f
indByAccessModesAndStorageCapacity is a convenience method that calls Find w/ requisite matchPredicate for storage
func
(
pvIndex
*
persistentVolumeOrderedIndex
)
findByAccessModesAndStorageCapacity
(
prebindKey
string
,
modes
[]
api
.
PersistentVolumeAccessMode
,
qty
resource
.
Quantity
)
(
*
api
.
PersistentVolume
,
error
)
{
pv
:=
&
api
.
PersistentVolume
{
ObjectMeta
:
api
.
ObjectMeta
{
Annotations
:
map
[
string
]
string
{
createdForKey
:
prebindKey
,
},
},
Spec
:
api
.
PersistentVolumeSpec
{
AccessModes
:
modes
,
Capacity
:
api
.
ResourceList
{
...
...
@@ -125,7 +153,7 @@ func (pvIndex *persistentVolumeOrderedIndex) FindByAccessModesAndStorageCapacity
// FindBestMatchForClaim is a convenience method that finds a volume by the claim's AccessModes and requests for Storage
func
(
pvIndex
*
persistentVolumeOrderedIndex
)
FindBestMatchForClaim
(
claim
*
api
.
PersistentVolumeClaim
)
(
*
api
.
PersistentVolume
,
error
)
{
return
pvIndex
.
FindByAccessModesAndStorageCapacity
(
claim
.
Spec
.
AccessModes
,
claim
.
Spec
.
Resources
.
Requests
[
api
.
ResourceName
(
api
.
ResourceStorage
)])
return
pvIndex
.
findByAccessModesAndStorageCapacity
(
fmt
.
Sprintf
(
"%s/%s"
,
claim
.
Namespace
,
claim
.
Name
),
claim
.
Spec
.
AccessModes
,
claim
.
Spec
.
Resources
.
Requests
[
api
.
ResourceName
(
api
.
ResourceStorage
)])
}
// byCapacity is used to order volumes by ascending storage size
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment