Skip to content

Commit

Permalink
feat: implement controller runtime watch cache
Browse files Browse the repository at this point in the history
If enabled, controller runtime uses a side-effect of watching resources to
build an in-memory view of resources being read to substitute controller
reads from the state with reads from the cache.

Signed-off-by: Andrey Smirnov <[email protected]>
  • Loading branch information
smira committed Jan 26, 2024
1 parent 582936d commit a7ba871
Show file tree
Hide file tree
Showing 20 changed files with 542 additions and 101 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2023-12-18T13:40:01Z by kres latest.
# Generated on 2024-01-25T15:42:54Z by kres latest.

ARG TOOLCHAIN

Expand All @@ -11,9 +11,9 @@ FROM ghcr.io/siderolabs/ca-certificates:v1.6.0 AS image-ca-certificates
FROM ghcr.io/siderolabs/fhs:v1.6.0 AS image-fhs

# runs markdownlint
FROM docker.io/node:21.4.0-alpine3.18 AS lint-markdown
FROM docker.io/node:21.5.0-alpine3.19 AS lint-markdown
WORKDIR /src
RUN npm i -g markdownlint-cli@0.37.0
RUN npm i -g markdownlint-cli@0.38.0
RUN npm i [email protected]
COPY .markdownlint.json .
COPY ./README.md ./README.md
Expand Down
27 changes: 22 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2023-12-18T13:40:01Z by kres latest.
# Generated on 2024-01-25T15:42:54Z by kres latest.

# common variables

Expand All @@ -14,15 +14,15 @@ WITH_RACE ?= false
REGISTRY ?= ghcr.io
USERNAME ?= cosi-project
REGISTRY_AND_USERNAME ?= $(REGISTRY)/$(USERNAME)
PROTOBUF_GO_VERSION ?= 1.31.0
PROTOBUF_GO_VERSION ?= 1.32.0
GRPC_GO_VERSION ?= 1.3.0
GRPC_GATEWAY_VERSION ?= 2.18.1
GRPC_GATEWAY_VERSION ?= 2.19.0
VTPROTOBUF_VERSION ?= 0.5.0
DEEPCOPY_VERSION ?= v0.5.5
GOLANGCILINT_VERSION ?= v1.55.2
GOFUMPT_VERSION ?= v0.5.0
GO_VERSION ?= 1.21.5
GOIMPORTS_VERSION ?= v0.16.1
GO_VERSION ?= 1.21.6
GOIMPORTS_VERSION ?= v0.17.0
GO_BUILDFLAGS ?=
GO_LDFLAGS ?=
CGO_ENABLED ?= 0
Expand Down Expand Up @@ -88,6 +88,23 @@ To create a builder instance, run:

docker buildx create --name local --use

If running builds that needs to be cached aggresively create a builder instance with the following:

docker buildx create --name local --use --config=config.toml

config.toml contents:

[worker.oci]
gc = true
gckeepstorage = 50000

[[worker.oci.gcpolicy]]
keepBytes = 10737418240
keepDuration = 604800
filters = [ "type==source.local", "type==exec.cachemount", "type==source.git.checkout"]
[[worker.oci.gcpolicy]]
all = true
keepBytes = 53687091200

If you already have a compatible builder instance, you may use that instead.

Expand Down
20 changes: 10 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/cosi-project/runtime

go 1.21.3
go 1.21.6

// forked yaml that introduces RawYAML interface that can be used to provide YAML encoder bytes
// which are then encoded as a valid YAML block with proper indentiation
Expand All @@ -10,7 +10,7 @@ require (
github.com/ProtonMail/gopenpgp/v2 v2.7.4
github.com/cenkalti/backoff/v4 v4.2.1
github.com/gertd/go-pluralize v0.2.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0
github.com/hashicorp/go-multierror v1.1.1
github.com/klauspost/compress v1.17.4
github.com/siderolabs/gen v0.4.7
Expand All @@ -21,10 +21,10 @@ require (
go.etcd.io/bbolt v1.3.8
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.26.0
golang.org/x/sync v0.5.0
golang.org/x/sync v0.6.0
golang.org/x/time v0.5.0
google.golang.org/grpc v1.60.0
google.golang.org/protobuf v1.31.0
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.32.0
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -39,10 +39,10 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
)
40 changes: 20 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
Expand Down Expand Up @@ -67,8 +67,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -77,13 +77,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -93,8 +93,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
Expand All @@ -116,18 +116,18 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k=
google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
8 changes: 4 additions & 4 deletions pkg/controller/conformance/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,8 +516,8 @@ func (suite *RuntimeSuite) TestControllerRuntimeMetrics() {

ctrl := &MetricsController{
ControllerName: controllerName,
SourceNamespace: "default",
TargetNamespace: "default",
SourceNamespace: "metrics",
TargetNamespace: "metrics",
}

suite.Zero(getIntValFromExpVarMap(metrics.ControllerWakeups, ctrl.Name()), "ControllerWakeups should be 0")
Expand All @@ -527,14 +527,14 @@ func (suite *RuntimeSuite) TestControllerRuntimeMetrics() {
// initial wakeup will be scheduled on RegisterController
suite.Equal(1, getIntValFromExpVarMap(metrics.ControllerWakeups, ctrl.Name()), "ControllerWakeups should be 1")

intRes := NewIntResource("default", "one", 1)
intRes := NewIntResource("metrics", "one", 1)

suite.Assert().NoError(suite.State.Create(suite.ctx, intRes))

suite.startRuntime()

suite.EventuallyWithT(func(collect *assert.CollectT) {
_, err := suite.State.Get(suite.ctx, NewStrResource("default", "one", "").Metadata())
_, err := suite.State.Get(suite.ctx, NewStrResource("metrics", "one", "").Metadata())

assert.NoError(collect, err)

Expand Down
1 change: 0 additions & 1 deletion pkg/controller/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ type Output struct {
type Reader interface {
Get(context.Context, resource.Pointer, ...state.GetOption) (resource.Resource, error)
List(context.Context, resource.Kind, ...state.ListOption) (resource.List, error)
WatchFor(context.Context, resource.Pointer, ...state.WatchForConditionFunc) (resource.Resource, error)
}

// Writer provides write access to the state.
Expand Down
2 changes: 2 additions & 0 deletions pkg/controller/runtime/internal/adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"go.uber.org/zap"

"github.com/cosi-project/runtime/pkg/controller/runtime/internal/cache"
"github.com/cosi-project/runtime/pkg/controller/runtime/internal/dependency"
"github.com/cosi-project/runtime/pkg/controller/runtime/internal/reduced"
"github.com/cosi-project/runtime/pkg/controller/runtime/options"
Expand All @@ -31,6 +32,7 @@ type Adapter interface {
type Options struct {
Logger *zap.Logger
State state.State
Cache *cache.ResourceCache
DepDB *dependency.Database
RegisterWatch func(resourceNamespace resource.Namespace, resourceType resource.Type) error
RuntimeOptions options.Options
Expand Down
136 changes: 136 additions & 0 deletions pkg/controller/runtime/internal/cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Package cache implements resource cache.
package cache

import (
"context"
"fmt"

"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/controller/runtime/options"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
)

// ResourceCache provides a read-only view of resources which implements controller.Reader interface.
//
// ResourceCache is populated by controller runtime based on Watch events.
//
// ResourceCache is supposed to be used with WatchKind with BootstrapContents:
// - before the Bootstrapped event, Get/List will block, and Watch handler should call CacheAppend to populate the cache
// - on the Bootstrapped event, Watch handler should call MarkBootstrapped, and Get/List operations will be unblocked
// - after the Bootstrapped event, CachePut/CacheRemove should be called to update the cache.
//
// Get/List operations will always return date from the cache without blocking once the cache is bootstrapped.
type ResourceCache struct {
// not using any locking here, as handlers can only be registered during initialization
handlers map[cacheKey]*cacheHandler
}

type cacheKey struct {
Namespace resource.Namespace
Type resource.Type
}

// Check interfaces.
var _ controller.Reader = (*ResourceCache)(nil)

// NewResourceCache creates new resource cache.
func NewResourceCache(resources []options.CachedResource) *ResourceCache {
cache := &ResourceCache{
handlers: make(map[cacheKey]*cacheHandler, len(resources)),
}

for _, r := range resources {
key := cacheKey{
Namespace: r.Namespace,
Type: r.Type,
}

cache.handlers[key] = newCacheHandler(key)
}

return cache
}

func (cache *ResourceCache) getHandler(namespace resource.Namespace, resourceType resource.Type) *cacheHandler {
key := cacheKey{
Namespace: namespace,
Type: resourceType,
}

handler, ok := cache.handlers[key]
if !ok {
panic(fmt.Sprintf("cache handler for %s/%s doesn't exist", namespace, resourceType))
}

return handler
}

// MarkBootstrapped marks cache as bootstrapped.
func (cache *ResourceCache) MarkBootstrapped(namespace resource.Namespace, resourceType resource.Type) {
cache.getHandler(namespace, resourceType).markBootstrapped()
}

// Len returns number of cached resources.
func (cache *ResourceCache) Len(namespace resource.Namespace, resourceType resource.Type) int {
return cache.getHandler(namespace, resourceType).len()
}

// IsHandled returns true if cache is handling given resource type.
func (cache *ResourceCache) IsHandled(namespace resource.Namespace, resourceType resource.Type) bool {
_, ok := cache.handlers[cacheKey{
Namespace: namespace,
Type: resourceType,
}]

return ok
}

// IsHandledBootstrapped returns true if cache is handling given resource type and whether it is bootstrapped.
func (cache *ResourceCache) IsHandledBootstrapped(namespace resource.Namespace, resourceType resource.Type) (handled bool, bootstrapped bool) {
var handler *cacheHandler

handler, handled = cache.handlers[cacheKey{
Namespace: namespace,
Type: resourceType,
}]

if handled {
bootstrapped = handler.isBootstrapped()
}

return handled, bootstrapped
}

// Get implements controller.Reader interface.
func (cache *ResourceCache) Get(ctx context.Context, ptr resource.Pointer, opts ...state.GetOption) (resource.Resource, error) {
return cache.getHandler(ptr.Namespace(), ptr.Type()).get(ctx, ptr.ID(), opts...)
}

// List implements controller.Reader interface.
func (cache *ResourceCache) List(ctx context.Context, kind resource.Kind, opts ...state.ListOption) (resource.List, error) {
return cache.getHandler(kind.Namespace(), kind.Type()).list(ctx, opts...)
}

// CacheAppend appends the value to the cached list.
//
// CacheAppend should be called in the bootstrapped phase, with resources coming in sorted by ID order.
func (cache *ResourceCache) CacheAppend(r resource.Resource) {
cache.getHandler(r.Metadata().Namespace(), r.Metadata().Type()).append(r)
}

// CachePut handles updated/created objects.
//
// It is called once the bootstrap is done.
func (cache *ResourceCache) CachePut(r resource.Resource) {
cache.getHandler(r.Metadata().Namespace(), r.Metadata().Type()).put(r)
}

// CacheRemove handles deleted objects.
func (cache *ResourceCache) CacheRemove(r resource.Resource) {
cache.getHandler(r.Metadata().Namespace(), r.Metadata().Type()).remove(r)
}
Loading

0 comments on commit a7ba871

Please sign in to comment.