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
de37da85
Commit
de37da85
authored
May 16, 2018
by
wojtekt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor cache based manager
parent
a0b1d4de
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
488 additions
and
150 deletions
+488
-150
BUILD
pkg/kubelet/BUILD
+1
-0
kubelet.go
pkg/kubelet/kubelet.go
+2
-1
BUILD
pkg/kubelet/secret/BUILD
+5
-8
secret_manager.go
pkg/kubelet/secret/secret_manager.go
+40
-67
secret_manager_test.go
pkg/kubelet/secret/secret_manager_test.go
+156
-0
BUILD
pkg/kubelet/util/BUILD
+1
-0
BUILD
pkg/kubelet/util/manager/BUILD
+54
-0
cache_based_manager.go
pkg/kubelet/util/manager/cache_based_manager.go
+127
-61
cache_based_manager_test.go
pkg/kubelet/util/manager/cache_based_manager_test.go
+46
-13
manager.go
pkg/kubelet/util/manager/manager.go
+56
-0
No files found.
pkg/kubelet/BUILD
View file @
de37da85
...
...
@@ -83,6 +83,7 @@ go_library(
"//pkg/kubelet/types:go_default_library",
"//pkg/kubelet/util:go_default_library",
"//pkg/kubelet/util/format:go_default_library",
"//pkg/kubelet/util/manager:go_default_library",
"//pkg/kubelet/util/queue:go_default_library",
"//pkg/kubelet/util/sliceutils:go_default_library",
"//pkg/kubelet/volumemanager:go_default_library",
...
...
pkg/kubelet/kubelet.go
View file @
de37da85
...
...
@@ -92,6 +92,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/sysctl"
kubetypes
"k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/kubelet/util/format"
"k8s.io/kubernetes/pkg/kubelet/util/manager"
"k8s.io/kubernetes/pkg/kubelet/util/queue"
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
"k8s.io/kubernetes/pkg/kubelet/volumemanager"
...
...
@@ -542,7 +543,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
}
secretManager
:=
secret
.
NewCachingSecretManager
(
kubeDeps
.
KubeClient
,
secret
.
GetObjectTTLFromNodeFunc
(
klet
.
GetNode
))
kubeDeps
.
KubeClient
,
manager
.
GetObjectTTLFromNodeFunc
(
klet
.
GetNode
))
klet
.
secretManager
=
secretManager
configMapManager
:=
configmap
.
NewCachingConfigMapManager
(
...
...
pkg/kubelet/secret/BUILD
View file @
de37da85
...
...
@@ -8,37 +8,34 @@ load(
go_test(
name = "go_default_test",
srcs = ["
caching_
secret_manager_test.go"],
srcs = ["secret_manager_test.go"],
embed = [":go_default_library"],
deps = [
"//
vendor/github.com/stretchr/testify/assert
:go_default_library",
"//
pkg/kubelet/util/manager
:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"caching_secret_manager.go",
"fake_manager.go",
"secret_manager.go",
],
importpath = "k8s.io/kubernetes/pkg/kubelet/secret",
deps = [
"//pkg/api/v1/pod:go_default_library",
"//pkg/kubelet/util:go_default_library",
"//pkg/kubelet/util
/manager
:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)
...
...
pkg/kubelet/secret/secret_manager.go
View file @
de37da85
...
...
@@ -17,13 +17,17 @@ limitations under the License.
package
secret
import
(
"sync"
"fmt"
"time"
"k8s.io/api/core/v1"
clientset
"k8s.io/client-go/kubernetes"
podutil
"k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/kubelet/util/manager"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/sets"
)
...
...
@@ -42,11 +46,6 @@ type Manager interface {
UnregisterPod
(
pod
*
v1
.
Pod
)
}
type
objectKey
struct
{
namespace
string
name
string
}
// simpleSecretManager implements SecretManager interfaces with
// simple operations to apiserver.
type
simpleSecretManager
struct
{
...
...
@@ -67,42 +66,31 @@ func (s *simpleSecretManager) RegisterPod(pod *v1.Pod) {
func
(
s
*
simpleSecretManager
)
UnregisterPod
(
pod
*
v1
.
Pod
)
{
}
// store is the interface for a secrets cache that
// can be used by cacheBasedSecretManager.
type
store
interface
{
// AddReference adds a reference to the secret to the store.
// Note that multiple additions to the store has to be allowed
// in the implementations and effectively treated as refcounted.
AddReference
(
namespace
,
name
string
)
// DeleteReference deletes reference to the secret from the store.
// Note that secret should be deleted only when there was a
// corresponding Delete call for each of Add calls (effectively
// when refcount was reduced to zero).
DeleteReference
(
namespace
,
name
string
)
// Get a secret from a store.
Get
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
}
// cachingBasedSecretManager keeps a store with secrets necessary
// cachingSecretManager keeps a store with secrets necessary
// for registered pods. Different implementations of the store
// may result in different semantics for freshness of secrets
// (e.g. ttl-based implementation vs watch-based implementation).
type
cacheBasedSecretManager
struct
{
secretStore
store
lock
sync
.
Mutex
registeredPods
map
[
objectKey
]
*
v1
.
Pod
type
cachingSecretManager
struct
{
manager
manager
.
Manager
}
func
newCacheBasedSecretManager
(
secretStore
store
)
Manager
{
return
&
cacheBasedSecretManager
{
secretStore
:
secretStore
,
registeredPods
:
make
(
map
[
objectKey
]
*
v1
.
Pod
),
func
(
c
*
cachingSecretManager
)
GetSecret
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
{
object
,
err
:=
c
.
manager
.
GetObject
(
namespace
,
name
)
if
err
!=
nil
{
return
nil
,
err
}
if
secret
,
ok
:=
object
.
(
*
v1
.
Secret
);
ok
{
return
secret
,
nil
}
return
nil
,
fmt
.
Errorf
(
"unexpected object type: %v"
,
object
)
}
func
(
c
*
cacheBasedSecretManager
)
GetSecret
(
namespace
,
name
string
)
(
*
v1
.
Secret
,
error
)
{
return
c
.
secretStore
.
Get
(
namespace
,
name
)
func
(
c
*
cachingSecretManager
)
RegisterPod
(
pod
*
v1
.
Pod
)
{
c
.
manager
.
RegisterPod
(
pod
)
}
func
(
c
*
cachingSecretManager
)
UnregisterPod
(
pod
*
v1
.
Pod
)
{
c
.
manager
.
UnregisterPod
(
pod
)
}
func
getSecretNames
(
pod
*
v1
.
Pod
)
sets
.
String
{
...
...
@@ -114,39 +102,24 @@ func getSecretNames(pod *v1.Pod) sets.String {
return
result
}
func
(
c
*
cacheBasedSecretManager
)
RegisterPod
(
pod
*
v1
.
Pod
)
{
names
:=
getSecretNames
(
pod
)
c
.
lock
.
Lock
()
defer
c
.
lock
.
Unlock
()
for
name
:=
range
names
{
c
.
secretStore
.
AddReference
(
pod
.
Namespace
,
name
)
}
var
prev
*
v1
.
Pod
key
:=
objectKey
{
namespace
:
pod
.
Namespace
,
name
:
pod
.
Name
}
prev
=
c
.
registeredPods
[
key
]
c
.
registeredPods
[
key
]
=
pod
if
prev
!=
nil
{
for
name
:=
range
getSecretNames
(
prev
)
{
// On an update, the .Add() call above will have re-incremented the
// ref count of any existing secrets, so any secrets that are in both
// names and prev need to have their ref counts decremented. Any that
// are only in prev need to be completely removed. This unconditional
// call takes care of both cases.
c
.
secretStore
.
DeleteReference
(
prev
.
Namespace
,
name
)
}
}
}
const
(
defaultTTL
=
time
.
Minute
)
func
(
c
*
cacheBasedSecretManager
)
UnregisterPod
(
pod
*
v1
.
Pod
)
{
var
prev
*
v1
.
Pod
key
:=
objectKey
{
namespace
:
pod
.
Namespace
,
name
:
pod
.
Name
}
c
.
lock
.
Lock
()
defer
c
.
lock
.
Unlock
()
prev
=
c
.
registeredPods
[
key
]
delete
(
c
.
registeredPods
,
key
)
if
prev
!=
nil
{
for
name
:=
range
getSecretNames
(
prev
)
{
c
.
secretStore
.
DeleteReference
(
prev
.
Namespace
,
name
)
}
// NewCacheBasedManager creates a manager that keeps a cache of all secrets
// necessary for registered pods.
// It implements the following logic:
// - whenever a pod is created or updated, the cached versions of all secrets
// are invalidated
// - every GetObject() call tries to fetch the value from local cache; if it is
// not there, invalidated or too old, we fetch it from apiserver and refresh the
// value in cache; otherwise it is just fetched from cache
func
NewCachingSecretManager
(
kubeClient
clientset
.
Interface
,
getTTL
manager
.
GetObjectTTLFunc
)
Manager
{
getSecret
:=
func
(
namespace
,
name
string
,
opts
metav1
.
GetOptions
)
(
runtime
.
Object
,
error
)
{
return
kubeClient
.
CoreV1
()
.
Secrets
(
namespace
)
.
Get
(
name
,
opts
)
}
secretStore
:=
manager
.
NewObjectStore
(
getSecret
,
clock
.
RealClock
{},
getTTL
,
defaultTTL
)
return
&
cachingSecretManager
{
manager
:
manager
.
NewCacheBasedManager
(
secretStore
,
getSecretNames
),
}
}
pkg/kubelet/secret/secret_manager_test.go
0 → 100644
View file @
de37da85
/*
Copyright 2018 The Kubernetes Authors.
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.
*/
package
secret
import
(
"fmt"
"strings"
"testing"
"time"
"k8s.io/api/core/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/clock"
clientset
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/kubernetes/pkg/kubelet/util/manager"
)
func
checkObject
(
t
*
testing
.
T
,
store
manager
.
Store
,
ns
,
name
string
,
shouldExist
bool
)
{
_
,
err
:=
store
.
Get
(
ns
,
name
)
if
shouldExist
&&
err
!=
nil
{
t
.
Errorf
(
"unexpected actions: %#v"
,
err
)
}
if
!
shouldExist
&&
(
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
fmt
.
Sprintf
(
"object %q/%q not registered"
,
ns
,
name
)))
{
t
.
Errorf
(
"unexpected actions: %#v"
,
err
)
}
}
func
noObjectTTL
()
(
time
.
Duration
,
bool
)
{
return
time
.
Duration
(
0
),
false
}
func
getSecret
(
fakeClient
clientset
.
Interface
)
manager
.
GetObjectFunc
{
return
func
(
namespace
,
name
string
,
opts
metav1
.
GetOptions
)
(
runtime
.
Object
,
error
)
{
return
fakeClient
.
CoreV1
()
.
Secrets
(
namespace
)
.
Get
(
name
,
opts
)
}
}
type
envSecrets
struct
{
envVarNames
[]
string
envFromNames
[]
string
}
type
secretsToAttach
struct
{
imagePullSecretNames
[]
string
containerEnvSecrets
[]
envSecrets
}
func
podWithSecrets
(
ns
,
podName
string
,
toAttach
secretsToAttach
)
*
v1
.
Pod
{
pod
:=
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Namespace
:
ns
,
Name
:
podName
,
},
Spec
:
v1
.
PodSpec
{},
}
for
_
,
name
:=
range
toAttach
.
imagePullSecretNames
{
pod
.
Spec
.
ImagePullSecrets
=
append
(
pod
.
Spec
.
ImagePullSecrets
,
v1
.
LocalObjectReference
{
Name
:
name
})
}
for
i
,
secrets
:=
range
toAttach
.
containerEnvSecrets
{
container
:=
v1
.
Container
{
Name
:
fmt
.
Sprintf
(
"container-%d"
,
i
),
}
for
_
,
name
:=
range
secrets
.
envFromNames
{
envFrom
:=
v1
.
EnvFromSource
{
SecretRef
:
&
v1
.
SecretEnvSource
{
LocalObjectReference
:
v1
.
LocalObjectReference
{
Name
:
name
,
},
},
}
container
.
EnvFrom
=
append
(
container
.
EnvFrom
,
envFrom
)
}
for
_
,
name
:=
range
secrets
.
envVarNames
{
envSource
:=
&
v1
.
EnvVarSource
{
SecretKeyRef
:
&
v1
.
SecretKeySelector
{
LocalObjectReference
:
v1
.
LocalObjectReference
{
Name
:
name
,
},
},
}
container
.
Env
=
append
(
container
.
Env
,
v1
.
EnvVar
{
ValueFrom
:
envSource
})
}
pod
.
Spec
.
Containers
=
append
(
pod
.
Spec
.
Containers
,
container
)
}
return
pod
}
func
TestCacheBasedSecretManager
(
t
*
testing
.
T
)
{
fakeClient
:=
&
fake
.
Clientset
{}
store
:=
manager
.
NewObjectStore
(
getSecret
(
fakeClient
),
clock
.
RealClock
{},
noObjectTTL
,
0
)
manager
:=
&
cachingSecretManager
{
manager
:
manager
.
NewCacheBasedManager
(
store
,
getSecretNames
),
}
// Create a pod with some secrets.
s1
:=
secretsToAttach
{
imagePullSecretNames
:
[]
string
{
"s1"
},
containerEnvSecrets
:
[]
envSecrets
{
{
envVarNames
:
[]
string
{
"s1"
}},
{
envVarNames
:
[]
string
{
"s2"
}},
{
envFromNames
:
[]
string
{
"s20"
}},
},
}
manager
.
RegisterPod
(
podWithSecrets
(
"ns1"
,
"name1"
,
s1
))
// Update the pod with a different secrets.
s2
:=
secretsToAttach
{
imagePullSecretNames
:
[]
string
{
"s1"
},
containerEnvSecrets
:
[]
envSecrets
{
{
envVarNames
:
[]
string
{
"s3"
}},
{
envVarNames
:
[]
string
{
"s4"
}},
{
envFromNames
:
[]
string
{
"s40"
}},
},
}
manager
.
RegisterPod
(
podWithSecrets
(
"ns1"
,
"name1"
,
s2
))
// Create another pod, but with same secrets in different namespace.
manager
.
RegisterPod
(
podWithSecrets
(
"ns2"
,
"name2"
,
s2
))
// Create and delete a pod with some other secrets.
s3
:=
secretsToAttach
{
imagePullSecretNames
:
[]
string
{
"s5"
},
containerEnvSecrets
:
[]
envSecrets
{
{
envVarNames
:
[]
string
{
"s6"
}},
{
envFromNames
:
[]
string
{
"s60"
}},
},
}
manager
.
RegisterPod
(
podWithSecrets
(
"ns3"
,
"name"
,
s3
))
manager
.
UnregisterPod
(
podWithSecrets
(
"ns3"
,
"name"
,
s3
))
// We should have only: s1, s3 and s4 secrets in namespaces: ns1 and ns2.
for
_
,
ns
:=
range
[]
string
{
"ns1"
,
"ns2"
,
"ns3"
}
{
for
_
,
secret
:=
range
[]
string
{
"s1"
,
"s2"
,
"s3"
,
"s4"
,
"s5"
,
"s6"
,
"s20"
,
"s40"
,
"s50"
}
{
shouldExist
:=
(
secret
==
"s1"
||
secret
==
"s3"
||
secret
==
"s4"
||
secret
==
"s40"
)
&&
(
ns
==
"ns1"
||
ns
==
"ns2"
)
checkObject
(
t
,
store
,
ns
,
secret
,
shouldExist
)
}
}
}
pkg/kubelet/util/BUILD
View file @
de37da85
...
...
@@ -92,6 +92,7 @@ filegroup(
"//pkg/kubelet/util/cache:all-srcs",
"//pkg/kubelet/util/format:all-srcs",
"//pkg/kubelet/util/ioutils:all-srcs",
"//pkg/kubelet/util/manager:all-srcs",
"//pkg/kubelet/util/queue:all-srcs",
"//pkg/kubelet/util/sliceutils:all-srcs",
"//pkg/kubelet/util/store:all-srcs",
...
...
pkg/kubelet/util/manager/BUILD
0 → 100644
View file @
de37da85
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"cache_based_manager.go",
"manager.go",
],
importpath = "k8s.io/kubernetes/pkg/kubelet/util/manager",
visibility = ["//visibility:public"],
deps = [
"//pkg/kubelet/util:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["cache_based_manager_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/api/v1/pod:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
pkg/kubelet/
secret/caching_secret
_manager.go
→
pkg/kubelet/
util/manager/cache_based
_manager.go
View file @
de37da85
/*
Copyright 201
6
The Kubernetes Authors.
Copyright 201
8
The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
...
...
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package
secret
package
manager
import
(
"fmt"
...
...
@@ -24,88 +24,95 @@ import (
"k8s.io/api/core/v1"
storageetcd
"k8s.io/apiserver/pkg/storage/etcd"
clientset
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/kubelet/util"
apierrors
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/sets"
)
const
(
defaultTTL
=
time
.
Minute
)
// GetObjectTTLFunc defines a function to get value of TTL.
type
GetObjectTTLFunc
func
()
(
time
.
Duration
,
bool
)
// secretStoreItems is a single item stored in secretStore.
type
secretStoreItem
struct
{
// GetObjectFunc defines a function to get object with a given namespace and name.
type
GetObjectFunc
func
(
string
,
string
,
metav1
.
GetOptions
)
(
runtime
.
Object
,
error
)
type
objectKey
struct
{
namespace
string
name
string
}
// objectStoreItems is a single item stored in objectStore.
type
objectStoreItem
struct
{
refCount
int
secret
*
secre
tData
data
*
objec
tData
}
type
secre
tData
struct
{
type
objec
tData
struct
{
sync
.
Mutex
secret
*
v1
.
Secre
t
object
runtime
.
Objec
t
err
error
lastUpdateTime
time
.
Time
}
//
secretStore is a local cache of secre
ts.
type
secre
tStore
struct
{
kubeClient
clientset
.
Interface
clock
clock
.
Clock
//
objectStore is a local cache of objec
ts.
type
objec
tStore
struct
{
getObject
GetObjectFunc
clock
clock
.
Clock
lock
sync
.
Mutex
items
map
[
objectKey
]
*
secre
tStoreItem
items
map
[
objectKey
]
*
objec
tStoreItem
defaultTTL
time
.
Duration
getTTL
GetObjectTTLFunc
}
func
newSecretStore
(
kubeClient
clientset
.
Interface
,
clock
clock
.
Clock
,
getTTL
GetObjectTTLFunc
,
ttl
time
.
Duration
)
*
secretStore
{
return
&
secretStore
{
kubeClient
:
kubeClient
,
// NewObjectStore returns a new ttl-based instance of Store interface.
func
NewObjectStore
(
getObject
GetObjectFunc
,
clock
clock
.
Clock
,
getTTL
GetObjectTTLFunc
,
ttl
time
.
Duration
)
Store
{
return
&
objectStore
{
getObject
:
getObject
,
clock
:
clock
,
items
:
make
(
map
[
objectKey
]
*
secre
tStoreItem
),
items
:
make
(
map
[
objectKey
]
*
objec
tStoreItem
),
defaultTTL
:
ttl
,
getTTL
:
getTTL
,
}
}
func
is
SecretOlder
(
newSecret
,
oldSecret
*
v1
.
Secre
t
)
bool
{
if
new
Secret
==
nil
||
oldSecre
t
==
nil
{
func
is
ObjectOlder
(
newObject
,
oldObject
runtime
.
Objec
t
)
bool
{
if
new
Object
==
nil
||
oldObjec
t
==
nil
{
return
false
}
newVersion
,
_
:=
storageetcd
.
Versioner
.
ObjectResourceVersion
(
new
Secre
t
)
oldVersion
,
_
:=
storageetcd
.
Versioner
.
ObjectResourceVersion
(
old
Secre
t
)
newVersion
,
_
:=
storageetcd
.
Versioner
.
ObjectResourceVersion
(
new
Objec
t
)
oldVersion
,
_
:=
storageetcd
.
Versioner
.
ObjectResourceVersion
(
old
Objec
t
)
return
newVersion
<
oldVersion
}
func
(
s
*
secre
tStore
)
AddReference
(
namespace
,
name
string
)
{
func
(
s
*
objec
tStore
)
AddReference
(
namespace
,
name
string
)
{
key
:=
objectKey
{
namespace
:
namespace
,
name
:
name
}
// AddReference is called from RegisterPod, thus it needs to be efficient.
// Thus Add() is only increasing refCount and generation of a given
secre
t.
// Thus Add() is only increasing refCount and generation of a given
objec
t.
// Then Get() is responsible for fetching if needed.
s
.
lock
.
Lock
()
defer
s
.
lock
.
Unlock
()
item
,
exists
:=
s
.
items
[
key
]
if
!
exists
{
item
=
&
secre
tStoreItem
{
item
=
&
objec
tStoreItem
{
refCount
:
0
,
secret
:
&
secre
tData
{},
data
:
&
objec
tData
{},
}
s
.
items
[
key
]
=
item
}
item
.
refCount
++
// This will trigger fetch on the next Get() operation.
item
.
secret
=
nil
item
.
data
=
nil
}
func
(
s
*
secre
tStore
)
DeleteReference
(
namespace
,
name
string
)
{
func
(
s
*
objec
tStore
)
DeleteReference
(
namespace
,
name
string
)
{
key
:=
objectKey
{
namespace
:
namespace
,
name
:
name
}
s
.
lock
.
Lock
()
...
...
@@ -118,6 +125,8 @@ func (s *secretStore) DeleteReference(namespace, name string) {
}
}
// GetObjectTTLFromNodeFunc returns a function that returns TTL value
// from a given Node object.
func
GetObjectTTLFromNodeFunc
(
getNode
func
()
(
*
v1
.
Node
,
error
))
GetObjectTTLFunc
{
return
func
()
(
time
.
Duration
,
bool
)
{
node
,
err
:=
getNode
()
...
...
@@ -135,72 +144,129 @@ func GetObjectTTLFromNodeFunc(getNode func() (*v1.Node, error)) GetObjectTTLFunc
}
}
func
(
s
*
secretStore
)
isSecretFresh
(
data
*
secre
tData
)
bool
{
secre
tTTL
:=
s
.
defaultTTL
func
(
s
*
objectStore
)
isObjectFresh
(
data
*
objec
tData
)
bool
{
objec
tTTL
:=
s
.
defaultTTL
if
ttl
,
ok
:=
s
.
getTTL
();
ok
{
secre
tTTL
=
ttl
objec
tTTL
=
ttl
}
return
s
.
clock
.
Now
()
.
Before
(
data
.
lastUpdateTime
.
Add
(
secre
tTTL
))
return
s
.
clock
.
Now
()
.
Before
(
data
.
lastUpdateTime
.
Add
(
objec
tTTL
))
}
func
(
s
*
secretStore
)
Get
(
namespace
,
name
string
)
(
*
v1
.
Secre
t
,
error
)
{
func
(
s
*
objectStore
)
Get
(
namespace
,
name
string
)
(
runtime
.
Objec
t
,
error
)
{
key
:=
objectKey
{
namespace
:
namespace
,
name
:
name
}
data
:=
func
()
*
secre
tData
{
data
:=
func
()
*
objec
tData
{
s
.
lock
.
Lock
()
defer
s
.
lock
.
Unlock
()
item
,
exists
:=
s
.
items
[
key
]
if
!
exists
{
return
nil
}
if
item
.
secret
==
nil
{
item
.
secret
=
&
secre
tData
{}
if
item
.
data
==
nil
{
item
.
data
=
&
objec
tData
{}
}
return
item
.
secret
return
item
.
data
}()
if
data
==
nil
{
return
nil
,
fmt
.
Errorf
(
"
secre
t %q/%q not registered"
,
namespace
,
name
)
return
nil
,
fmt
.
Errorf
(
"
objec
t %q/%q not registered"
,
namespace
,
name
)
}
// After updating data in
secretStore, lock the data, fetch secre
t if
// After updating data in
objectStore, lock the data, fetch objec
t if
// needed and return data.
data
.
Lock
()
defer
data
.
Unlock
()
if
data
.
err
!=
nil
||
!
s
.
is
Secre
tFresh
(
data
)
{
if
data
.
err
!=
nil
||
!
s
.
is
Objec
tFresh
(
data
)
{
opts
:=
metav1
.
GetOptions
{}
if
data
.
secre
t
!=
nil
&&
data
.
err
==
nil
{
// This is just a periodic refresh of a
secre
t we successfully fetched previously.
if
data
.
objec
t
!=
nil
&&
data
.
err
==
nil
{
// This is just a periodic refresh of a
n objec
t we successfully fetched previously.
// In this case, server data from apiserver cache to reduce the load on both
// etcd and apiserver (the cache is eventually consistent).
util
.
FromApiserverCache
(
&
opts
)
}
secret
,
err
:=
s
.
kubeClient
.
CoreV1
()
.
Secrets
(
namespace
)
.
Get
(
name
,
opts
)
if
err
!=
nil
&&
!
apierrors
.
IsNotFound
(
err
)
&&
data
.
secret
==
nil
&&
data
.
err
==
nil
{
// Couldn't fetch the latest secret, but there is no cached data to return.
object
,
err
:=
s
.
getObject
(
namespace
,
name
,
opts
)
if
err
!=
nil
&&
!
apierrors
.
IsNotFound
(
err
)
&&
data
.
object
==
nil
&&
data
.
err
==
nil
{
// Couldn't fetch the latest object, but there is no cached data to return.
// Return the fetch result instead.
return
secre
t
,
err
return
objec
t
,
err
}
if
(
err
==
nil
&&
!
is
SecretOlder
(
secret
,
data
.
secre
t
))
||
apierrors
.
IsNotFound
(
err
)
{
// If the fetch succeeded with a newer version of the
secre
t, or if the
//
secre
t could not be found in the apiserver, update the cached data to
if
(
err
==
nil
&&
!
is
ObjectOlder
(
object
,
data
.
objec
t
))
||
apierrors
.
IsNotFound
(
err
)
{
// If the fetch succeeded with a newer version of the
objec
t, or if the
//
objec
t could not be found in the apiserver, update the cached data to
// reflect the current status.
data
.
secret
=
secre
t
data
.
object
=
objec
t
data
.
err
=
err
data
.
lastUpdateTime
=
s
.
clock
.
Now
()
}
}
return
data
.
secret
,
data
.
err
return
data
.
object
,
data
.
err
}
// cacheBasedManager keeps a store with objects necessary
// for registered pods. Different implementations of the store
// may result in different semantics for freshness of objects
// (e.g. ttl-based implementation vs watch-based implementation).
type
cacheBasedManager
struct
{
objectStore
Store
getReferencedObjects
func
(
*
v1
.
Pod
)
sets
.
String
lock
sync
.
Mutex
registeredPods
map
[
objectKey
]
*
v1
.
Pod
}
func
(
c
*
cacheBasedManager
)
GetObject
(
namespace
,
name
string
)
(
runtime
.
Object
,
error
)
{
return
c
.
objectStore
.
Get
(
namespace
,
name
)
}
// NewCachingSecretManager creates a manager that keeps a cache of all secrets
func
(
c
*
cacheBasedManager
)
RegisterPod
(
pod
*
v1
.
Pod
)
{
names
:=
c
.
getReferencedObjects
(
pod
)
c
.
lock
.
Lock
()
defer
c
.
lock
.
Unlock
()
for
name
:=
range
names
{
c
.
objectStore
.
AddReference
(
pod
.
Namespace
,
name
)
}
var
prev
*
v1
.
Pod
key
:=
objectKey
{
namespace
:
pod
.
Namespace
,
name
:
pod
.
Name
}
prev
=
c
.
registeredPods
[
key
]
c
.
registeredPods
[
key
]
=
pod
if
prev
!=
nil
{
for
name
:=
range
c
.
getReferencedObjects
(
prev
)
{
// On an update, the .Add() call above will have re-incremented the
// ref count of any existing object, so any objects that are in both
// names and prev need to have their ref counts decremented. Any that
// are only in prev need to be completely removed. This unconditional
// call takes care of both cases.
c
.
objectStore
.
DeleteReference
(
prev
.
Namespace
,
name
)
}
}
}
func
(
c
*
cacheBasedManager
)
UnregisterPod
(
pod
*
v1
.
Pod
)
{
var
prev
*
v1
.
Pod
key
:=
objectKey
{
namespace
:
pod
.
Namespace
,
name
:
pod
.
Name
}
c
.
lock
.
Lock
()
defer
c
.
lock
.
Unlock
()
prev
=
c
.
registeredPods
[
key
]
delete
(
c
.
registeredPods
,
key
)
if
prev
!=
nil
{
for
name
:=
range
c
.
getReferencedObjects
(
prev
)
{
c
.
objectStore
.
DeleteReference
(
prev
.
Namespace
,
name
)
}
}
}
// NewCacheBasedManager creates a manager that keeps a cache of all objects
// necessary for registered pods.
// It implements the following logic:
// - whenever a pod is created or updated, the cached versions of all
its secre
ts
// are invalidated
// - every Get
Secre
t() call tries to fetch the value from local cache; if it is
// - whenever a pod is created or updated, the cached versions of all
objec
ts
//
is is referencing
are invalidated
// - every Get
Objec
t() call tries to fetch the value from local cache; if it is
// not there, invalidated or too old, we fetch it from apiserver and refresh the
// value in cache; otherwise it is just fetched from cache
func
NewCachingSecretManager
(
kubeClient
clientset
.
Interface
,
getTTL
GetObjectTTLFunc
)
Manager
{
secretStore
:=
newSecretStore
(
kubeClient
,
clock
.
RealClock
{},
getTTL
,
defaultTTL
)
return
newCacheBasedSecretManager
(
secretStore
)
func
NewCacheBasedManager
(
objectStore
Store
,
getReferencedObjects
func
(
*
v1
.
Pod
)
sets
.
String
)
Manager
{
return
&
cacheBasedManager
{
objectStore
:
objectStore
,
getReferencedObjects
:
getReferencedObjects
,
registeredPods
:
make
(
map
[
objectKey
]
*
v1
.
Pod
),
}
}
pkg/kubelet/
secret/caching_secret
_manager_test.go
→
pkg/kubelet/
util/manager/cache_based
_manager_test.go
View file @
de37da85
/*
Copyright 201
6
The Kubernetes Authors.
Copyright 201
8
The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
...
...
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package
secret
package
manager
import
(
"fmt"
...
...
@@ -25,23 +25,27 @@ import (
"time"
"k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/fake"
apierrors
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/sets"
clientset
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
core
"k8s.io/client-go/testing"
podutil
"k8s.io/kubernetes/pkg/api/v1/pod"
"github.com/stretchr/testify/assert"
)
func
check
Secret
(
t
*
testing
.
T
,
store
*
secre
tStore
,
ns
,
name
string
,
shouldExist
bool
)
{
func
check
Object
(
t
*
testing
.
T
,
store
*
objec
tStore
,
ns
,
name
string
,
shouldExist
bool
)
{
_
,
err
:=
store
.
Get
(
ns
,
name
)
if
shouldExist
&&
err
!=
nil
{
t
.
Errorf
(
"unexpected actions: %#v"
,
err
)
}
if
!
shouldExist
&&
(
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
fmt
.
Sprintf
(
"
secre
t %q/%q not registered"
,
ns
,
name
)))
{
if
!
shouldExist
&&
(
err
==
nil
||
!
strings
.
Contains
(
err
.
Error
(),
fmt
.
Sprintf
(
"
objec
t %q/%q not registered"
,
ns
,
name
)))
{
t
.
Errorf
(
"unexpected actions: %#v"
,
err
)
}
}
...
...
@@ -50,6 +54,35 @@ func noObjectTTL() (time.Duration, bool) {
return
time
.
Duration
(
0
),
false
}
func
getSecret
(
fakeClient
clientset
.
Interface
)
GetObjectFunc
{
return
func
(
namespace
,
name
string
,
opts
metav1
.
GetOptions
)
(
runtime
.
Object
,
error
)
{
return
fakeClient
.
CoreV1
()
.
Secrets
(
namespace
)
.
Get
(
name
,
opts
)
}
}
func
newSecretStore
(
fakeClient
clientset
.
Interface
,
clock
clock
.
Clock
,
getTTL
GetObjectTTLFunc
,
ttl
time
.
Duration
)
*
objectStore
{
return
&
objectStore
{
getObject
:
getSecret
(
fakeClient
),
clock
:
clock
,
items
:
make
(
map
[
objectKey
]
*
objectStoreItem
),
defaultTTL
:
ttl
,
getTTL
:
getTTL
,
}
}
func
getSecretNames
(
pod
*
v1
.
Pod
)
sets
.
String
{
result
:=
sets
.
NewString
()
podutil
.
VisitPodSecretNames
(
pod
,
func
(
name
string
)
bool
{
result
.
Insert
(
name
)
return
true
})
return
result
}
func
newCacheBasedSecretManager
(
store
Store
)
Manager
{
return
NewCacheBasedManager
(
store
,
getSecretNames
)
}
func
TestSecretStore
(
t
*
testing
.
T
)
{
fakeClient
:=
&
fake
.
Clientset
{}
store
:=
newSecretStore
(
fakeClient
,
clock
.
RealClock
{},
noObjectTTL
,
0
)
...
...
@@ -78,10 +111,10 @@ func TestSecretStore(t *testing.T) {
assert
.
True
(
t
,
a
.
Matches
(
"get"
,
"secrets"
),
"unexpected actions: %#v"
,
a
)
}
check
Secre
t
(
t
,
store
,
"ns1"
,
"name1"
,
true
)
check
Secre
t
(
t
,
store
,
"ns2"
,
"name2"
,
false
)
check
Secre
t
(
t
,
store
,
"ns3"
,
"name3"
,
true
)
check
Secre
t
(
t
,
store
,
"ns4"
,
"name4"
,
false
)
check
Objec
t
(
t
,
store
,
"ns1"
,
"name1"
,
true
)
check
Objec
t
(
t
,
store
,
"ns2"
,
"name2"
,
false
)
check
Objec
t
(
t
,
store
,
"ns3"
,
"name3"
,
true
)
check
Objec
t
(
t
,
store
,
"ns4"
,
"name4"
,
false
)
}
func
TestSecretStoreDeletingSecret
(
t
*
testing
.
T
)
{
...
...
@@ -481,10 +514,10 @@ func TestCacheRefcounts(t *testing.T) {
assert
.
Equal
(
t
,
1
,
refs
(
"ns1"
,
"s70"
))
}
func
TestCach
ing
SecretManager
(
t
*
testing
.
T
)
{
func
TestCach
eBased
SecretManager
(
t
*
testing
.
T
)
{
fakeClient
:=
&
fake
.
Clientset
{}
s
ecretS
tore
:=
newSecretStore
(
fakeClient
,
clock
.
RealClock
{},
noObjectTTL
,
0
)
manager
:=
newCacheBasedSecretManager
(
s
ecretS
tore
)
store
:=
newSecretStore
(
fakeClient
,
clock
.
RealClock
{},
noObjectTTL
,
0
)
manager
:=
newCacheBasedSecretManager
(
store
)
// Create a pod with some secrets.
s1
:=
secretsToAttach
{
...
...
@@ -524,7 +557,7 @@ func TestCachingSecretManager(t *testing.T) {
for
_
,
secret
:=
range
[]
string
{
"s1"
,
"s2"
,
"s3"
,
"s4"
,
"s5"
,
"s6"
,
"s20"
,
"s40"
,
"s50"
}
{
shouldExist
:=
(
secret
==
"s1"
||
secret
==
"s3"
||
secret
==
"s4"
||
secret
==
"s40"
)
&&
(
ns
==
"ns1"
||
ns
==
"ns2"
)
check
Secret
(
t
,
secretS
tore
,
ns
,
secret
,
shouldExist
)
check
Object
(
t
,
s
tore
,
ns
,
secret
,
shouldExist
)
}
}
}
pkg/kubelet/util/manager/manager.go
0 → 100644
View file @
de37da85
/*
Copyright 2018 The Kubernetes Authors.
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.
*/
package
manager
import
(
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// Manager is the interface for registering and unregistering
// objects referenced by pods in the underlying cache and
// extracting those from that cache if needed.
type
Manager
interface
{
// Get object by its namespace and name.
GetObject
(
namespace
,
name
string
)
(
runtime
.
Object
,
error
)
// WARNING: Register/UnregisterPod functions should be efficient,
// i.e. should not block on network operations.
// RegisterPod registers all objects referenced from a given pod.
RegisterPod
(
pod
*
v1
.
Pod
)
// UnregisterPod unregisters objects referenced from a given pod that are not
// used by any other registered pod.
UnregisterPod
(
pod
*
v1
.
Pod
)
}
// Store is the interface for a object cache that
// can be used by cacheBasedManager.
type
Store
interface
{
// AddReference adds a reference to the object to the store.
// Note that multiple additions to the store has to be allowed
// in the implementations and effectively treated as refcounted.
AddReference
(
namespace
,
name
string
)
// DeleteReference deletes reference to the object from the store.
// Note that object should be deleted only when there was a
// corresponding Delete call for each of Add calls (effectively
// when refcount was reduced to zero).
DeleteReference
(
namespace
,
name
string
)
// Get an object from a store.
Get
(
namespace
,
name
string
)
(
runtime
.
Object
,
error
)
}
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