Unverified Commit a8cbb225 authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #74747 from liggitt/quota-deadlock

quota controller fixes
parents 6c22cff3 bef996d0
...@@ -873,7 +873,6 @@ func TestGarbageCollectorSync(t *testing.T) { ...@@ -873,7 +873,6 @@ func TestGarbageCollectorSync(t *testing.T) {
go gc.Sync(fakeDiscoveryClient, 200*time.Millisecond, stopCh) go gc.Sync(fakeDiscoveryClient, 200*time.Millisecond, stopCh)
// Wait until the sync discovers the initial resources // Wait until the sync discovers the initial resources
fmt.Printf("Test output")
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
err = expectSyncNotBlocked(fakeDiscoveryClient, &gc.workerLock) err = expectSyncNotBlocked(fakeDiscoveryClient, &gc.workerLock)
......
...@@ -29,6 +29,7 @@ go_library( ...@@ -29,6 +29,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/client-go/discovery:go_default_library", "//staging/src/k8s.io/client-go/discovery:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library",
...@@ -53,12 +54,14 @@ go_test( ...@@ -53,12 +54,14 @@ go_test(
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
], ],
......
...@@ -30,7 +30,9 @@ import ( ...@@ -30,7 +30,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
...@@ -320,12 +322,12 @@ func (rq *ResourceQuotaController) syncResourceQuotaFromKey(key string) (err err ...@@ -320,12 +322,12 @@ func (rq *ResourceQuotaController) syncResourceQuotaFromKey(key string) (err err
// syncResourceQuota runs a complete sync of resource quota status across all known kinds // syncResourceQuota runs a complete sync of resource quota status across all known kinds
func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQuota) (err error) { func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQuota) (err error) {
// quota is dirty if any part of spec hard limits differs from the status hard limits // quota is dirty if any part of spec hard limits differs from the status hard limits
dirty := !apiequality.Semantic.DeepEqual(resourceQuota.Spec.Hard, resourceQuota.Status.Hard) statusLimitsDirty := !apiequality.Semantic.DeepEqual(resourceQuota.Spec.Hard, resourceQuota.Status.Hard)
// dirty tracks if the usage status differs from the previous sync, // dirty tracks if the usage status differs from the previous sync,
// if so, we send a new usage with latest status // if so, we send a new usage with latest status
// if this is our first sync, it will be dirty by default, since we need track usage // if this is our first sync, it will be dirty by default, since we need track usage
dirty = dirty || resourceQuota.Status.Hard == nil || resourceQuota.Status.Used == nil dirty := statusLimitsDirty || resourceQuota.Status.Hard == nil || resourceQuota.Status.Used == nil
used := v1.ResourceList{} used := v1.ResourceList{}
if resourceQuota.Status.Used != nil { if resourceQuota.Status.Used != nil {
...@@ -333,9 +335,12 @@ func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQ ...@@ -333,9 +335,12 @@ func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQ
} }
hardLimits := quota.Add(v1.ResourceList{}, resourceQuota.Spec.Hard) hardLimits := quota.Add(v1.ResourceList{}, resourceQuota.Spec.Hard)
errors := []error{}
newUsage, err := quota.CalculateUsage(resourceQuota.Namespace, resourceQuota.Spec.Scopes, hardLimits, rq.registry, resourceQuota.Spec.ScopeSelector) newUsage, err := quota.CalculateUsage(resourceQuota.Namespace, resourceQuota.Spec.Scopes, hardLimits, rq.registry, resourceQuota.Spec.ScopeSelector)
if err != nil { if err != nil {
return err // if err is non-nil, remember it to return, but continue updating status with any resources in newUsage
errors = append(errors, err)
} }
for key, value := range newUsage { for key, value := range newUsage {
used[key] = value used[key] = value
...@@ -358,9 +363,11 @@ func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQ ...@@ -358,9 +363,11 @@ func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQ
// there was a change observed by this controller that requires we update quota // there was a change observed by this controller that requires we update quota
if dirty { if dirty {
_, err = rq.rqClient.ResourceQuotas(usage.Namespace).UpdateStatus(usage) _, err = rq.rqClient.ResourceQuotas(usage.Namespace).UpdateStatus(usage)
return err if err != nil {
errors = append(errors, err)
}
} }
return nil return utilerrors.NewAggregate(errors)
} }
// replenishQuota is a replenishment function invoked by a controller to notify that a quota should be recalculated // replenishQuota is a replenishment function invoked by a controller to notify that a quota should be recalculated
...@@ -423,26 +430,66 @@ func (rq *ResourceQuotaController) Sync(discoveryFunc NamespacedResourcesFunc, p ...@@ -423,26 +430,66 @@ func (rq *ResourceQuotaController) Sync(discoveryFunc NamespacedResourcesFunc, p
return return
} }
// Something has changed, so track the new state and perform a sync.
klog.V(2).Infof("syncing resource quota controller with updated resources from discovery: %v", newResources)
oldResources = newResources
// Ensure workers are paused to avoid processing events before informers // Ensure workers are paused to avoid processing events before informers
// have resynced. // have resynced.
rq.workerLock.Lock() rq.workerLock.Lock()
defer rq.workerLock.Unlock() defer rq.workerLock.Unlock()
// Something has changed, so track the new state and perform a sync.
if klog.V(2) {
klog.Infof("syncing resource quota controller with updated resources from discovery: %s", printDiff(oldResources, newResources))
}
// Perform the monitor resync and wait for controllers to report cache sync. // Perform the monitor resync and wait for controllers to report cache sync.
if err := rq.resyncMonitors(newResources); err != nil { if err := rq.resyncMonitors(newResources); err != nil {
utilruntime.HandleError(fmt.Errorf("failed to sync resource monitors: %v", err)) utilruntime.HandleError(fmt.Errorf("failed to sync resource monitors: %v", err))
return return
} }
if rq.quotaMonitor != nil && !controller.WaitForCacheSync("resource quota", stopCh, rq.quotaMonitor.IsSynced) { // wait for caches to fill for a while (our sync period).
// this protects us from deadlocks where available resources changed and one of our informer caches will never fill.
// informers keep attempting to sync in the background, so retrying doesn't interrupt them.
// the call to resyncMonitors on the reattempt will no-op for resources that still exist.
if rq.quotaMonitor != nil && !controller.WaitForCacheSync("resource quota", waitForStopOrTimeout(stopCh, period), rq.quotaMonitor.IsSynced) {
utilruntime.HandleError(fmt.Errorf("timed out waiting for quota monitor sync")) utilruntime.HandleError(fmt.Errorf("timed out waiting for quota monitor sync"))
return
} }
// success, remember newly synced resources
oldResources = newResources
klog.V(2).Infof("synced quota controller")
}, period, stopCh) }, period, stopCh)
} }
// printDiff returns a human-readable summary of what resources were added and removed
func printDiff(oldResources, newResources map[schema.GroupVersionResource]struct{}) string {
removed := sets.NewString()
for oldResource := range oldResources {
if _, ok := newResources[oldResource]; !ok {
removed.Insert(fmt.Sprintf("%+v", oldResource))
}
}
added := sets.NewString()
for newResource := range newResources {
if _, ok := oldResources[newResource]; !ok {
added.Insert(fmt.Sprintf("%+v", newResource))
}
}
return fmt.Sprintf("added: %v, removed: %v", added.List(), removed.List())
}
// waitForStopOrTimeout returns a stop channel that closes when the provided stop channel closes or when the specified timeout is reached
func waitForStopOrTimeout(stopCh <-chan struct{}, timeout time.Duration) <-chan struct{} {
stopChWithTimeout := make(chan struct{})
go func() {
defer close(stopChWithTimeout)
select {
case <-stopCh:
case <-time.After(timeout):
}
}()
return stopChWithTimeout
}
// resyncMonitors starts or stops quota monitors as needed to ensure that all // resyncMonitors starts or stops quota monitors as needed to ensure that all
// (and only) those resources present in the map are monitored. // (and only) those resources present in the map are monitored.
func (rq *ResourceQuotaController) resyncMonitors(resources map[schema.GroupVersionResource]struct{}) error { func (rq *ResourceQuotaController) resyncMonitors(resources map[schema.GroupVersionResource]struct{}) error {
......
...@@ -284,11 +284,13 @@ func (qm *QuotaMonitor) IsSynced() bool { ...@@ -284,11 +284,13 @@ func (qm *QuotaMonitor) IsSynced() bool {
defer qm.monitorLock.Unlock() defer qm.monitorLock.Unlock()
if len(qm.monitors) == 0 { if len(qm.monitors) == 0 {
klog.V(4).Info("quota monitor not synced: no monitors")
return false return false
} }
for _, monitor := range qm.monitors { for resource, monitor := range qm.monitors {
if !monitor.controller.HasSynced() { if !monitor.controller.HasSynced() {
klog.V(4).Infof("quota monitor not synced: %v", resource)
return false return false
} }
} }
......
...@@ -18,6 +18,7 @@ go_library( ...@@ -18,6 +18,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
......
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
load( load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library( go_library(
name = "go_default_library", name = "go_default_library",
...@@ -38,3 +35,14 @@ filegroup( ...@@ -38,3 +35,14 @@ filegroup(
srcs = [":package-srcs"], srcs = [":package-srcs"],
tags = ["automanaged"], tags = ["automanaged"],
) )
go_test(
name = "go_default_test",
srcs = ["evaluator_test.go"],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
],
)
...@@ -18,6 +18,7 @@ package generic ...@@ -18,6 +18,7 @@ package generic
import ( import (
"fmt" "fmt"
"sync/atomic"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
...@@ -33,17 +34,83 @@ import ( ...@@ -33,17 +34,83 @@ import (
// InformerForResourceFunc knows how to provision an informer // InformerForResourceFunc knows how to provision an informer
type InformerForResourceFunc func(schema.GroupVersionResource) (informers.GenericInformer, error) type InformerForResourceFunc func(schema.GroupVersionResource) (informers.GenericInformer, error)
// ListerFuncForResourceFunc knows how to provision a lister from an informer func // ListerFuncForResourceFunc knows how to provision a lister from an informer func.
// The lister returns errors until the informer has synced.
func ListerFuncForResourceFunc(f InformerForResourceFunc) quota.ListerForResourceFunc { func ListerFuncForResourceFunc(f InformerForResourceFunc) quota.ListerForResourceFunc {
return func(gvr schema.GroupVersionResource) (cache.GenericLister, error) { return func(gvr schema.GroupVersionResource) (cache.GenericLister, error) {
informer, err := f(gvr) informer, err := f(gvr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return informer.Lister(), nil return &protectedLister{
hasSynced: cachedHasSynced(informer.Informer().HasSynced),
notReadyErr: fmt.Errorf("%v not yet synced", gvr),
delegate: informer.Lister(),
}, nil
} }
} }
// cachedHasSynced returns a function that calls hasSynced() until it returns true once, then returns true
func cachedHasSynced(hasSynced func() bool) func() bool {
cache := &atomic.Value{}
cache.Store(false)
return func() bool {
if cache.Load().(bool) {
// short-circuit if already synced
return true
}
if hasSynced() {
// remember we synced
cache.Store(true)
return true
}
return false
}
}
// protectedLister returns notReadyError if hasSynced returns false, otherwise delegates to delegate
type protectedLister struct {
hasSynced func() bool
notReadyErr error
delegate cache.GenericLister
}
func (p *protectedLister) List(selector labels.Selector) (ret []runtime.Object, err error) {
if !p.hasSynced() {
return nil, p.notReadyErr
}
return p.delegate.List(selector)
}
func (p *protectedLister) Get(name string) (runtime.Object, error) {
if !p.hasSynced() {
return nil, p.notReadyErr
}
return p.delegate.Get(name)
}
func (p *protectedLister) ByNamespace(namespace string) cache.GenericNamespaceLister {
return &protectedNamespaceLister{p.hasSynced, p.notReadyErr, p.delegate.ByNamespace(namespace)}
}
// protectedNamespaceLister returns notReadyError if hasSynced returns false, otherwise delegates to delegate
type protectedNamespaceLister struct {
hasSynced func() bool
notReadyErr error
delegate cache.GenericNamespaceLister
}
func (p *protectedNamespaceLister) List(selector labels.Selector) (ret []runtime.Object, err error) {
if !p.hasSynced() {
return nil, p.notReadyErr
}
return p.delegate.List(selector)
}
func (p *protectedNamespaceLister) Get(name string) (runtime.Object, error) {
if !p.hasSynced() {
return nil, p.notReadyErr
}
return p.delegate.Get(name)
}
// ListResourceUsingListerFunc returns a listing function based on the shared informer factory for the specified resource. // ListResourceUsingListerFunc returns a listing function based on the shared informer factory for the specified resource.
func ListResourceUsingListerFunc(l quota.ListerForResourceFunc, resource schema.GroupVersionResource) ListFuncByNamespace { func ListResourceUsingListerFunc(l quota.ListerForResourceFunc, resource schema.GroupVersionResource) ListFuncByNamespace {
return func(namespace string) ([]runtime.Object, error) { return func(namespace string) ([]runtime.Object, error) {
......
/*
Copyright 2019 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 generic
import (
"errors"
"testing"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
)
func TestCachedHasSynced(t *testing.T) {
called := 0
result := false
cachedFunc := cachedHasSynced(func() bool {
called++
return result
})
if cachedFunc() {
t.Fatal("expected false")
}
if called != 1 {
t.Fatalf("expected called=1, got %d", called)
}
if cachedFunc() {
t.Fatal("expected false")
}
if called != 2 {
t.Fatalf("expected called=2, got %d", called)
}
result = true
if !cachedFunc() {
t.Fatal("expected true")
}
if called != 3 {
t.Fatalf("expected called=3, got %d", called)
}
if !cachedFunc() {
t.Fatal("expected true")
}
if called != 3 {
// no more calls once we return true
t.Fatalf("expected called=3, got %d", called)
}
}
func TestProtectedLister(t *testing.T) {
hasSynced := false
notReadyErr := errors.New("not ready")
fake := &fakeLister{}
l := &protectedLister{
hasSynced: func() bool { return hasSynced },
notReadyErr: notReadyErr,
delegate: fake,
}
if _, err := l.List(nil); err != notReadyErr {
t.Fatalf("expected %v, got %v", notReadyErr, err)
}
if _, err := l.Get(""); err != notReadyErr {
t.Fatalf("expected %v, got %v", notReadyErr, err)
}
if fake.called != 0 {
t.Fatalf("expected called=0, got %d", fake.called)
}
fake.called = 0
hasSynced = true
if _, err := l.List(nil); err != errFakeLister {
t.Fatalf("expected %v, got %v", errFakeLister, err)
}
if _, err := l.Get(""); err != errFakeLister {
t.Fatalf("expected %v, got %v", errFakeLister, err)
}
if fake.called != 2 {
t.Fatalf("expected called=2, got %d", fake.called)
}
fake.called = 0
hasSynced = false
if _, err := l.List(nil); err != notReadyErr {
t.Fatalf("expected %v, got %v", notReadyErr, err)
}
if _, err := l.Get(""); err != notReadyErr {
t.Fatalf("expected %v, got %v", notReadyErr, err)
}
if fake.called != 0 {
t.Fatalf("expected called=2, got %d", fake.called)
}
}
var errFakeLister = errors.New("errFakeLister")
type fakeLister struct {
called int
}
func (f *fakeLister) List(selector labels.Selector) (ret []runtime.Object, err error) {
f.called++
return nil, errFakeLister
}
func (f *fakeLister) Get(name string) (runtime.Object, error) {
f.called++
return nil, errFakeLister
}
func (f *fakeLister) ByNamespace(namespace string) cache.GenericNamespaceLister {
panic("not implemented")
}
...@@ -17,10 +17,12 @@ limitations under the License. ...@@ -17,10 +17,12 @@ limitations under the License.
package quota package quota
import ( import (
"sort"
"strings" "strings"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
) )
...@@ -186,7 +188,12 @@ func ResourceNames(resources corev1.ResourceList) []corev1.ResourceName { ...@@ -186,7 +188,12 @@ func ResourceNames(resources corev1.ResourceList) []corev1.ResourceName {
// Contains returns true if the specified item is in the list of items // Contains returns true if the specified item is in the list of items
func Contains(items []corev1.ResourceName, item corev1.ResourceName) bool { func Contains(items []corev1.ResourceName, item corev1.ResourceName) bool {
return ToSet(items).Has(string(item)) for _, i := range items {
if i == item {
return true
}
}
return false
} }
// ContainsPrefix returns true if the specified item has a prefix that contained in given prefix Set // ContainsPrefix returns true if the specified item has a prefix that contained in given prefix Set
...@@ -199,15 +206,32 @@ func ContainsPrefix(prefixSet []string, item corev1.ResourceName) bool { ...@@ -199,15 +206,32 @@ func ContainsPrefix(prefixSet []string, item corev1.ResourceName) bool {
return false return false
} }
// Intersection returns the intersection of both list of resources // Intersection returns the intersection of both list of resources, deduped and sorted
func Intersection(a []corev1.ResourceName, b []corev1.ResourceName) []corev1.ResourceName { func Intersection(a []corev1.ResourceName, b []corev1.ResourceName) []corev1.ResourceName {
setA := ToSet(a) result := make([]corev1.ResourceName, 0, len(a))
setB := ToSet(b) for _, item := range a {
setC := setA.Intersection(setB) if Contains(result, item) {
result := []corev1.ResourceName{} continue
for _, resourceName := range setC.List() { }
result = append(result, corev1.ResourceName(resourceName)) if !Contains(b, item) {
continue
}
result = append(result, item)
} }
sort.Slice(result, func(i, j int) bool { return result[i] < result[j] })
return result
}
// Difference returns the list of resources resulting from a-b, deduped and sorted
func Difference(a []corev1.ResourceName, b []corev1.ResourceName) []corev1.ResourceName {
result := make([]corev1.ResourceName, 0, len(a))
for _, item := range a {
if Contains(b, item) || Contains(result, item) {
continue
}
result = append(result, item)
}
sort.Slice(result, func(i, j int) bool { return result[i] < result[j] })
return result return result
} }
...@@ -243,7 +267,8 @@ func ToSet(resourceNames []corev1.ResourceName) sets.String { ...@@ -243,7 +267,8 @@ func ToSet(resourceNames []corev1.ResourceName) sets.String {
return result return result
} }
// CalculateUsage calculates and returns the requested ResourceList usage // CalculateUsage calculates and returns the requested ResourceList usage.
// If an error is returned, usage only contains the resources which encountered no calculation errors.
func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, hardLimits corev1.ResourceList, registry Registry, scopeSelector *corev1.ScopeSelector) (corev1.ResourceList, error) { func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, hardLimits corev1.ResourceList, registry Registry, scopeSelector *corev1.ScopeSelector) (corev1.ResourceList, error) {
// find the intersection between the hard resources on the quota // find the intersection between the hard resources on the quota
// and the resources this controller can track to know what we can // and the resources this controller can track to know what we can
...@@ -257,6 +282,8 @@ func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, ha ...@@ -257,6 +282,8 @@ func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, ha
// NOTE: the intersection just removes duplicates since the evaluator match intersects with hard // NOTE: the intersection just removes duplicates since the evaluator match intersects with hard
matchedResources := Intersection(hardResources, potentialResources) matchedResources := Intersection(hardResources, potentialResources)
errors := []error{}
// sum the observed usage from each evaluator // sum the observed usage from each evaluator
newUsage := corev1.ResourceList{} newUsage := corev1.ResourceList{}
for _, evaluator := range evaluators { for _, evaluator := range evaluators {
...@@ -269,7 +296,11 @@ func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, ha ...@@ -269,7 +296,11 @@ func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, ha
usageStatsOptions := UsageStatsOptions{Namespace: namespaceName, Scopes: scopes, Resources: intersection, ScopeSelector: scopeSelector} usageStatsOptions := UsageStatsOptions{Namespace: namespaceName, Scopes: scopes, Resources: intersection, ScopeSelector: scopeSelector}
stats, err := evaluator.UsageStats(usageStatsOptions) stats, err := evaluator.UsageStats(usageStatsOptions)
if err != nil { if err != nil {
return nil, err // remember the error
errors = append(errors, err)
// exclude resources which encountered calculation errors
matchedResources = Difference(matchedResources, intersection)
continue
} }
newUsage = Add(newUsage, stats.Used) newUsage = Add(newUsage, stats.Used)
} }
...@@ -278,5 +309,5 @@ func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, ha ...@@ -278,5 +309,5 @@ func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, ha
// merge our observed usage with the quota usage status // merge our observed usage with the quota usage status
// if the new usage is different than the last usage, we will need to do an update // if the new usage is different than the last usage, we will need to do an update
newUsage = Mask(newUsage, matchedResources) newUsage = Mask(newUsage, matchedResources)
return newUsage, nil return newUsage, utilerrors.NewAggregate(errors)
} }
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package quota package quota
import ( import (
"reflect"
"testing" "testing"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
...@@ -319,3 +320,93 @@ func TestIsNegative(t *testing.T) { ...@@ -319,3 +320,93 @@ func TestIsNegative(t *testing.T) {
} }
} }
} }
func TestIntersection(t *testing.T) {
testCases := map[string]struct {
a []corev1.ResourceName
b []corev1.ResourceName
expected []corev1.ResourceName
}{
"empty": {
a: []corev1.ResourceName{},
b: []corev1.ResourceName{},
expected: []corev1.ResourceName{},
},
"equal": {
a: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
b: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
expected: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
},
"a has extra": {
a: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
b: []corev1.ResourceName{corev1.ResourceCPU},
expected: []corev1.ResourceName{corev1.ResourceCPU},
},
"b has extra": {
a: []corev1.ResourceName{corev1.ResourceCPU},
b: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
expected: []corev1.ResourceName{corev1.ResourceCPU},
},
"dedupes": {
a: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceCPU, corev1.ResourceMemory, corev1.ResourceMemory},
b: []corev1.ResourceName{corev1.ResourceCPU},
expected: []corev1.ResourceName{corev1.ResourceCPU},
},
"sorts": {
a: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceMemory, corev1.ResourceCPU, corev1.ResourceCPU},
b: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceMemory, corev1.ResourceCPU, corev1.ResourceCPU},
expected: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
},
}
for testName, testCase := range testCases {
actual := Intersection(testCase.a, testCase.b)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("%s expected: %#v, actual: %#v", testName, testCase.expected, actual)
}
}
}
func TestDifference(t *testing.T) {
testCases := map[string]struct {
a []corev1.ResourceName
b []corev1.ResourceName
expected []corev1.ResourceName
}{
"empty": {
a: []corev1.ResourceName{},
b: []corev1.ResourceName{},
expected: []corev1.ResourceName{},
},
"equal": {
a: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
b: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
expected: []corev1.ResourceName{},
},
"a has extra": {
a: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
b: []corev1.ResourceName{corev1.ResourceCPU},
expected: []corev1.ResourceName{corev1.ResourceMemory},
},
"b has extra": {
a: []corev1.ResourceName{corev1.ResourceCPU},
b: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
expected: []corev1.ResourceName{},
},
"dedupes": {
a: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceCPU, corev1.ResourceMemory, corev1.ResourceMemory},
b: []corev1.ResourceName{corev1.ResourceCPU},
expected: []corev1.ResourceName{corev1.ResourceMemory},
},
"sorts": {
a: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceMemory, corev1.ResourceCPU, corev1.ResourceCPU},
b: []corev1.ResourceName{},
expected: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
},
}
for testName, testCase := range testCases {
actual := Difference(testCase.a, testCase.b)
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("%s expected: %#v, actual: %#v", testName, testCase.expected, actual)
}
}
}
...@@ -1098,10 +1098,12 @@ func TestAdmitBestEffortQuotaLimitIgnoresBurstable(t *testing.T) { ...@@ -1098,10 +1098,12 @@ func TestAdmitBestEffortQuotaLimitIgnoresBurstable(t *testing.T) {
func TestHasUsageStats(t *testing.T) { func TestHasUsageStats(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
a corev1.ResourceQuota a corev1.ResourceQuota
relevant []corev1.ResourceName
expected bool expected bool
}{ }{
"empty": { "empty": {
a: corev1.ResourceQuota{Status: corev1.ResourceQuotaStatus{Hard: corev1.ResourceList{}}}, a: corev1.ResourceQuota{Status: corev1.ResourceQuotaStatus{Hard: corev1.ResourceList{}}},
relevant: []corev1.ResourceName{corev1.ResourceMemory},
expected: true, expected: true,
}, },
"hard-only": { "hard-only": {
...@@ -1113,6 +1115,7 @@ func TestHasUsageStats(t *testing.T) { ...@@ -1113,6 +1115,7 @@ func TestHasUsageStats(t *testing.T) {
Used: corev1.ResourceList{}, Used: corev1.ResourceList{},
}, },
}, },
relevant: []corev1.ResourceName{corev1.ResourceMemory},
expected: false, expected: false,
}, },
"hard-used": { "hard-used": {
...@@ -1126,11 +1129,27 @@ func TestHasUsageStats(t *testing.T) { ...@@ -1126,11 +1129,27 @@ func TestHasUsageStats(t *testing.T) {
}, },
}, },
}, },
relevant: []corev1.ResourceName{corev1.ResourceMemory},
expected: true,
},
"hard-used-relevant": {
a: corev1.ResourceQuota{
Status: corev1.ResourceQuotaStatus{
Hard: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("1Gi"),
corev1.ResourcePods: resource.MustParse("1"),
},
Used: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("500Mi"),
},
},
},
relevant: []corev1.ResourceName{corev1.ResourceMemory},
expected: true, expected: true,
}, },
} }
for testName, testCase := range testCases { for testName, testCase := range testCases {
if result := hasUsageStats(&testCase.a); result != testCase.expected { if result := hasUsageStats(&testCase.a, testCase.relevant); result != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, result) t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, result)
} }
} }
......
...@@ -460,8 +460,8 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat ...@@ -460,8 +460,8 @@ func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluat
if err := evaluator.Constraints(restrictedResources, inputObject); err != nil { if err := evaluator.Constraints(restrictedResources, inputObject); err != nil {
return nil, admission.NewForbidden(a, fmt.Errorf("failed quota: %s: %v", resourceQuota.Name, err)) return nil, admission.NewForbidden(a, fmt.Errorf("failed quota: %s: %v", resourceQuota.Name, err))
} }
if !hasUsageStats(&resourceQuota) { if !hasUsageStats(&resourceQuota, restrictedResources) {
return nil, admission.NewForbidden(a, fmt.Errorf("status unknown for quota: %s", resourceQuota.Name)) return nil, admission.NewForbidden(a, fmt.Errorf("status unknown for quota: %s, resources: %s", resourceQuota.Name, prettyPrintResourceNames(restrictedResources)))
} }
interestingQuotaIndexes = append(interestingQuotaIndexes, i) interestingQuotaIndexes = append(interestingQuotaIndexes, i)
localRestrictedResourcesSet := quota.ToSet(restrictedResources) localRestrictedResourcesSet := quota.ToSet(restrictedResources)
...@@ -702,9 +702,13 @@ func prettyPrintResourceNames(a []corev1.ResourceName) string { ...@@ -702,9 +702,13 @@ func prettyPrintResourceNames(a []corev1.ResourceName) string {
return strings.Join(values, ",") return strings.Join(values, ",")
} }
// hasUsageStats returns true if for each hard constraint there is a value for its current usage // hasUsageStats returns true if for each hard constraint in interestingResources there is a value for its current usage
func hasUsageStats(resourceQuota *corev1.ResourceQuota) bool { func hasUsageStats(resourceQuota *corev1.ResourceQuota, interestingResources []corev1.ResourceName) bool {
interestingSet := quota.ToSet(interestingResources)
for resourceName := range resourceQuota.Status.Hard { for resourceName := range resourceQuota.Status.Hard {
if !interestingSet.Has(string(resourceName)) {
continue
}
if _, found := resourceQuota.Status.Used[resourceName]; !found { if _, found := resourceQuota.Status.Used[resourceName]; !found {
return false return false
} }
......
...@@ -1186,6 +1186,7 @@ type PodSecurityPolicyList struct { ...@@ -1186,6 +1186,7 @@ type PodSecurityPolicyList struct {
Items []PodSecurityPolicy `json:"items" protobuf:"bytes,2,rep,name=items"` Items []PodSecurityPolicy `json:"items" protobuf:"bytes,2,rep,name=items"`
} }
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DEPRECATED 1.9 - This group version of NetworkPolicy is deprecated by networking/v1/NetworkPolicy. // DEPRECATED 1.9 - This group version of NetworkPolicy is deprecated by networking/v1/NetworkPolicy.
......
...@@ -12,6 +12,7 @@ go_library( ...@@ -12,6 +12,7 @@ go_library(
"deployment.go", "deployment.go",
"ingress.go", "ingress.go",
"interface.go", "interface.go",
"networkpolicy.go",
"podsecuritypolicy.go", "podsecuritypolicy.go",
"replicaset.go", "replicaset.go",
], ],
......
...@@ -30,6 +30,8 @@ type Interface interface { ...@@ -30,6 +30,8 @@ type Interface interface {
Deployments() DeploymentInformer Deployments() DeploymentInformer
// Ingresses returns a IngressInformer. // Ingresses returns a IngressInformer.
Ingresses() IngressInformer Ingresses() IngressInformer
// NetworkPolicies returns a NetworkPolicyInformer.
NetworkPolicies() NetworkPolicyInformer
// PodSecurityPolicies returns a PodSecurityPolicyInformer. // PodSecurityPolicies returns a PodSecurityPolicyInformer.
PodSecurityPolicies() PodSecurityPolicyInformer PodSecurityPolicies() PodSecurityPolicyInformer
// ReplicaSets returns a ReplicaSetInformer. // ReplicaSets returns a ReplicaSetInformer.
...@@ -62,6 +64,11 @@ func (v *version) Ingresses() IngressInformer { ...@@ -62,6 +64,11 @@ func (v *version) Ingresses() IngressInformer {
return &ingressInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} return &ingressInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
} }
// NetworkPolicies returns a NetworkPolicyInformer.
func (v *version) NetworkPolicies() NetworkPolicyInformer {
return &networkPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// PodSecurityPolicies returns a PodSecurityPolicyInformer. // PodSecurityPolicies returns a PodSecurityPolicyInformer.
func (v *version) PodSecurityPolicies() PodSecurityPolicyInformer { func (v *version) PodSecurityPolicies() PodSecurityPolicyInformer {
return &podSecurityPolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} return &podSecurityPolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
......
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1beta1
import (
time "time"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
internalinterfaces "k8s.io/client-go/informers/internalinterfaces"
kubernetes "k8s.io/client-go/kubernetes"
v1beta1 "k8s.io/client-go/listers/extensions/v1beta1"
cache "k8s.io/client-go/tools/cache"
)
// NetworkPolicyInformer provides access to a shared informer and lister for
// NetworkPolicies.
type NetworkPolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1beta1.NetworkPolicyLister
}
type networkPolicyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewNetworkPolicyInformer constructs a new informer for NetworkPolicy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewNetworkPolicyInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredNetworkPolicyInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredNetworkPolicyInformer constructs a new informer for NetworkPolicy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredNetworkPolicyInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ExtensionsV1beta1().NetworkPolicies(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ExtensionsV1beta1().NetworkPolicies(namespace).Watch(options)
},
},
&extensionsv1beta1.NetworkPolicy{},
resyncPeriod,
indexers,
)
}
func (f *networkPolicyInformer) defaultInformer(client kubernetes.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredNetworkPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *networkPolicyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&extensionsv1beta1.NetworkPolicy{}, f.defaultInformer)
}
func (f *networkPolicyInformer) Lister() v1beta1.NetworkPolicyLister {
return v1beta1.NewNetworkPolicyLister(f.Informer().GetIndexer())
}
...@@ -206,6 +206,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource ...@@ -206,6 +206,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Deployments().Informer()}, nil return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Deployments().Informer()}, nil
case extensionsv1beta1.SchemeGroupVersion.WithResource("ingresses"): case extensionsv1beta1.SchemeGroupVersion.WithResource("ingresses"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Ingresses().Informer()}, nil return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().Ingresses().Informer()}, nil
case extensionsv1beta1.SchemeGroupVersion.WithResource("networkpolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().NetworkPolicies().Informer()}, nil
case extensionsv1beta1.SchemeGroupVersion.WithResource("podsecuritypolicies"): case extensionsv1beta1.SchemeGroupVersion.WithResource("podsecuritypolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().PodSecurityPolicies().Informer()}, nil return &genericInformer{resource: resource.GroupResource(), informer: f.Extensions().V1beta1().PodSecurityPolicies().Informer()}, nil
case extensionsv1beta1.SchemeGroupVersion.WithResource("replicasets"): case extensionsv1beta1.SchemeGroupVersion.WithResource("replicasets"):
......
...@@ -15,6 +15,7 @@ go_library( ...@@ -15,6 +15,7 @@ go_library(
"extensions_client.go", "extensions_client.go",
"generated_expansion.go", "generated_expansion.go",
"ingress.go", "ingress.go",
"networkpolicy.go",
"podsecuritypolicy.go", "podsecuritypolicy.go",
"replicaset.go", "replicaset.go",
], ],
......
...@@ -30,6 +30,7 @@ type ExtensionsV1beta1Interface interface { ...@@ -30,6 +30,7 @@ type ExtensionsV1beta1Interface interface {
DaemonSetsGetter DaemonSetsGetter
DeploymentsGetter DeploymentsGetter
IngressesGetter IngressesGetter
NetworkPoliciesGetter
PodSecurityPoliciesGetter PodSecurityPoliciesGetter
ReplicaSetsGetter ReplicaSetsGetter
} }
...@@ -51,6 +52,10 @@ func (c *ExtensionsV1beta1Client) Ingresses(namespace string) IngressInterface { ...@@ -51,6 +52,10 @@ func (c *ExtensionsV1beta1Client) Ingresses(namespace string) IngressInterface {
return newIngresses(c, namespace) return newIngresses(c, namespace)
} }
func (c *ExtensionsV1beta1Client) NetworkPolicies(namespace string) NetworkPolicyInterface {
return newNetworkPolicies(c, namespace)
}
func (c *ExtensionsV1beta1Client) PodSecurityPolicies() PodSecurityPolicyInterface { func (c *ExtensionsV1beta1Client) PodSecurityPolicies() PodSecurityPolicyInterface {
return newPodSecurityPolicies(c) return newPodSecurityPolicies(c)
} }
......
...@@ -14,6 +14,7 @@ go_library( ...@@ -14,6 +14,7 @@ go_library(
"fake_deployment_expansion.go", "fake_deployment_expansion.go",
"fake_extensions_client.go", "fake_extensions_client.go",
"fake_ingress.go", "fake_ingress.go",
"fake_networkpolicy.go",
"fake_podsecuritypolicy.go", "fake_podsecuritypolicy.go",
"fake_replicaset.go", "fake_replicaset.go",
], ],
......
...@@ -40,6 +40,10 @@ func (c *FakeExtensionsV1beta1) Ingresses(namespace string) v1beta1.IngressInter ...@@ -40,6 +40,10 @@ func (c *FakeExtensionsV1beta1) Ingresses(namespace string) v1beta1.IngressInter
return &FakeIngresses{c, namespace} return &FakeIngresses{c, namespace}
} }
func (c *FakeExtensionsV1beta1) NetworkPolicies(namespace string) v1beta1.NetworkPolicyInterface {
return &FakeNetworkPolicies{c, namespace}
}
func (c *FakeExtensionsV1beta1) PodSecurityPolicies() v1beta1.PodSecurityPolicyInterface { func (c *FakeExtensionsV1beta1) PodSecurityPolicies() v1beta1.PodSecurityPolicyInterface {
return &FakePodSecurityPolicies{c} return &FakePodSecurityPolicies{c}
} }
......
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1beta1 "k8s.io/api/extensions/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeNetworkPolicies implements NetworkPolicyInterface
type FakeNetworkPolicies struct {
Fake *FakeExtensionsV1beta1
ns string
}
var networkpoliciesResource = schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "networkpolicies"}
var networkpoliciesKind = schema.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}
// Get takes name of the networkPolicy, and returns the corresponding networkPolicy object, and an error if there is any.
func (c *FakeNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1beta1.NetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(networkpoliciesResource, c.ns, name), &v1beta1.NetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.NetworkPolicy), err
}
// List takes label and field selectors, and returns the list of NetworkPolicies that match those selectors.
func (c *FakeNetworkPolicies) List(opts v1.ListOptions) (result *v1beta1.NetworkPolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(networkpoliciesResource, networkpoliciesKind, c.ns, opts), &v1beta1.NetworkPolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1beta1.NetworkPolicyList{ListMeta: obj.(*v1beta1.NetworkPolicyList).ListMeta}
for _, item := range obj.(*v1beta1.NetworkPolicyList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested networkPolicies.
func (c *FakeNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(networkpoliciesResource, c.ns, opts))
}
// Create takes the representation of a networkPolicy and creates it. Returns the server's representation of the networkPolicy, and an error, if there is any.
func (c *FakeNetworkPolicies) Create(networkPolicy *v1beta1.NetworkPolicy) (result *v1beta1.NetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(networkpoliciesResource, c.ns, networkPolicy), &v1beta1.NetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.NetworkPolicy), err
}
// Update takes the representation of a networkPolicy and updates it. Returns the server's representation of the networkPolicy, and an error, if there is any.
func (c *FakeNetworkPolicies) Update(networkPolicy *v1beta1.NetworkPolicy) (result *v1beta1.NetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(networkpoliciesResource, c.ns, networkPolicy), &v1beta1.NetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.NetworkPolicy), err
}
// Delete takes name of the networkPolicy and deletes it. Returns an error if one occurs.
func (c *FakeNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(networkpoliciesResource, c.ns, name), &v1beta1.NetworkPolicy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(networkpoliciesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1beta1.NetworkPolicyList{})
return err
}
// Patch applies the patch and returns the patched networkPolicy.
func (c *FakeNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.NetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(networkpoliciesResource, c.ns, name, pt, data, subresources...), &v1beta1.NetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1beta1.NetworkPolicy), err
}
...@@ -22,6 +22,8 @@ type DaemonSetExpansion interface{} ...@@ -22,6 +22,8 @@ type DaemonSetExpansion interface{}
type IngressExpansion interface{} type IngressExpansion interface{}
type NetworkPolicyExpansion interface{}
type PodSecurityPolicyExpansion interface{} type PodSecurityPolicyExpansion interface{}
type ReplicaSetExpansion interface{} type ReplicaSetExpansion interface{}
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
"time"
v1beta1 "k8s.io/api/extensions/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
scheme "k8s.io/client-go/kubernetes/scheme"
rest "k8s.io/client-go/rest"
)
// NetworkPoliciesGetter has a method to return a NetworkPolicyInterface.
// A group's client should implement this interface.
type NetworkPoliciesGetter interface {
NetworkPolicies(namespace string) NetworkPolicyInterface
}
// NetworkPolicyInterface has methods to work with NetworkPolicy resources.
type NetworkPolicyInterface interface {
Create(*v1beta1.NetworkPolicy) (*v1beta1.NetworkPolicy, error)
Update(*v1beta1.NetworkPolicy) (*v1beta1.NetworkPolicy, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1beta1.NetworkPolicy, error)
List(opts v1.ListOptions) (*v1beta1.NetworkPolicyList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.NetworkPolicy, err error)
NetworkPolicyExpansion
}
// networkPolicies implements NetworkPolicyInterface
type networkPolicies struct {
client rest.Interface
ns string
}
// newNetworkPolicies returns a NetworkPolicies
func newNetworkPolicies(c *ExtensionsV1beta1Client, namespace string) *networkPolicies {
return &networkPolicies{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the networkPolicy, and returns the corresponding networkPolicy object, and an error if there is any.
func (c *networkPolicies) Get(name string, options v1.GetOptions) (result *v1beta1.NetworkPolicy, err error) {
result = &v1beta1.NetworkPolicy{}
err = c.client.Get().
Namespace(c.ns).
Resource("networkpolicies").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of NetworkPolicies that match those selectors.
func (c *networkPolicies) List(opts v1.ListOptions) (result *v1beta1.NetworkPolicyList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1beta1.NetworkPolicyList{}
err = c.client.Get().
Namespace(c.ns).
Resource("networkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested networkPolicies.
func (c *networkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("networkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a networkPolicy and creates it. Returns the server's representation of the networkPolicy, and an error, if there is any.
func (c *networkPolicies) Create(networkPolicy *v1beta1.NetworkPolicy) (result *v1beta1.NetworkPolicy, err error) {
result = &v1beta1.NetworkPolicy{}
err = c.client.Post().
Namespace(c.ns).
Resource("networkpolicies").
Body(networkPolicy).
Do().
Into(result)
return
}
// Update takes the representation of a networkPolicy and updates it. Returns the server's representation of the networkPolicy, and an error, if there is any.
func (c *networkPolicies) Update(networkPolicy *v1beta1.NetworkPolicy) (result *v1beta1.NetworkPolicy, err error) {
result = &v1beta1.NetworkPolicy{}
err = c.client.Put().
Namespace(c.ns).
Resource("networkpolicies").
Name(networkPolicy.Name).
Body(networkPolicy).
Do().
Into(result)
return
}
// Delete takes name of the networkPolicy and deletes it. Returns an error if one occurs.
func (c *networkPolicies) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("networkpolicies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *networkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("networkpolicies").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched networkPolicy.
func (c *networkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.NetworkPolicy, err error) {
result = &v1beta1.NetworkPolicy{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("networkpolicies").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}
...@@ -15,6 +15,7 @@ go_library( ...@@ -15,6 +15,7 @@ go_library(
"deployment_expansion.go", "deployment_expansion.go",
"expansion_generated.go", "expansion_generated.go",
"ingress.go", "ingress.go",
"networkpolicy.go",
"podsecuritypolicy.go", "podsecuritypolicy.go",
"replicaset.go", "replicaset.go",
"replicaset_expansion.go", "replicaset_expansion.go",
......
...@@ -26,6 +26,14 @@ type IngressListerExpansion interface{} ...@@ -26,6 +26,14 @@ type IngressListerExpansion interface{}
// IngressNamespaceLister. // IngressNamespaceLister.
type IngressNamespaceListerExpansion interface{} type IngressNamespaceListerExpansion interface{}
// NetworkPolicyListerExpansion allows custom methods to be added to
// NetworkPolicyLister.
type NetworkPolicyListerExpansion interface{}
// NetworkPolicyNamespaceListerExpansion allows custom methods to be added to
// NetworkPolicyNamespaceLister.
type NetworkPolicyNamespaceListerExpansion interface{}
// PodSecurityPolicyListerExpansion allows custom methods to be added to // PodSecurityPolicyListerExpansion allows custom methods to be added to
// PodSecurityPolicyLister. // PodSecurityPolicyLister.
type PodSecurityPolicyListerExpansion interface{} type PodSecurityPolicyListerExpansion interface{}
/*
Copyright 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
import (
v1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// NetworkPolicyLister helps list NetworkPolicies.
type NetworkPolicyLister interface {
// List lists all NetworkPolicies in the indexer.
List(selector labels.Selector) (ret []*v1beta1.NetworkPolicy, err error)
// NetworkPolicies returns an object that can list and get NetworkPolicies.
NetworkPolicies(namespace string) NetworkPolicyNamespaceLister
NetworkPolicyListerExpansion
}
// networkPolicyLister implements the NetworkPolicyLister interface.
type networkPolicyLister struct {
indexer cache.Indexer
}
// NewNetworkPolicyLister returns a new NetworkPolicyLister.
func NewNetworkPolicyLister(indexer cache.Indexer) NetworkPolicyLister {
return &networkPolicyLister{indexer: indexer}
}
// List lists all NetworkPolicies in the indexer.
func (s *networkPolicyLister) List(selector labels.Selector) (ret []*v1beta1.NetworkPolicy, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.NetworkPolicy))
})
return ret, err
}
// NetworkPolicies returns an object that can list and get NetworkPolicies.
func (s *networkPolicyLister) NetworkPolicies(namespace string) NetworkPolicyNamespaceLister {
return networkPolicyNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// NetworkPolicyNamespaceLister helps list and get NetworkPolicies.
type NetworkPolicyNamespaceLister interface {
// List lists all NetworkPolicies in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1beta1.NetworkPolicy, err error)
// Get retrieves the NetworkPolicy from the indexer for a given namespace and name.
Get(name string) (*v1beta1.NetworkPolicy, error)
NetworkPolicyNamespaceListerExpansion
}
// networkPolicyNamespaceLister implements the NetworkPolicyNamespaceLister
// interface.
type networkPolicyNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all NetworkPolicies in the indexer for a given namespace.
func (s networkPolicyNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.NetworkPolicy, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.NetworkPolicy))
})
return ret, err
}
// Get retrieves the NetworkPolicy from the indexer for a given namespace and name.
func (s networkPolicyNamespaceLister) Get(name string) (*v1beta1.NetworkPolicy, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("networkpolicy"), name)
}
return obj.(*v1beta1.NetworkPolicy), nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment