diff --git a/cmd/controller-manager/app/controllers.go b/cmd/controller-manager/app/controllers.go index 67d009e76..9b37b8cce 100644 --- a/cmd/controller-manager/app/controllers.go +++ b/cmd/controller-manager/app/controllers.go @@ -194,7 +194,8 @@ func addControllers( client.KubeSphere(), kubesphereInformer.Iam().V1alpha2().LoginRecords(), kubesphereInformer.Iam().V1alpha2().Users(), - authenticationOptions.LoginHistoryRetentionPeriod) + authenticationOptions.LoginHistoryRetentionPeriod, + authenticationOptions.LoginHistoryMaximumEntries) csrController := certificatesigningrequest.NewController(client.Kubernetes(), kubernetesInformer.Certificates().V1beta1().CertificateSigningRequests(), diff --git a/go.mod b/go.mod index eb1afff07..3cd12bee4 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,6 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/json-iterator/go v1.1.10 github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/kubernetes-csi/external-snapshotter/client/v3 v3.0.0 github.com/kubesphere/sonargo v0.0.2 github.com/mitchellh/mapstructure v1.2.2 @@ -71,14 +70,12 @@ require ( github.com/spf13/viper v1.4.0 github.com/stretchr/testify v1.6.1 github.com/xanzy/ssh-agent v0.2.1 // indirect - golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 + golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de golang.org/x/net v0.0.0-20200707034311-ab3426394381 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d - golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect google.golang.org/grpc v1.30.0 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/cas.v2 v2.2.0 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/square/go-jose.v2 v2.4.0 gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect gopkg.in/src-d/go-git.v4 v4.11.0 @@ -193,7 +190,6 @@ replace ( github.com/brancz/kube-rbac-proxy => github.com/brancz/kube-rbac-proxy v0.5.0 github.com/bshuster-repo/logrus-logstash-hook => github.com/bshuster-repo/logrus-logstash-hook v0.4.1 github.com/bugsnag/bugsnag-go => github.com/bugsnag/bugsnag-go v1.5.0 - github.com/bugsnag/osext => github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b github.com/bugsnag/panicwrap => github.com/bugsnag/panicwrap v1.2.0 github.com/c-bata/go-prompt => github.com/c-bata/go-prompt v0.2.2 github.com/campoy/embedmd => github.com/campoy/embedmd v1.0.0 @@ -212,7 +208,6 @@ replace ( github.com/circonus-labs/circonus-gometrics => github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible github.com/circonus-labs/circonusllhist => github.com/circonus-labs/circonusllhist v0.1.3 github.com/clbanning/x2j => github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec - github.com/cloudflare/cfssl => github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 github.com/cockroachdb/apd => github.com/cockroachdb/apd v1.1.0 github.com/cockroachdb/cockroach-go => github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c github.com/cockroachdb/datadriven => github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa @@ -220,9 +215,6 @@ replace ( github.com/container-storage-interface/spec => github.com/container-storage-interface/spec v1.2.0 github.com/containerd/containerd => github.com/containerd/containerd v1.3.0 github.com/containerd/continuity => github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 - github.com/containerd/fifo => github.com/containerd/fifo v0.0.0-20210129194248-f8e8fdba47ef - github.com/containerd/ttrpc => github.com/containerd/ttrpc v1.0.2 - github.com/containerd/typeurl => github.com/containerd/typeurl v1.0.1 github.com/containernetworking/cni => github.com/containernetworking/cni v0.8.0 github.com/coreos/bbolt => github.com/coreos/bbolt v1.3.3 github.com/coreos/etcd => github.com/coreos/etcd v3.3.17+incompatible @@ -256,21 +248,16 @@ replace ( github.com/dhui/dktest => github.com/dhui/dktest v0.3.0 github.com/disintegration/imaging => github.com/disintegration/imaging v1.6.1 github.com/docker/cli => github.com/docker/cli v0.0.0-20190506213505-d88565df0c2d - github.com/docker/compose-on-kubernetes => github.com/docker/compose-on-kubernetes v0.4.24 github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 github.com/docker/docker-credential-helpers => github.com/docker/docker-credential-helpers v0.6.1 - github.com/docker/go => github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c github.com/docker/go-connections => github.com/docker/go-connections v0.4.0 - github.com/docker/go-events => github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c github.com/docker/go-metrics => github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 github.com/docker/go-units => github.com/docker/go-units v0.4.0 github.com/docker/libtrust => github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 github.com/docker/spdystream => github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c - github.com/docker/swarmkit => github.com/docker/swarmkit v1.12.0 github.com/docopt/docopt-go => github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 github.com/dustin/go-humanize => github.com/dustin/go-humanize v1.0.0 - github.com/dvsekhvalnov/jose2go => github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae github.com/eapache/go-resiliency => github.com/eapache/go-resiliency v1.1.0 github.com/eapache/go-xerial-snappy => github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 github.com/eapache/queue => github.com/eapache/queue v1.1.0 @@ -374,7 +361,6 @@ replace ( github.com/gomodule/redigo => github.com/gomodule/redigo v2.0.0+incompatible github.com/google/addlicense => github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76 github.com/google/btree => github.com/google/btree v1.0.0 - github.com/google/certificate-transparency-go => github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 github.com/google/flatbuffers => github.com/google/flatbuffers v1.11.0 github.com/google/go-cmp => github.com/google/go-cmp v0.4.0 github.com/google/go-github => github.com/google/go-github v17.0.0+incompatible @@ -384,7 +370,6 @@ replace ( github.com/google/martian => github.com/google/martian v2.1.0+incompatible github.com/google/pprof => github.com/google/pprof v0.0.0-20200417002340-c6e0a841f49a github.com/google/renameio => github.com/google/renameio v0.1.0 - github.com/google/shlex => github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid => github.com/google/uuid v1.1.1 github.com/googleapis/gax-go => github.com/googleapis/gax-go v2.0.2+incompatible github.com/googleapis/gax-go/v2 => github.com/googleapis/gax-go/v2 v2.0.5 @@ -453,7 +438,6 @@ replace ( github.com/jstemmer/go-junit-report => github.com/jstemmer/go-junit-report v0.9.1 github.com/jsternberg/zap-logfmt => github.com/jsternberg/zap-logfmt v1.0.0 github.com/jtolds/gls => github.com/jtolds/gls v4.20.0+incompatible - github.com/juju/loggo => github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 github.com/julienschmidt/httprouter => github.com/julienschmidt/httprouter v1.3.0 github.com/jung-kurt/gofpdf => github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 github.com/jwilder/encoding => github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef @@ -505,7 +489,6 @@ replace ( github.com/mdlayher/wifi => github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee github.com/mgutz/ansi => github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/miekg/dns => github.com/miekg/dns v1.1.29 - github.com/miekg/pkcs11 => github.com/miekg/pkcs11 v1.0.2 github.com/minio/md5-simd => github.com/minio/md5-simd v1.1.0 github.com/minio/minio-go/v7 => github.com/minio/minio-go/v7 v7.0.2 github.com/minio/sha256-simd => github.com/minio/sha256-simd v0.1.1 @@ -535,7 +518,6 @@ replace ( github.com/nats-io/nkeys => github.com/nats-io/nkeys v0.1.3 github.com/nats-io/nuid => github.com/nats-io/nuid v1.0.1 github.com/ncw/swift => github.com/ncw/swift v1.0.50 - github.com/niemeyer/pretty => github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e github.com/nxadm/tail => github.com/nxadm/tail v1.4.4 github.com/oklog/oklog => github.com/oklog/oklog v0.3.2 github.com/oklog/run => github.com/oklog/run v1.1.0 @@ -548,7 +530,6 @@ replace ( github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/runc => github.com/opencontainers/runc v0.1.1 - github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.2 github.com/opentracing-contrib/go-grpc => github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing-contrib/go-observer => github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 github.com/opentracing-contrib/go-stdlib => github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9 @@ -634,9 +615,7 @@ replace ( github.com/streadway/handy => github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a github.com/stretchr/objx => github.com/stretchr/objx v0.2.0 github.com/stretchr/testify => github.com/stretchr/testify v1.4.0 - github.com/syndtr/gocapability => github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/thanos-io/thanos => github.com/thanos-io/thanos v0.13.1-0.20200910143741-e0b7f7b32e9c - github.com/theupdateframework/notary => github.com/theupdateframework/notary v0.7.0 github.com/tidwall/pretty => github.com/tidwall/pretty v1.0.0 github.com/tinylib/msgp => github.com/tinylib/msgp v1.1.0 github.com/tmc/grpc-websocket-proxy => github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 @@ -690,7 +669,6 @@ replace ( golang.org/x/oauth2 => golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a golang.org/x/sync => golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/sys => golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e - golang.org/x/term => golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 golang.org/x/text => golang.org/x/text v0.3.0 golang.org/x/time => golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/tools => golang.org/x/tools v0.0.0-20190710153321-831012c29e42 @@ -709,7 +687,6 @@ replace ( gopkg.in/alexcesaro/quotedprintable.v3 => gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc gopkg.in/asn1-ber.v1 => gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d gopkg.in/cas.v2 => gopkg.in/cas.v2 v2.2.0 - gopkg.in/cenkalti/backoff.v2 => gopkg.in/cenkalti/backoff.v2 v2.2.1 gopkg.in/check.v1 => gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 gopkg.in/cheggaaa/pb.v1 => gopkg.in/cheggaaa/pb.v1 v1.0.25 gopkg.in/errgo.v2 => gopkg.in/errgo.v2 v2.1.0 @@ -725,7 +702,6 @@ replace ( gopkg.in/ini.v1 => gopkg.in/ini.v1 v1.57.0 gopkg.in/mail.v2 => gopkg.in/mail.v2 v2.3.1 gopkg.in/natefinch/lumberjack.v2 => gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/rethinkdb/rethinkdb-go.v6 => gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 gopkg.in/square/go-jose.v1 => gopkg.in/square/go-jose.v1 v1.1.2 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.4.0 gopkg.in/src-d/go-billy.v4 => gopkg.in/src-d/go-billy.v4 v4.3.0 diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 5f4aaf33c..6319d8140 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -240,10 +240,11 @@ func (s *APIServer) installKubeSphereAPIs() { s.KubernetesClient.KubeSphere(), s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister(), s.Config.AuthenticationOptions), - auth.NewOAuth2Authenticator(s.KubernetesClient.KubeSphere(), - s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister(), + auth.NewOAuthAuthenticator(s.KubernetesClient.KubeSphere(), + s.InformerFactory.KubeSphereSharedInformerFactory(), s.Config.AuthenticationOptions), - auth.NewLoginRecorder(s.KubernetesClient.KubeSphere()), + auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(), + s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister()), s.Config.AuthenticationOptions)) urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container)) urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost)) @@ -340,7 +341,8 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) { handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher) } - loginRecorder := auth.NewLoginRecorder(s.KubernetesClient.KubeSphere()) + loginRecorder := auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(), + s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister()) // authenticators are unordered authn := unionauth.New(anonymous.NewAuthenticator(), basictoken.New(basic.NewBasicAuthenticator(auth.NewPasswordAuthenticator(s.KubernetesClient.KubeSphere(), diff --git a/pkg/apiserver/authentication/identityprovider/ldap/ldap.go b/pkg/apiserver/authentication/identityprovider/ldap/ldap.go index 2bd5df527..f98c80157 100644 --- a/pkg/apiserver/authentication/identityprovider/ldap/ldap.go +++ b/pkg/apiserver/authentication/identityprovider/ldap/ldap.go @@ -126,7 +126,10 @@ func (l ldapProvider) Authenticate(username string, password string) (identitypr return nil, err } - filter := fmt.Sprintf("(&(%s=%s)%s)", l.LoginAttribute, username, l.UserSearchFilter) + filter := fmt.Sprintf("(%s=%s)", l.LoginAttribute, ldap.EscapeFilter(username)) + if l.UserSearchFilter != "" { + filter = fmt.Sprintf("(&%s%s)", filter, l.UserSearchFilter) + } result, err := conn.Search(&ldap.SearchRequest{ BaseDN: l.UserSearchBase, Scope: ldap.ScopeWholeSubtree, diff --git a/pkg/apiserver/authentication/options/authenticate_options.go b/pkg/apiserver/authentication/options/authenticate_options.go index c6d8bf674..e5381b4da 100644 --- a/pkg/apiserver/authentication/options/authenticate_options.go +++ b/pkg/apiserver/authentication/options/authenticate_options.go @@ -17,7 +17,7 @@ limitations under the License. package options import ( - "fmt" + "errors" "github.com/spf13/pflag" "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider" _ "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider/aliyunidaas" @@ -42,6 +42,9 @@ type AuthenticationOptions struct { MaximumClockSkew time.Duration `json:"maximumClockSkew" yaml:"maximumClockSkew"` // retention login history, records beyond this amount will be deleted LoginHistoryRetentionPeriod time.Duration `json:"loginHistoryRetentionPeriod" yaml:"loginHistoryRetentionPeriod"` + // retention login history, records beyond this amount will be deleted + // LoginHistoryMaximumEntries restricts for all kubesphere accounts and must be greater than AuthenticateRateLimiterMaxTries + LoginHistoryMaximumEntries int `json:"loginHistoryMaximumEntries" yaml:"loginHistoryMaximumEntries"` // allow multiple users login from different location at the same time MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"` // secret to sign jwt token @@ -58,6 +61,7 @@ func NewAuthenticateOptions() *AuthenticationOptions { AuthenticateRateLimiterDuration: time.Minute * 30, MaximumClockSkew: 10 * time.Second, LoginHistoryRetentionPeriod: time.Hour * 24 * 7, + LoginHistoryMaximumEntries: 100, OAuthOptions: oauth.NewOptions(), MultipleLogin: false, JwtSecret: "", @@ -68,7 +72,10 @@ func NewAuthenticateOptions() *AuthenticationOptions { func (options *AuthenticationOptions) Validate() []error { var errs []error if len(options.JwtSecret) == 0 { - errs = append(errs, fmt.Errorf("jwt secret is empty")) + errs = append(errs, errors.New("JWT secret MUST not be empty")) + } + if options.AuthenticateRateLimiterMaxTries > options.LoginHistoryMaximumEntries { + errs = append(errs, errors.New("authenticateRateLimiterMaxTries MUST not be greater than loginHistoryMaximumEntries")) } if err := identityprovider.SetupWithOptions(options.OAuthOptions.IdentityProviders); err != nil { errs = append(errs, err) @@ -82,6 +89,7 @@ func (options *AuthenticationOptions) AddFlags(fs *pflag.FlagSet, s *Authenticat fs.BoolVar(&options.MultipleLogin, "multiple-login", s.MultipleLogin, "Allow multiple login with the same account, disable means only one user can login at the same time.") fs.StringVar(&options.JwtSecret, "jwt-secret", s.JwtSecret, "Secret to sign jwt token, must not be empty.") fs.DurationVar(&options.LoginHistoryRetentionPeriod, "login-history-retention-period", s.LoginHistoryRetentionPeriod, "login-history-retention-period defines how long login history should be kept.") + fs.IntVar(&options.LoginHistoryMaximumEntries, "login-history-maximum-entries", s.LoginHistoryMaximumEntries, "login-history-maximum-entries defines how many entries of login history should be kept.") fs.DurationVar(&options.OAuthOptions.AccessTokenMaxAge, "access-token-max-age", s.OAuthOptions.AccessTokenMaxAge, "access-token-max-age control the lifetime of access tokens, 0 means no expiration.") fs.StringVar(&s.KubectlImage, "kubectl-image", s.KubectlImage, "Setup the image used by kubectl terminal pod") fs.DurationVar(&options.MaximumClockSkew, "maximum-clock-skew", s.MaximumClockSkew, "The maximum time difference between the system clocks of the ks-apiserver that issued a JWT and the ks-apiserver that verified the JWT.") diff --git a/pkg/controller/loginrecord/loginrecord_controller.go b/pkg/controller/loginrecord/loginrecord_controller.go index aa73e5bc7..4a441a861 100644 --- a/pkg/controller/loginrecord/loginrecord_controller.go +++ b/pkg/controller/loginrecord/loginrecord_controller.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" @@ -35,6 +36,7 @@ import ( iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2" iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" "kubesphere.io/kubesphere/pkg/controller/utils/controller" + "sort" "time" ) @@ -55,6 +57,7 @@ type loginRecordController struct { userLister iamv1alpha2listers.UserLister userSynced cache.InformerSynced loginHistoryRetentionPeriod time.Duration + loginHistoryMaximumEntries int // recorder is an event recorder for recording Event resources to the // Kubernetes API. recorder record.EventRecorder @@ -64,7 +67,8 @@ func NewLoginRecordController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, loginRecordInformer iamv1alpha2informers.LoginRecordInformer, userInformer iamv1alpha2informers.UserInformer, - loginHistoryRetentionPeriod time.Duration) *loginRecordController { + loginHistoryRetentionPeriod time.Duration, + loginHistoryMaximumEntries int) *loginRecordController { klog.V(4).Info("Creating event broadcaster") eventBroadcaster := record.NewBroadcaster() @@ -82,6 +86,7 @@ func NewLoginRecordController(k8sClient kubernetes.Interface, loginRecordLister: loginRecordInformer.Lister(), userLister: userInformer.Lister(), loginHistoryRetentionPeriod: loginHistoryRetentionPeriod, + loginHistoryMaximumEntries: loginHistoryMaximumEntries, recorder: recorder, } ctl.Handler = ctl.reconcile @@ -117,7 +122,20 @@ func (c *loginRecordController) reconcile(key string) error { return nil } - if err = c.updateUserLastLoginTime(loginRecord); err != nil { + user, err := c.userForLoginRecord(loginRecord) + if err != nil { + // delete orphan object + if errors.IsNotFound(err) { + return c.ksClient.IamV1alpha2().LoginRecords().Delete(context.TODO(), loginRecord.Name, metav1.DeleteOptions{}) + } + return err + } + + if err = c.updateUserLastLoginTime(user, loginRecord); err != nil { + return err + } + + if err = c.shrinkEntriesFor(user); err != nil { return err } @@ -136,28 +154,44 @@ func (c *loginRecordController) reconcile(key string) error { } // updateUserLastLoginTime accepts a login object and set user lastLoginTime field -func (c *loginRecordController) updateUserLastLoginTime(loginRecord *iamv1alpha2.LoginRecord) error { - username, ok := loginRecord.Labels[iamv1alpha2.UserReferenceLabel] - if !ok || len(username) == 0 { - klog.V(4).Info("login doesn't belong to any user") - return nil - } - user, err := c.userLister.Get(username) - if err != nil { - // ignore not found error - if errors.IsNotFound(err) { - klog.V(4).Infof("user %s doesn't exist any more, login record will be deleted later", username) - return nil - } - klog.Error(err) - return err - } +func (c *loginRecordController) updateUserLastLoginTime(user *iamv1alpha2.User, loginRecord *iamv1alpha2.LoginRecord) error { // update lastLoginTime if user.DeletionTimestamp.IsZero() && (user.Status.LastLoginTime == nil || user.Status.LastLoginTime.Before(&loginRecord.CreationTimestamp)) { user.Status.LastLoginTime = &loginRecord.CreationTimestamp - user, err = c.ksClient.IamV1alpha2().Users().UpdateStatus(context.Background(), user, metav1.UpdateOptions{}) + _, err := c.ksClient.IamV1alpha2().Users().UpdateStatus(context.Background(), user, metav1.UpdateOptions{}) return err } return nil } + +// shrinkEntriesFor will delete old entries out of limit +func (c *loginRecordController) shrinkEntriesFor(user *iamv1alpha2.User) error { + loginRecords, err := c.loginRecordLister.List(labels.SelectorFromSet(labels.Set{iamv1alpha2.UserReferenceLabel: user.Name})) + if err != nil { + return err + } + if len(loginRecords) <= c.loginHistoryMaximumEntries { + return nil + } + sort.Slice(loginRecords, func(i, j int) bool { + return loginRecords[j].CreationTimestamp.After(loginRecords[i].CreationTimestamp.Time) + }) + oldEntries := loginRecords[:len(loginRecords)-c.loginHistoryMaximumEntries] + for _, r := range oldEntries { + err = c.ksClient.IamV1alpha2().LoginRecords().Delete(context.TODO(), r.Name, metav1.DeleteOptions{}) + if err != nil { + return err + } + } + return nil +} + +func (c *loginRecordController) userForLoginRecord(loginRecord *iamv1alpha2.LoginRecord) (*iamv1alpha2.User, error) { + username, ok := loginRecord.Labels[iamv1alpha2.UserReferenceLabel] + if !ok || len(username) == 0 { + klog.V(4).Info("login doesn't belong to any user") + return nil, errors.NewNotFound(iamv1alpha2.Resource(iamv1alpha2.ResourcesSingularUser), username) + } + return c.userLister.Get(username) +} diff --git a/pkg/controller/loginrecord/loginrecord_controller_test.go b/pkg/controller/loginrecord/loginrecord_controller_test.go index 5f2c65197..74a7d3711 100644 --- a/pkg/controller/loginrecord/loginrecord_controller_test.go +++ b/pkg/controller/loginrecord/loginrecord_controller_test.go @@ -17,257 +17,167 @@ limitations under the License. package loginrecord import ( + "context" "fmt" - "reflect" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/klog/klogr" + "kubesphere.io/kubesphere/pkg/apis" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "os" + "path/filepath" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + logf "sigs.k8s.io/controller-runtime/pkg/log" "testing" "time" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/diff" - kubeinformers "k8s.io/client-go/informers" - k8sfake "k8s.io/client-go/kubernetes/fake" - core "k8s.io/client-go/testing" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" - "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" - ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -var ( - alwaysReady = func() bool { return true } - noResyncPeriodFunc = func() time.Duration { return 0 } -) +var testEnv *envtest.Environment +var k8sManager ctrl.Manager -type fixture struct { - t *testing.T - - ksclient *fake.Clientset - k8sclient *k8sfake.Clientset - // Objects to put in the store. - user *iamv1alpha2.User - loginRecord *iamv1alpha2.LoginRecord - // Actions expected to happen on the client. - kubeactions []core.Action - actions []core.Action - // Objects from here preloaded into NewSimpleFake. - kubeobjects []runtime.Object - objects []runtime.Object +func TestLoginRecordController(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecsWithDefaultAndCustomReporters(t, + "LoginRecord Controller Test Suite", + []Reporter{printer.NewlineReporter{}}) } -func newFixture(t *testing.T) *fixture { - f := &fixture{} - f.t = t - f.objects = []runtime.Object{} - f.kubeobjects = []runtime.Object{} - return f -} +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(klogr.New()) -func newUser(name string) *iamv1alpha2.User { - return &iamv1alpha2.User{ - TypeMeta: metav1.TypeMeta{APIVersion: iamv1alpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: iamv1alpha2.UserSpec{ - Email: fmt.Sprintf("%s@kubesphere.io", name), - Lang: "zh-CN", - Description: "fake user", - }, - } -} - -func newLoginRecord(username string) *iamv1alpha2.LoginRecord { - return &iamv1alpha2.LoginRecord{ - TypeMeta: metav1.TypeMeta{APIVersion: iamv1alpha2.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: username, - CreationTimestamp: metav1.Now(), - Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username}, - }, - Spec: iamv1alpha2.LoginRecordSpec{ - Type: iamv1alpha2.Token, - Success: true, - Reason: "", - }, - } -} - -func (f *fixture) newController() (*loginRecordController, ksinformers.SharedInformerFactory, kubeinformers.SharedInformerFactory) { - f.ksclient = fake.NewSimpleClientset(f.objects...) - f.k8sclient = k8sfake.NewSimpleClientset(f.kubeobjects...) - - ksInformers := ksinformers.NewSharedInformerFactory(f.ksclient, noResyncPeriodFunc()) - k8sInformers := kubeinformers.NewSharedInformerFactory(f.k8sclient, noResyncPeriodFunc()) - if err := ksInformers.Iam().V1alpha2().Users().Informer().GetIndexer().Add(f.user); err != nil { - f.t.Errorf("add user:%s", err) - } - if err := ksInformers.Iam().V1alpha2().LoginRecords().Informer().GetIndexer().Add(f.loginRecord); err != nil { - f.t.Errorf("add login record:%s", err) - } - - c := NewLoginRecordController(f.k8sclient, f.ksclient, - ksInformers.Iam().V1alpha2().LoginRecords(), - ksInformers.Iam().V1alpha2().Users(), - time.Minute*5) - c.userSynced = alwaysReady - c.loginRecordSynced = alwaysReady - c.recorder = &record.FakeRecorder{} - - return c, ksInformers, k8sInformers -} - -func (f *fixture) run(userName string) { - f.runController(userName, true, false) -} - -func (f *fixture) runExpectError(userName string) { - f.runController(userName, true, true) -} - -func (f *fixture) runController(user string, startInformers bool, expectError bool) { - c, i, k8sI := f.newController() - if startInformers { - stopCh := make(chan struct{}) - defer close(stopCh) - i.Start(stopCh) - k8sI.Start(stopCh) - } - - err := c.reconcile(user) - if !expectError && err != nil { - f.t.Errorf("error syncing user: %v", err) - } else if expectError && err == nil { - f.t.Error("expected error syncing user, got nil") - } - - actions := filterInformerActions(f.ksclient.Actions()) - for j, action := range actions { - if len(f.actions) < j+1 { - f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[j:]) - break + By("bootstrapping test environment") + t := true + if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { + testEnv = &envtest.Environment{ + UseExistingCluster: &t, } - - expectedAction := f.actions[j] - checkAction(expectedAction, action, f.t) - } - - if len(f.actions) > len(actions) { - f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) - } - - k8sActions := filterInformerActions(f.k8sclient.Actions()) - for k, action := range k8sActions { - if len(f.kubeactions) < k+1 { - f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[k:]) - break + } else { + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")}, + AttachControlPlaneOutput: false, } - - expectedAction := f.kubeactions[k] - checkAction(expectedAction, action, f.t) } - if len(f.kubeactions) > len(k8sActions) { - f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):]) - } -} + cfg, err := testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) -// checkAction verifies that expected and actual actions are equal and both have -// same attached resources -func checkAction(expected, actual core.Action, t *testing.T) { - if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { - t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) - //return - // TODO : failed sometimes, need to be verified by hongming - } + err = apis.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) - if reflect.TypeOf(actual) != reflect.TypeOf(expected) { - t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) - return - } + k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + MetricsBindAddress: "0", + }) + Expect(err).ToNot(HaveOccurred()) - switch a := actual.(type) { - case core.CreateActionImpl: - e, _ := expected.(core.CreateActionImpl) - expObject := e.GetObject() - object := a.GetObject() - if !reflect.DeepEqual(expObject, object) { - t.Errorf("Action %s %s has wrong object\nDiff:\n %s", - a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + k8sClient, err := kubernetes.NewForConfig(cfg) + Expect(err).NotTo(HaveOccurred()) + + ksClient, err := kubesphere.NewForConfig(cfg) + Expect(err).NotTo(HaveOccurred()) + + ksInformers := externalversions.NewSharedInformerFactory(ksClient, time.Second*30) + Expect(err).NotTo(HaveOccurred()) + + loginRecordInformer := ksInformers.Iam().V1alpha2().LoginRecords() + userInformer := ksInformers.Iam().V1alpha2().Users() + + loginRecordController := NewLoginRecordController(k8sClient, ksClient, loginRecordInformer, userInformer, time.Hour, 1) + err = k8sManager.Add(loginRecordController) + Expect(err).NotTo(HaveOccurred()) + + go func() { + stopChan := ctrl.SetupSignalHandler() + ksInformers.Start(stopChan) + err = k8sManager.Start(stopChan) + Expect(err).ToNot(HaveOccurred()) + }() + + close(done) +}, 60) + +var _ = Describe("LoginRecord", func() { + const timeout = time.Second * 30 + const interval = time.Second * 1 + + BeforeEach(func() { + admin := &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{Name: "admin"}, } - case core.UpdateActionImpl: - e, _ := expected.(core.UpdateActionImpl) - expObject := e.GetObject() - object := a.GetObject() - expUser := expObject.(*iamv1alpha2.User) - user := object.(*iamv1alpha2.User) - expUser.Status.LastTransitionTime = nil - user.Status.LastTransitionTime = nil - if !reflect.DeepEqual(expUser, user) { - t.Errorf("Action %s %s has wrong object\nDiff:\n %s", - a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) - } - case core.PatchActionImpl: - e, _ := expected.(core.PatchActionImpl) - expPatch := e.GetPatch() - patch := a.GetPatch() - if !reflect.DeepEqual(expPatch, patch) { - t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", - a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) - } - default: - t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", - actual.GetVerb(), actual.GetResource().Resource) - } -} + Expect(k8sManager.GetClient().Create(context.Background(), admin, &client.CreateOptions{})).Should(Succeed()) + }) -// filterInformerActions filters list and watch actions for testing resources. -// Since list and watch don't change resource state we can filter it to lower -// nose level in our tests. -func filterInformerActions(actions []core.Action) []core.Action { - var ret []core.Action - for _, action := range actions { - if len(action.GetNamespace()) == 0 && - (action.Matches("list", "users") || - action.Matches("watch", "users") || - action.Matches("get", "users")) { - continue - } - ret = append(ret, action) - } + // Add Tests for OpenAPI validation (or additonal CRD features) specified in + // your API definition. + // Avoid adding tests for vanilla CRUD operations because they would + // test Kubernetes API server, which isn't the goal here. + Context("LoginRecord Controller", func() { + It("Should create successfully", func() { + ctx := context.Background() + username := "admin" + loginRecord := &iamv1alpha2.LoginRecord{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-1", username), + Labels: map[string]string{ + iamv1alpha2.UserReferenceLabel: username, + }, + }, + Spec: iamv1alpha2.LoginRecordSpec{ + Type: iamv1alpha2.Token, + Provider: "", + Success: true, + Reason: iamv1alpha2.AuthenticatedSuccessfully, + SourceIP: "", + UserAgent: "", + }, + } - return ret -} + By("Expecting to create login record successfully") + Expect(k8sManager.GetClient().Create(ctx, loginRecord, &client.CreateOptions{})).Should(Succeed()) -func (f *fixture) expectUpdateUserStatusAction(user *iamv1alpha2.User) { - expect := user.DeepCopy() - action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "users"}, "", expect) - action.Subresource = "status" - expect.Status.LastLoginTime = &f.loginRecord.CreationTimestamp - f.actions = append(f.actions, action) -} + expected := &iamv1alpha2.LoginRecord{} + Eventually(func() bool { + err := k8sManager.GetClient().Get(ctx, types.NamespacedName{Name: loginRecord.Name}, expected) + fmt.Print(err) + return !expected.CreationTimestamp.IsZero() + }, timeout, interval).Should(BeTrue()) -func getKey(user *iamv1alpha2.User, t *testing.T) string { - key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(user) - if err != nil { - t.Errorf("Unexpected error getting key for user %v: %v", user.Name, err) - return "" - } - return key -} + loginRecord.Name = fmt.Sprintf("%s-2", username) + loginRecord.ResourceVersion = "" + By("Expecting to create login record successfully") + Expect(k8sManager.GetClient().Create(ctx, loginRecord, &client.CreateOptions{})).Should(Succeed()) -func TestDoNothing(t *testing.T) { - f := newFixture(t) - user := newUser("test") - loginRecord := newLoginRecord("test") + Eventually(func() bool { + k8sManager.GetClient().Get(ctx, types.NamespacedName{Name: loginRecord.Name}, expected) + return !expected.CreationTimestamp.IsZero() + }, timeout, interval).Should(BeTrue()) - f.user = user - f.loginRecord = loginRecord - f.objects = append(f.objects, user, loginRecord) + By("Expecting to limit login record successfully") + Eventually(func() bool { + loginRecordList := &iamv1alpha2.LoginRecordList{} + selector := labels.SelectorFromSet(labels.Set{iamv1alpha2.UserReferenceLabel: username}) + k8sManager.GetClient().List(ctx, loginRecordList, &client.ListOptions{LabelSelector: selector}) + return len(loginRecordList.Items) == 1 + }, timeout, interval).Should(BeTrue()) + }) + }) +}) - f.expectUpdateUserStatusAction(user) - f.run(getKey(user, t)) -} +var _ = AfterSuite(func() { + By("tearing down the test environment") + gexec.KillAndWait(5 * time.Second) + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) diff --git a/pkg/controller/serviceaccount/serviceaccount_controller_suite_test.go b/pkg/controller/serviceaccount/serviceaccount_controller_suite_test.go index f394cc4b3..174a96d7a 100644 --- a/pkg/controller/serviceaccount/serviceaccount_controller_suite_test.go +++ b/pkg/controller/serviceaccount/serviceaccount_controller_suite_test.go @@ -43,7 +43,7 @@ var k8sClient client.Client var k8sManager ctrl.Manager var testEnv *envtest.Environment -func TestMain(t *testing.T) { +func TestServiceAccountController(t *testing.T) { RegisterFailHandler(Fail) RunSpecsWithDefaultAndCustomReporters(t, "ServiceAccount Controller Test Suite", diff --git a/pkg/kapis/oauth/handler.go b/pkg/kapis/oauth/handler.go index 84c2dbfa7..27b5e62f0 100644 --- a/pkg/kapis/oauth/handler.go +++ b/pkg/kapis/oauth/handler.go @@ -72,14 +72,14 @@ type handler struct { options *authoptions.AuthenticationOptions tokenOperator auth.TokenManagementInterface passwordAuthenticator auth.PasswordAuthenticator - oauth2Authenticator auth.OAuth2Authenticator + oauth2Authenticator auth.OAuthAuthenticator loginRecorder auth.LoginRecorder } func newHandler(im im.IdentityManagementInterface, tokenOperator auth.TokenManagementInterface, passwordAuthenticator auth.PasswordAuthenticator, - oauth2Authenticator auth.OAuth2Authenticator, + oauth2Authenticator auth.OAuthAuthenticator, loginRecorder auth.LoginRecorder, options *authoptions.AuthenticationOptions) *handler { return &handler{im: im, diff --git a/pkg/kapis/oauth/register.go b/pkg/kapis/oauth/register.go index 9b55fc606..5447895f3 100644 --- a/pkg/kapis/oauth/register.go +++ b/pkg/kapis/oauth/register.go @@ -37,7 +37,7 @@ import ( func AddToContainer(c *restful.Container, im im.IdentityManagementInterface, tokenOperator auth.TokenManagementInterface, passwordAuthenticator auth.PasswordAuthenticator, - oauth2Authenticator auth.OAuth2Authenticator, + oauth2Authenticator auth.OAuthAuthenticator, loginRecorder auth.LoginRecorder, options *authoptions.AuthenticationOptions) error { diff --git a/pkg/models/auth/authenticator.go b/pkg/models/auth/authenticator.go index 820e395bc..93aa02928 100644 --- a/pkg/models/auth/authenticator.go +++ b/pkg/models/auth/authenticator.go @@ -22,6 +22,7 @@ import ( "fmt" "golang.org/x/crypto/bcrypt" "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/constants" "net/mail" @@ -46,7 +47,7 @@ type PasswordAuthenticator interface { Authenticate(username, password string) (authuser.Info, string, error) } -type OAuth2Authenticator interface { +type OAuthAuthenticator interface { Authenticate(provider, code string) (authuser.Info, string, error) } @@ -77,12 +78,12 @@ func NewPasswordAuthenticator(ksClient kubesphere.Interface, return passwordAuthenticator } -func NewOAuth2Authenticator(ksClient kubesphere.Interface, - userLister iamv1alpha2listers.UserLister, - options *authoptions.AuthenticationOptions) OAuth2Authenticator { +func NewOAuthAuthenticator(ksClient kubesphere.Interface, + ksInformer informers.SharedInformerFactory, + options *authoptions.AuthenticationOptions) OAuthAuthenticator { oauth2Authenticator := &oauth2Authenticator{ ksClient: ksClient, - userGetter: &userGetter{userLister: userLister}, + userGetter: &userGetter{userLister: ksInformer.Iam().V1alpha2().Users().Lister()}, authOptions: options, } return oauth2Authenticator diff --git a/pkg/models/auth/login_recoder.go b/pkg/models/auth/login_recoder.go index da0a8f975..60b9fe4f0 100644 --- a/pkg/models/auth/login_recoder.go +++ b/pkg/models/auth/login_recoder.go @@ -19,11 +19,12 @@ package auth import ( "context" "fmt" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - "strings" + iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" ) type LoginRecorder interface { @@ -31,25 +32,34 @@ type LoginRecorder interface { } type loginRecorder struct { - ksClient kubesphere.Interface + ksClient kubesphere.Interface + userGetter *userGetter } -func NewLoginRecorder(ksClient kubesphere.Interface) LoginRecorder { +func NewLoginRecorder(ksClient kubesphere.Interface, userLister iamv1alpha2listers.UserLister) LoginRecorder { return &loginRecorder{ - ksClient: ksClient, + ksClient: ksClient, + userGetter: &userGetter{userLister: userLister}, } } -func (l *loginRecorder) RecordLogin(username string, loginType iamv1alpha2.LoginType, provider string, sourceIP string, userAgent string, authErr error) error { - // This is a temporary solution in case of user login with email, - // '@' is not allowed in Kubernetes object name. - username = strings.Replace(username, "@", "-", -1) - +// RecordLogin Create v1alpha2.LoginRecord for existing accounts +func (l *loginRecorder) RecordLogin(username string, loginType iamv1alpha2.LoginType, provider, sourceIP, userAgent string, authErr error) error { + // only for existing accounts, solve the problem of huge entries + user, err := l.userGetter.findUser(username) + if err != nil { + // ignore not found error + if errors.IsNotFound(err) { + return nil + } + klog.Error(err) + return err + } loginEntry := &iamv1alpha2.LoginRecord{ ObjectMeta: metav1.ObjectMeta{ - GenerateName: fmt.Sprintf("%s-", username), + GenerateName: fmt.Sprintf("%s-", user.Name), Labels: map[string]string{ - iamv1alpha2.UserReferenceLabel: username, + iamv1alpha2.UserReferenceLabel: user.Name, }, }, Spec: iamv1alpha2.LoginRecordSpec{ @@ -67,7 +77,7 @@ func (l *loginRecorder) RecordLogin(username string, loginType iamv1alpha2.Login loginEntry.Spec.Reason = authErr.Error() } - _, err := l.ksClient.IamV1alpha2().LoginRecords().Create(context.Background(), loginEntry, metav1.CreateOptions{}) + _, err = l.ksClient.IamV1alpha2().LoginRecords().Create(context.Background(), loginEntry, metav1.CreateOptions{}) if err != nil { klog.Error(err) return err diff --git a/vendor/k8s.io/client-go/metadata/metadatainformer/informer.go b/vendor/k8s.io/client-go/metadata/metadatainformer/informer.go deleted file mode 100644 index e4ebd61f8..000000000 --- a/vendor/k8s.io/client-go/metadata/metadatainformer/informer.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package metadatainformer - -import ( - "context" - "sync" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/informers" - "k8s.io/client-go/metadata" - "k8s.io/client-go/metadata/metadatalister" - "k8s.io/client-go/tools/cache" -) - -// NewSharedInformerFactory constructs a new instance of metadataSharedInformerFactory for all namespaces. -func NewSharedInformerFactory(client metadata.Interface, defaultResync time.Duration) SharedInformerFactory { - return NewFilteredSharedInformerFactory(client, defaultResync, metav1.NamespaceAll, nil) -} - -// NewFilteredSharedInformerFactory constructs a new instance of metadataSharedInformerFactory. -// Listers obtained via this factory will be subject to the same filters as specified here. -func NewFilteredSharedInformerFactory(client metadata.Interface, defaultResync time.Duration, namespace string, tweakListOptions TweakListOptionsFunc) SharedInformerFactory { - return &metadataSharedInformerFactory{ - client: client, - defaultResync: defaultResync, - namespace: namespace, - informers: map[schema.GroupVersionResource]informers.GenericInformer{}, - startedInformers: make(map[schema.GroupVersionResource]bool), - tweakListOptions: tweakListOptions, - } -} - -type metadataSharedInformerFactory struct { - client metadata.Interface - defaultResync time.Duration - namespace string - - lock sync.Mutex - informers map[schema.GroupVersionResource]informers.GenericInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[schema.GroupVersionResource]bool - tweakListOptions TweakListOptionsFunc -} - -var _ SharedInformerFactory = &metadataSharedInformerFactory{} - -func (f *metadataSharedInformerFactory) ForResource(gvr schema.GroupVersionResource) informers.GenericInformer { - f.lock.Lock() - defer f.lock.Unlock() - - key := gvr - informer, exists := f.informers[key] - if exists { - return informer - } - - informer = NewFilteredMetadataInformer(f.client, gvr, f.namespace, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) - f.informers[key] = informer - - return informer -} - -// Start initializes all requested informers. -func (f *metadataSharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - go informer.Informer().Run(stopCh) - f.startedInformers[informerType] = true - } - } -} - -// WaitForCacheSync waits for all started informers' cache were synced. -func (f *metadataSharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool { - informers := func() map[schema.GroupVersionResource]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[schema.GroupVersionResource]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer.Informer() - } - } - return informers - }() - - res := map[schema.GroupVersionResource]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// NewFilteredMetadataInformer constructs a new informer for a metadata type. -func NewFilteredMetadataInformer(client metadata.Interface, gvr schema.GroupVersionResource, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions TweakListOptionsFunc) informers.GenericInformer { - return &metadataInformer{ - gvr: gvr, - informer: cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.Resource(gvr).Namespace(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.Resource(gvr).Namespace(namespace).Watch(context.TODO(), options) - }, - }, - &metav1.PartialObjectMetadata{}, - resyncPeriod, - indexers, - ), - } -} - -type metadataInformer struct { - informer cache.SharedIndexInformer - gvr schema.GroupVersionResource -} - -var _ informers.GenericInformer = &metadataInformer{} - -func (d *metadataInformer) Informer() cache.SharedIndexInformer { - return d.informer -} - -func (d *metadataInformer) Lister() cache.GenericLister { - return metadatalister.NewRuntimeObjectShim(metadatalister.New(d.informer.GetIndexer(), d.gvr)) -} diff --git a/vendor/k8s.io/client-go/metadata/metadatainformer/interface.go b/vendor/k8s.io/client-go/metadata/metadatainformer/interface.go deleted file mode 100644 index 732e565c7..000000000 --- a/vendor/k8s.io/client-go/metadata/metadatainformer/interface.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package metadatainformer - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/informers" -) - -// SharedInformerFactory provides access to a shared informer and lister for dynamic client -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - ForResource(gvr schema.GroupVersionResource) informers.GenericInformer - WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool -} - -// TweakListOptionsFunc defines the signature of a helper function -// that wants to provide more listing options to API -type TweakListOptionsFunc func(*metav1.ListOptions) diff --git a/vendor/k8s.io/client-go/metadata/metadatalister/interface.go b/vendor/k8s.io/client-go/metadata/metadatalister/interface.go deleted file mode 100644 index bb3548589..000000000 --- a/vendor/k8s.io/client-go/metadata/metadatalister/interface.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package metadatalister - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -// Lister helps list resources. -type Lister interface { - // List lists all resources in the indexer. - List(selector labels.Selector) (ret []*metav1.PartialObjectMetadata, err error) - // Get retrieves a resource from the indexer with the given name - Get(name string) (*metav1.PartialObjectMetadata, error) - // Namespace returns an object that can list and get resources in a given namespace. - Namespace(namespace string) NamespaceLister -} - -// NamespaceLister helps list and get resources. -type NamespaceLister interface { - // List lists all resources in the indexer for a given namespace. - List(selector labels.Selector) (ret []*metav1.PartialObjectMetadata, err error) - // Get retrieves a resource from the indexer for a given namespace and name. - Get(name string) (*metav1.PartialObjectMetadata, error) -} diff --git a/vendor/k8s.io/client-go/metadata/metadatalister/lister.go b/vendor/k8s.io/client-go/metadata/metadatalister/lister.go deleted file mode 100644 index faeccc0fc..000000000 --- a/vendor/k8s.io/client-go/metadata/metadatalister/lister.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package metadatalister - -import ( - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/tools/cache" -) - -var _ Lister = &metadataLister{} -var _ NamespaceLister = &metadataNamespaceLister{} - -// metadataLister implements the Lister interface. -type metadataLister struct { - indexer cache.Indexer - gvr schema.GroupVersionResource -} - -// New returns a new Lister. -func New(indexer cache.Indexer, gvr schema.GroupVersionResource) Lister { - return &metadataLister{indexer: indexer, gvr: gvr} -} - -// List lists all resources in the indexer. -func (l *metadataLister) List(selector labels.Selector) (ret []*metav1.PartialObjectMetadata, err error) { - err = cache.ListAll(l.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*metav1.PartialObjectMetadata)) - }) - return ret, err -} - -// Get retrieves a resource from the indexer with the given name -func (l *metadataLister) Get(name string) (*metav1.PartialObjectMetadata, error) { - obj, exists, err := l.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(l.gvr.GroupResource(), name) - } - return obj.(*metav1.PartialObjectMetadata), nil -} - -// Namespace returns an object that can list and get resources from a given namespace. -func (l *metadataLister) Namespace(namespace string) NamespaceLister { - return &metadataNamespaceLister{indexer: l.indexer, namespace: namespace, gvr: l.gvr} -} - -// metadataNamespaceLister implements the NamespaceLister interface. -type metadataNamespaceLister struct { - indexer cache.Indexer - namespace string - gvr schema.GroupVersionResource -} - -// List lists all resources in the indexer for a given namespace. -func (l *metadataNamespaceLister) List(selector labels.Selector) (ret []*metav1.PartialObjectMetadata, err error) { - err = cache.ListAllByNamespace(l.indexer, l.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*metav1.PartialObjectMetadata)) - }) - return ret, err -} - -// Get retrieves a resource from the indexer for a given namespace and name. -func (l *metadataNamespaceLister) Get(name string) (*metav1.PartialObjectMetadata, error) { - obj, exists, err := l.indexer.GetByKey(l.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(l.gvr.GroupResource(), name) - } - return obj.(*metav1.PartialObjectMetadata), nil -} diff --git a/vendor/k8s.io/client-go/metadata/metadatalister/shim.go b/vendor/k8s.io/client-go/metadata/metadatalister/shim.go deleted file mode 100644 index f31c60725..000000000 --- a/vendor/k8s.io/client-go/metadata/metadatalister/shim.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package metadatalister - -import ( - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/cache" -) - -var _ cache.GenericLister = &metadataListerShim{} -var _ cache.GenericNamespaceLister = &metadataNamespaceListerShim{} - -// metadataListerShim implements the cache.GenericLister interface. -type metadataListerShim struct { - lister Lister -} - -// NewRuntimeObjectShim returns a new shim for Lister. -// It wraps Lister so that it implements cache.GenericLister interface -func NewRuntimeObjectShim(lister Lister) cache.GenericLister { - return &metadataListerShim{lister: lister} -} - -// List will return all objects across namespaces -func (s *metadataListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) { - objs, err := s.lister.List(selector) - if err != nil { - return nil, err - } - - ret = make([]runtime.Object, len(objs)) - for index, obj := range objs { - ret[index] = obj - } - return ret, err -} - -// Get will attempt to retrieve assuming that name==key -func (s *metadataListerShim) Get(name string) (runtime.Object, error) { - return s.lister.Get(name) -} - -func (s *metadataListerShim) ByNamespace(namespace string) cache.GenericNamespaceLister { - return &metadataNamespaceListerShim{ - namespaceLister: s.lister.Namespace(namespace), - } -} - -// metadataNamespaceListerShim implements the NamespaceLister interface. -// It wraps NamespaceLister so that it implements cache.GenericNamespaceLister interface -type metadataNamespaceListerShim struct { - namespaceLister NamespaceLister -} - -// List will return all objects in this namespace -func (ns *metadataNamespaceListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) { - objs, err := ns.namespaceLister.List(selector) - if err != nil { - return nil, err - } - - ret = make([]runtime.Object, len(objs)) - for index, obj := range objs { - ret[index] = obj - } - return ret, err -} - -// Get will attempt to retrieve by namespace and name -func (ns *metadataNamespaceListerShim) Get(name string) (runtime.Object, error) { - return ns.namespaceLister.Get(name) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7e42a3498..c6183d0c8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -652,7 +652,7 @@ go.uber.org/zap/internal/bufferpool go.uber.org/zap/internal/color go.uber.org/zap/internal/exit go.uber.org/zap/zapcore -# golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 => golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 +# golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de => golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish golang.org/x/crypto/cast5 @@ -701,7 +701,7 @@ golang.org/x/oauth2/internal # golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 => golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/sync/errgroup golang.org/x/sync/singleflight -# golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c => golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e +# golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 => golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e golang.org/x/sys/cpu golang.org/x/sys/unix golang.org/x/sys/windows @@ -1424,8 +1424,6 @@ k8s.io/client-go/listers/storage/v1 k8s.io/client-go/listers/storage/v1alpha1 k8s.io/client-go/listers/storage/v1beta1 k8s.io/client-go/metadata -k8s.io/client-go/metadata/metadatainformer -k8s.io/client-go/metadata/metadatalister k8s.io/client-go/pkg/apis/clientauthentication k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1 k8s.io/client-go/pkg/apis/clientauthentication/v1beta1