diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1001a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/** diff --git a/cmd/per-node-resources/doc.go b/cmd/per-node-resources/doc.go new file mode 100644 index 0000000..3a6392a --- /dev/null +++ b/cmd/per-node-resources/doc.go @@ -0,0 +1,16 @@ +// Copyright 2019-2022 The Liqo 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 main bootstraps the main plugin +package main diff --git a/cmd/per-node-resources/main.go b/cmd/per-node-resources/main.go new file mode 100644 index 0000000..a499e88 --- /dev/null +++ b/cmd/per-node-resources/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "os" + "time" + + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + + grpcserver "github.com/liqotech/liqo-resource-plugins/pkg/per-node-resources" + "github.com/liqotech/liqo/pkg/utils/restcfg" +) + +func main() { + var port int + var selector string + var resyncPeriod time.Duration + var nodeSelector labels.Selector + var includeVirtualNodes bool + + var rootCmd = &cobra.Command{ + Use: os.Args[0], + Short: "Liqo plugin which devides the available resource among peered clusters", + SilenceUsage: true, + PreRunE: func(cmd *cobra.Command, args []string) error { + var err error + if nodeSelector, err = labels.Parse(selector); err != nil { + return err + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + config := restcfg.SetRateLimiter(ctrl.GetConfigOrDie()) + clientset := kubernetes.NewForConfigOrDie(config) + + return grpcserver.ListenAndServeGRPCServer(port, clientset, resyncPeriod, nodeSelector, includeVirtualNodes) + }, + } + + rootCmd.PersistentFlags().BoolVar(&includeVirtualNodes, "include-virtual-nodes", false, + "if true resources of virtual nodes are considered, otherwise ignored") + rootCmd.PersistentFlags().DurationVar(&resyncPeriod, "resync-period", 10*time.Hour, "the resync period for the informers") + rootCmd.PersistentFlags().IntVar(&port, "port", 6001, "set port where the server will listen on.") + rootCmd.PersistentFlags().StringVar(&selector, "selector", "", "the selector to filter nodes. This flag can be used multiple times.") + + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index 9faaa52..1d27989 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,27 @@ go 1.19 require ( github.com/liqotech/liqo v0.6.0 + github.com/onsi/ginkgo/v2 v2.6.1 github.com/spf13/cobra v1.6.1 google.golang.org/grpc v1.50.1 - k8s.io/apimachinery v0.25.3 + k8s.io/apimachinery v0.26.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + k8s.io/apiextensions-apiserver v0.26.0 // indirect + k8s.io/component-base v0.26.0 // indirect ) require ( @@ -31,29 +49,30 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/gomega v1.24.2 github.com/pkg/errors v0.9.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/virtual-kubelet/virtual-kubelet v1.6.1-0.20220831210300-d2523fe808a2 // indirect - golang.org/x/net v0.1.0 // indirect + golang.org/x/net v0.4.0 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/term v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect - golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/term v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.25.3 // indirect - k8s.io/client-go v0.25.3 // indirect + k8s.io/api v0.26.0 + k8s.io/client-go v0.26.0 k8s.io/klog/v2 v2.80.1 // indirect - k8s.io/kube-openapi v0.0.0-20220928191237-829ce0c27909 // indirect - k8s.io/kubectl v0.25.3 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + k8s.io/kubectl v0.25.3 k8s.io/metrics v0.25.3 // indirect - k8s.io/utils v0.0.0-20220922133306-665eaaec4324 // indirect - sigs.k8s.io/controller-runtime v0.13.0 // indirect + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect + sigs.k8s.io/controller-runtime v0.14.0 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index d04555a..88d720d 100644 --- a/go.sum +++ b/go.sum @@ -75,7 +75,6 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/bombsimon/logrusr v1.0.0/go.mod h1:Jq0nHtvxabKE5EMwAAdgTaz7dfWE8C4i11NOltxGQpc= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -126,15 +125,19 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -220,6 +223,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -280,6 +284,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= @@ -357,6 +362,7 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= +github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -375,7 +381,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -383,14 +388,15 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= +github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= +github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= +github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -407,13 +413,15 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -510,6 +518,7 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= @@ -517,7 +526,7 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -603,8 +612,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -680,12 +689,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -694,15 +704,15 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -756,13 +766,14 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -868,7 +879,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -896,24 +906,26 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= k8s.io/api v0.19.10/go.mod h1:FSHnCfzQ9/b9qSSF1A1+QWk5cdO1auExjLcc/RJ/zB0= -k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= -k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= +k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= +k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= -k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= +k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo= +k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ= k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.19.10/go.mod h1:9eb44nUQSsz9QZiilFRuMj3ZbTmoWolU8S2gnXoRMjo= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= +k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= +k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/apiserver v0.19.10/go.mod h1:kmUiThSt+QHVWZmlskXRXa+ODBgZkeTi5m+LgJvD1pk= k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= k8s.io/client-go v0.19.10/go.mod h1:z1zmFQISK3I2eWLcRjhhX2/DpQUuq5Jemy45W2a5cPQ= -k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= -k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= +k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= +k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/component-base v0.19.10/go.mod h1:zI8HZGnByYplvaM9JevUscbVVG4bTGzJmrDR60jffvQ= -k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= +k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs= +k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= @@ -922,24 +934,24 @@ k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20220928191237-829ce0c27909 h1:q/70bz7C1/LGuQu/JBX7Fpi55CwcCts/wbvlehe0RRo= -k8s.io/kube-openapi v0.0.0-20220928191237-829ce0c27909/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= k8s.io/metrics v0.25.3 h1:fp5RuALkbwI3UbKITdNYu6sa3LF4JPANR/ofq3oe+Fg= k8s.io/metrics v0.25.3/go.mod h1:5j5FKJb8RHsb3Q2PLsD/p1mLiA1fTrl+a62Les+KDhc= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220922133306-665eaaec4324 h1:i+xdFemcSNuJvIfBlaYuXgRondKxK4z4prVPKzEaelI= -k8s.io/utils v0.0.0-20220922133306-665eaaec4324/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/controller-runtime v0.7.1/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU= -sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= -sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/controller-runtime v0.14.0 h1:ju2xsov5Ara6FoQuddg+az+rAxsUsTYn2IYyEKCTyDc= +sigs.k8s.io/controller-runtime v0.14.0/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/pkg/per-node-resources/doc.go b/pkg/per-node-resources/doc.go new file mode 100644 index 0000000..093edf5 --- /dev/null +++ b/pkg/per-node-resources/doc.go @@ -0,0 +1,16 @@ +// Copyright 2019-2022 The Liqo 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 pernoderesources is the standard implementation to handle resources in liqo +package pernoderesources diff --git a/pkg/per-node-resources/grpc_server.go b/pkg/per-node-resources/grpc_server.go new file mode 100644 index 0000000..a33cf41 --- /dev/null +++ b/pkg/per-node-resources/grpc_server.go @@ -0,0 +1,148 @@ +// Copyright 2019-2022 The Liqo 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 pernoderesources + +import ( + "context" + "fmt" + "log" + "net" + "sync" + "time" + + "google.golang.org/grpc" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + + pluginsutils "github.com/liqotech/liqo-resource-plugins/pkg/utils" + resourcemonitors "github.com/liqotech/liqo/pkg/liqo-controller-manager/resource-request-controller/resource-monitors" +) + +type server struct { + Server *grpc.Server + resourcemonitors.ResourceReaderServer + subscribers sync.Map + nodesHandler *NodesHandler + notifyChan chan bool +} + +// ListenAndServeGRPCServer creates the gRPC server and makes it listen on the given port. +func ListenAndServeGRPCServer(port int, clientset *kubernetes.Clientset, + resyncPeriod time.Duration, nodeSelector labels.Selector, includeVirtualNodes bool) error { + ctx := ctrl.SetupSignalHandler() + // server setup. The stream is not initialized here because it needs a subscriber so + // it will be initialized in the Subscribe method below + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + return fmt.Errorf("failed to listen on port %d: %w", port, err) + } + + s := server{ + Server: grpc.NewServer(), + notifyChan: make(chan bool, 1), + } + nh, err := NewNodesHandler(ctx, clientset, resyncPeriod, &s, nodeSelector, includeVirtualNodes) + if err != nil { + return err + } + s.nodesHandler = nh + + // This is a custom implementation, it waits for pods informers cache to be ready + nh.WaitForCacheSync() + + // register this server using the register interface defined in liqo + resourcemonitors.RegisterResourceReaderServer(s.Server, &s) + log.Printf("info: external resource monitor listening at %v", lis.Addr()) + go s.notifier() + log.Printf("info: notifier started") + if err := s.Server.Serve(lis); err != nil { + return fmt.Errorf("grpc server failed to serve: %w", err) + } + + return nil +} + +// ReadResources receives a clusterID and returns the resources for that specific clusterID. In this version of the resource plugin +// the clusterID is ignored and the same resources are returned for every clusterID received. Since this method could be called multiple +// times it has to be idempotent. +func (s *server) ReadResources(ctx context.Context, req *resourcemonitors.ClusterIdentity) (*resourcemonitors.ResourceList, error) { + log.Printf("info: reading resources for cluster %s", req.ClusterID) + + wholeClusterAllocatable := s.nodesHandler.getWholeClusterAllocatable() + pluginsutils.AddResources(wholeClusterAllocatable, s.nodesHandler.getClusterIDResources(req.ClusterID)) + pluginsutils.NormalizeResources(wholeClusterAllocatable) + + return &resourcemonitors.ResourceList{Resources: pluginsutils.MapResources(wholeClusterAllocatable)}, nil +} + +// Subscribe is quite standard in this implementation so the only thing that it does is to notify liqo immediately. +func (s *server) Subscribe(req *resourcemonitors.Empty, srv resourcemonitors.ResourceReader_SubscribeServer) error { + log.Printf("info: liqo controller manager subscribed") + + // Store the stream. Using req as key since each request will have a different req object. + s.subscribers.Store(req, srv) + ctx := srv.Context() + + for { + <-ctx.Done() + s.subscribers.Delete(req) + log.Printf("info: liqo controller manager disconnected") + return nil + } +} + +// RemoveCluster is useful to clean cluster's information if it exists when a cluster is upeered. This method receives +// a clusterID which identifies the cluster that has been removed. We believe that this method is useful in custom +// implementation, for example where a database is involved in the implementation. +func (s *server) RemoveCluster(ctx context.Context, req *resourcemonitors.ClusterIdentity) (*resourcemonitors.Empty, error) { + log.Printf("info: removing cluster having clusterID %s", req.ClusterID) + + s.nodesHandler.deletePodsByClusterID(req.ClusterID) + return &resourcemonitors.Empty{}, nil +} + +// Notify uses the cached streams to notify liqo that some resources changed. +// It notifies Liqo every second if an event that requires a resources update occurred. +func (s *server) notifier() { + log.Printf("info: sending notification to liqo controller manager for all clusters") + var err error + for { + <-s.notifyChan + s.subscribers.Range(func(key, value interface{}) bool { + stream := value.(resourcemonitors.ResourceReader_SubscribeServer) + + err = stream.Send(&resourcemonitors.ClusterIdentity{ClusterID: resourcemonitors.AllClusterIDs}) + if err != nil { + err = fmt.Errorf("error: error during sending a notification %w", err) + } + return true + }) + if err != nil { + fmt.Printf("%s", err) + } + + log.Printf("info: notification sent to liqo controller manager for all clusters") + time.Sleep(time.Second) + } +} + +// Notify send a message in the notifyChannel to let the notifier know that it must send a notification. +func (s *server) Notify() { + select { + case s.notifyChan <- false: + default: + } +} diff --git a/pkg/per-node-resources/nodes_handler.go b/pkg/per-node-resources/nodes_handler.go new file mode 100644 index 0000000..372fb29 --- /dev/null +++ b/pkg/per-node-resources/nodes_handler.go @@ -0,0 +1,245 @@ +package pernoderesources + +import ( + "context" + "log" + "sync" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + + pluginsutils "github.com/liqotech/liqo-resource-plugins/pkg/utils" +) + +type nodeInfo struct { + ready bool + podsHandler *PodsHandler + allocatable corev1.ResourceList +} + +// NodesHandler exposes methods to handle resources divided per node. +type NodesHandler struct { + mutex sync.RWMutex + notifier *server + nodes map[string]nodeInfo + initChan chan bool + createPodInformer func(string) *PodsHandler +} + +// NewNodesHandler creates and returns a new NodesHandler having an empty map of nodes. +func NewNodesHandler(ctx context.Context, clientset *kubernetes.Clientset, resyncPeriod time.Duration, + notifier *server, nodeSelector labels.Selector, includeVirtualNodes bool) (*NodesHandler, error) { + nh := &NodesHandler{ + notifier: notifier, + nodes: make(map[string]nodeInfo), + initChan: make(chan bool, 1), + } + + nh.createPodInformer = func(nodeName string) *PodsHandler { + ph, err := NewPodsHandler(ctx, nodeName, clientset, resyncPeriod, nh, notifier) + if err != nil { + //TODO: what should we do? If the pod handler creations fails nothing will work. + return nil + } + + nh.mutex.Lock() + defer nh.mutex.Unlock() + + if entry, exists := nh.nodes[nodeName]; exists { + entry.podsHandler = ph + nh.nodes[nodeName] = entry + } + + select { + case nh.initChan <- false: + default: + } + + return ph + } + + nodeFactory := informers.NewSharedInformerFactoryWithOptions( + clientset, resyncPeriod, informers.WithTweakListOptions(pluginsutils.FilterNodes(nodeSelector, includeVirtualNodes)), + ) + nodeInformer := nodeFactory.Core().V1().Nodes().Informer() + + _, err := nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: nh.onNodeAdd, + UpdateFunc: nh.onNodeUpdate, + DeleteFunc: nh.onNodeDelete, + }) + if err != nil { + return nil, err + } + + nodeFactory.Start(ctx.Done()) + nodeFactory.WaitForCacheSync(ctx.Done()) + + return nh, nil +} + +// insertNewNode creates a nodeInfo struct to store all node's information. +func (nh *NodesHandler) insertNewNode(nodeName string, allocatable corev1.ResourceList, nodeReady bool) { + nh.mutex.Lock() + defer nh.mutex.Unlock() + + node := nodeInfo{ + podsHandler: nh.createPodInformer(nodeName), + allocatable: allocatable, + ready: nodeReady, + } + nh.nodes[nodeName] = node +} + +// turnNodeOff toggle node status from ready to not-ready and vice-versa. +func (nh *NodesHandler) turnNodeOff(nodeName string) { + nh.mutex.Lock() + defer nh.mutex.Unlock() + + if entry, exists := nh.nodes[nodeName]; exists { + if entry.ready { + entry.ready = false + nh.nodes[nodeName] = entry + } + } +} + +// decreaseAllocatableForNode subtracts the given resources from the node's allocatable resources. +func (nh *NodesHandler) decreaseAllocatableForNode(nodeName string, resources corev1.ResourceList) { + nh.mutex.Lock() + defer nh.mutex.Unlock() + + if entry, exists := nh.nodes[nodeName]; exists { + pluginsutils.SubResources(entry.allocatable, resources) + nh.nodes[nodeName] = entry + } +} + +// increaseAllocatableForNode adds the given resources to the node's allocatable resources. +func (nh *NodesHandler) increaseAllocatableForNode(nodeName string, resources corev1.ResourceList) { + nh.mutex.Lock() + defer nh.mutex.Unlock() + + if entry, exists := nh.nodes[nodeName]; exists { + pluginsutils.AddResources(entry.allocatable, resources) + nh.nodes[nodeName] = entry + } +} + +// setAllocatableForNode substitutes the actual resources with the given ones. +func (nh *NodesHandler) setAllocatableForNode(nodeName string, resources corev1.ResourceList) { + nh.mutex.Lock() + defer nh.mutex.Unlock() + + if entry, exists := nh.nodes[nodeName]; exists { + entry.allocatable = resources + nh.nodes[nodeName] = entry + } +} + +// deletePodsByClusterID deletes all the pods belonging to the cluster identified by the given clusterID. +func (nh *NodesHandler) deletePodsByClusterID(clusterID string) { + nh.mutex.Lock() + defer nh.mutex.Unlock() + + for _, entry := range nh.nodes { + entry.podsHandler.deletePodsByClusterID(clusterID) + } +} + +func (nh *NodesHandler) getWholeClusterAllocatable() corev1.ResourceList { + nh.mutex.RLock() + defer nh.mutex.RUnlock() + + resources := corev1.ResourceList{} + + for _, entry := range nh.nodes { + if entry.ready && entry.podsHandler.cacheReady { + pluginsutils.AddResources(resources, entry.allocatable) + } + } + + return resources +} + +func (nh *NodesHandler) getClusterIDResources(clusterID string) corev1.ResourceList { + nh.mutex.RLock() + defer nh.mutex.RUnlock() + + resources := corev1.ResourceList{} + + for _, nodeEntry := range nh.nodes { + if nodeEntry.ready { + pluginsutils.AddResources(resources, nodeEntry.podsHandler.getPodsResourcesByClusterID(clusterID)) + } + } + + return resources +} + +// react to a Node Creation/First informer run. +func (nh *NodesHandler) onNodeAdd(obj interface{}) { + node := obj.(*corev1.Node) + log.Printf("Adding Node %s", node.Name) + nh.insertNewNode(node.Name, node.Status.Allocatable, pluginsutils.IsNodeReadyAndSchedulable(node)) +} + +// react to a Node Update. +func (nh *NodesHandler) onNodeUpdate(oldObj, newObj interface{}) { + oldNode := oldObj.(*corev1.Node) + newNode := newObj.(*corev1.Node) + newNodeResources := newNode.Status.Allocatable + log.Printf("Updating Node %s", oldNode.Name) + if pluginsutils.IsNodeReadyAndSchedulable(newNode) { + // node was already Ready, update with possible new resources. + // if ready but not schedulable it doesn't update the resources since it is useless. + // the resources will be updated as soon as the node becomes schedulable. + if pluginsutils.IsNodeReadyAndSchedulable(oldNode) { + nh.setAllocatableForNode(oldNode.Name, newNodeResources) + nh.notifier.Notify() + } else { + nh.insertNewNode(newNode.Name, newNodeResources, false) + } + // node is terminating or stopping, delete all its resources. + } else if pluginsutils.IsNodeReadyAndSchedulable(oldNode) && !pluginsutils.IsNodeReadyAndSchedulable(newNode) { + nh.turnNodeOff(oldNode.Name) + nh.notifier.Notify() + } +} + +// react to a Node Delete. +func (nh *NodesHandler) onNodeDelete(obj interface{}) { + node := obj.(*corev1.Node) + log.Printf("info: Deleting Node %s", node.Name) + if entry, exists := nh.nodes[node.Name]; exists { + entry.podsHandler.stopFunction() + } + delete(nh.nodes, node.Name) + nh.notifier.Notify() +} + +func (nh *NodesHandler) areAllPodsInformerReady() bool { + nh.mutex.RLock() + defer nh.mutex.RUnlock() + + for _, entry := range nh.nodes { + if !entry.podsHandler.cacheReady { + return false + } + } + return true +} + +// WaitForCacheSync waits for pods informers cache to be ready. +func (nh *NodesHandler) WaitForCacheSync() { + for { + <-nh.initChan + if nh.areAllPodsInformerReady() { + break + } + } +} diff --git a/pkg/per-node-resources/nodes_handler_test.go b/pkg/per-node-resources/nodes_handler_test.go new file mode 100644 index 0000000..f4d46b0 --- /dev/null +++ b/pkg/per-node-resources/nodes_handler_test.go @@ -0,0 +1,12 @@ +package pernoderesources + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("checking nodes handler implementation", func() { + When("a node is discovered", func() { + Expect(nil).NotTo(HaveOccurred()) + }) +}) diff --git a/pkg/per-node-resources/pods_handler.go b/pkg/per-node-resources/pods_handler.go new file mode 100644 index 0000000..97694ff --- /dev/null +++ b/pkg/per-node-resources/pods_handler.go @@ -0,0 +1,148 @@ +package pernoderesources + +import ( + "context" + "log" + "sync" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + + pluginsutils "github.com/liqotech/liqo-resource-plugins/pkg/utils" + "github.com/liqotech/liqo/pkg/virtualKubelet/forge" +) + +// PodsHandler exposes methods to handle pods' resources divided per clusterID. +type PodsHandler struct { + pods map[string]corev1.ResourceList + stopFunction context.CancelFunc + cacheReady bool + mutex sync.RWMutex + nodesHandler *NodesHandler + notifier *server +} + +// NewPodsHandler creates a new pods handler. +func NewPodsHandler(ctx context.Context, nodeName string, clientset *kubernetes.Clientset, + resyncPeriod time.Duration, nodesHandler *NodesHandler, notifier *server) (*PodsHandler, error) { + ph := &PodsHandler{ + pods: make(map[string]corev1.ResourceList), + cacheReady: false, + nodesHandler: nodesHandler, + notifier: notifier, + } + + informerCtx, cancel := context.WithCancel(ctx) + podFactory := informers.NewSharedInformerFactoryWithOptions( + clientset, resyncPeriod, + informers.WithTweakListOptions(pluginsutils.FilterPods(nodeName)), + ) + podInformer := podFactory.Core().V1().Pods().Informer() + _, err := podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: ph.onPodAdd, + // We do not care about update events, since resources are immutable. + DeleteFunc: ph.onPodDelete, + }) + if err != nil { + cancel() + return nil, err + } + + ph.stopFunction = cancel + podFactory.Start(informerCtx.Done()) + go func() { + // wait to synch the cache before write the resource and notify + podFactory.WaitForCacheSync(informerCtx.Done()) + if informerCtx.Err() == nil { + ph.cacheReady = true + ph.notifier.Notify() + } + }() + + return ph, nil +} + +// onPodAdd reacts on pod add. +func (ph *PodsHandler) onPodAdd(obj interface{}) { + // Thanks to the filters at the informer level, add events are received only when pods running on physical nodes turn running. + podAdded := obj.(*corev1.Pod) + log.Printf("info: OnPodAdd: Add for pod %s:%s", podAdded.Namespace, podAdded.Name) + + podResources := pluginsutils.ExtractPodResources(podAdded) + ph.nodesHandler.decreaseAllocatableForNode(podAdded.Spec.NodeName, podResources) + + if clusterID := podAdded.Labels[forge.LiqoOriginClusterIDKey]; clusterID != "" { + log.Printf("info: OnPodAdd: Pod %s:%s passed ClusterID check. ClusterID = %s", podAdded.Namespace, podAdded.Name, clusterID) + ph.addPod(clusterID, podResources) + } + ph.notifier.Notify() +} + +// onPodDelete reacts on pod delete. +func (ph *PodsHandler) onPodDelete(obj interface{}) { + // Thanks to the filters at the informer level, delete events are received only when + // pods previously running on a physical node are no longer running. + podDeleted := obj.(*corev1.Pod) + log.Printf("info: OnPodDelete: Delete for pod %s:%s", podDeleted.Namespace, podDeleted.Name) + + podResources := pluginsutils.ExtractPodResources(podDeleted) + ph.nodesHandler.increaseAllocatableForNode(podDeleted.Spec.NodeName, podResources) + + if clusterID := podDeleted.Labels[forge.LiqoOriginClusterIDKey]; clusterID != "" { + log.Printf("info: OnPodDelete: Pod %s:%s passed ClusterID check. ClusterID = %s", podDeleted.Namespace, podDeleted.Name, clusterID) + ph.deletePod(clusterID, podResources) + } + ph.notifier.Notify() +} + +// IsCacheReady returns true if the cache is ready, false otherwise. +func (ph *PodsHandler) IsCacheReady() bool { + return ph.cacheReady +} + +// deletePod delete pod resources from the actual resource count. +func (ph *PodsHandler) deletePod(clusterID string, resources corev1.ResourceList) { + ph.mutex.Lock() + defer ph.mutex.Unlock() + + if entry, exists := ph.pods[clusterID]; exists { + pluginsutils.SubResources(entry, resources) + ph.pods[clusterID] = entry + } +} + +// addPod adds pod resources to the actual resource count. +func (ph *PodsHandler) addPod(clusterID string, resources corev1.ResourceList) { + ph.mutex.Lock() + defer ph.mutex.Unlock() + + if entry, exists := ph.pods[clusterID]; exists { + pluginsutils.AddResources(entry, resources) + ph.pods[clusterID] = entry + } +} + +// deletePodsByClusterID deletes all the pods scheduled from the cluster identified by the given clusterID. +func (ph *PodsHandler) deletePodsByClusterID(clusterID string) { + ph.mutex.Lock() + defer ph.mutex.Unlock() + + delete(ph.pods, clusterID) +} + +// getPodsResourcesByClusterID returns all the resources used by the cluster idetified by the given clusterID. +func (ph *PodsHandler) getPodsResourcesByClusterID(clusterID string) corev1.ResourceList { + if ph.cacheReady { + ph.mutex.RLock() + defer ph.mutex.RUnlock() + + if entry, exists := ph.pods[clusterID]; exists { + return entry + } + } + + return corev1.ResourceList{} +} diff --git a/pkg/per-node-resources/suite_test.go b/pkg/per-node-resources/suite_test.go new file mode 100644 index 0000000..5f90abf --- /dev/null +++ b/pkg/per-node-resources/suite_test.go @@ -0,0 +1,24 @@ +package pernoderesources + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var ( + clientBuilder fake.ClientBuilder +) + +var _ = BeforeSuite(func() { + clientBuilder = *fake. + NewClientBuilder(). + WithObjects() +}) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "PerNodeResourcesPlugin") +} diff --git a/pkg/utils/doc.go b/pkg/utils/doc.go new file mode 100644 index 0000000..f29be23 --- /dev/null +++ b/pkg/utils/doc.go @@ -0,0 +1,16 @@ +// Copyright 2019-2022 The Liqo 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 utils contains utils used around the code +package utils diff --git a/pkg/utils/nodes.go b/pkg/utils/nodes.go new file mode 100644 index 0000000..7f22608 --- /dev/null +++ b/pkg/utils/nodes.go @@ -0,0 +1,27 @@ +package utils + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/liqotech/liqo/pkg/utils" +) + +const ( + controlPlane = "node-role.kubernetes.io/master" + master = "node-role.kubernetes.io/control-plane" +) + +// IsNodeReadyAndSchedulable return true if the given node is ready and schedulable, false otherwise. +func IsNodeReadyAndSchedulable(node *corev1.Node) bool { + return utils.IsNodeReady(node) && isSchedulable(node.Spec.Taints) +} + +func isSchedulable(taints []corev1.Taint) bool { + for _, taint := range taints { + if (taint.Key == controlPlane || taint.Key == master) && taint.Effect == corev1.TaintEffectNoSchedule { + return false + } + } + + return true +} diff --git a/pkg/utils/resources.go b/pkg/utils/resources.go new file mode 100644 index 0000000..d71a0af --- /dev/null +++ b/pkg/utils/resources.go @@ -0,0 +1,57 @@ +package utils + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + resourcehelper "k8s.io/kubectl/pkg/util/resource" +) + +// AddResources is a utility function to add resources. +func AddResources(currentResources, toAdd corev1.ResourceList) { + for resourceName, quantity := range toAdd { + if value, exists := currentResources[resourceName]; exists { + value.Add(quantity) + currentResources[resourceName] = value + } else { + currentResources[resourceName] = quantity + } + } +} + +// SubResources is an utility function to subtract resources. +func SubResources(currentResources, toSub corev1.ResourceList) { + for resourceName, quantity := range toSub { + if value, exists := currentResources[resourceName]; exists { + value.Sub(quantity) + currentResources[resourceName] = value + } + } +} + +// ExtractPodResources get resources of a given pod. +func ExtractPodResources(podToExtract *corev1.Pod) corev1.ResourceList { + resourcesToExtract, _ := resourcehelper.PodRequestsAndLimits(podToExtract) + return resourcesToExtract +} + +// NormalizeResources sets resource to 0 if it is negative. +func NormalizeResources(currentResources corev1.ResourceList) { + for resourceName, value := range currentResources { + if value.Sign() == -1 { + value.Set(0) + currentResources[resourceName] = value + } + } +} + +// MapResources gets a resourceList and returns anoter map having a string key and a pointer to quantity as value type. +func MapResources(resourceList map[corev1.ResourceName]resource.Quantity) map[string]*resource.Quantity { + result := make(map[string]*resource.Quantity, len(resourceList)) + + for entry := range resourceList { + resourceQuantity := resourceList[entry] + result[entry.String()] = &resourceQuantity + } + + return result +} diff --git a/pkg/utils/selectors.go b/pkg/utils/selectors.go new file mode 100644 index 0000000..8b2ad0f --- /dev/null +++ b/pkg/utils/selectors.go @@ -0,0 +1,32 @@ +package utils + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + + "github.com/liqotech/liqo/pkg/consts" +) + +// FilterNodes is used to filter and ignore virtual nodes at informer level. +func FilterNodes(nodeSelector labels.Selector, includeVirtualNodes bool) func(*metav1.ListOptions) { + return func(options *metav1.ListOptions) { + if !includeVirtualNodes { + req, err := labels.NewRequirement(consts.TypeLabel, selection.NotEquals, []string{consts.TypeNode}) + utilruntime.Must(err) + nodeSelector = nodeSelector.Add(*req) + } + options.LabelSelector = nodeSelector.String() + } +} + +// FilterPods creates a filter for a given node name. +func FilterPods(nodeName string) func(*metav1.ListOptions) { + return func(options *metav1.ListOptions) { + options.FieldSelector = fields.AndSelectors(fields.OneTermEqualSelector("spec.nodeName", nodeName), + fields.OneTermEqualSelector("status.phase", string(corev1.PodRunning))).String() + } +}