├── .gitignore ├── .travis.yml ├── Makefile ├── api ├── Dockerfile ├── main.go └── server │ ├── api.go │ ├── api_test.go │ ├── cache_service.go │ └── error.go ├── examples └── client.go ├── go.mod ├── go.sum ├── helm-charts ├── .helmignore ├── Chart.yaml ├── templates │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── tests │ │ └── test-connection.yaml └── values.yaml ├── k8s ├── deployment.yaml ├── ingress.yaml ├── namespace.yaml └── service.yaml ├── license ├── proto ├── cache-service.pb.go └── cache-service.proto ├── readme.md ├── scripts ├── build-docker.sh ├── build.sh ├── protoc-gen.sh ├── run-helm.sh ├── run-k8-server.sh └── run-terraform.sh └── terraform ├── main.tf ├── outputs.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | server-cache 4 | client-cache 5 | .terraform/ 6 | terraform.* 7 | .terraform* 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: go 3 | 4 | go: 5 | - 1.11.x 6 | - 1.12.x 7 | - 1.13.x 8 | - tip 9 | 10 | matrix: 11 | allow_failures: 12 | - go: tip 13 | 14 | env: 15 | - GO111MODULE=on 16 | 17 | before_install: 18 | - go get github.com/axw/gocov/gocov 19 | - go get github.com/mattn/goveralls 20 | - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi 21 | 22 | script: 23 | - go test api/server/* -v -cover -race 24 | - $HOME/gopath/bin/goveralls -ignore "proto/*" -service=travis-ci 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | - chmod 777 scripts/protoc-gen.sh && chmod 777 scripts/build.sh 3 | - scripts/build.sh 4 | 5 | docker: 6 | - chmod 777 scripts/build-docker.sh 7 | - scripts/build-docker.sh 8 | 9 | protoc: 10 | - scripts/protoc-gen.sh 11 | 12 | server: 13 | - go run api/main.go 14 | 15 | dockerServer: 16 | - docker run -it -p 5001:5001 knrt10/grpc-cache 17 | 18 | client: 19 | - go run examples/client.go 20 | 21 | test: 22 | - go test api/server/* -v -cover -race 23 | 24 | run-k8s-server: 25 | - chmod 777 scripts/run-k8-server.sh 26 | - scripts/run-k8-server.sh 27 | 28 | run-helm-server: 29 | - chmod 777 scripts/run-helm.sh 30 | - scripts/run-helm.sh 31 | 32 | run-terraform-server: 33 | - chmod 777 scripts/run-terraform.sh 34 | - scripts/run-terraform.sh 35 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang 2 | 3 | ADD . /go/src/github.com/knrt10/gRPC-cache/ 4 | 5 | WORKDIR /go/src/github.com/knrt10/gRPC-cache/ 6 | 7 | RUN chmod 777 scripts/protoc-gen.sh && chmod 777 scripts/build.sh && scripts/build.sh 8 | 9 | ENTRYPOINT [ "./server-cache" ] 10 | 11 | EXPOSE 5001 12 | -------------------------------------------------------------------------------- /api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net" 8 | "time" 9 | 10 | cache "github.com/knrt10/grpc-cache/api/server" 11 | api "github.com/knrt10/grpc-cache/proto" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/reflection" 14 | ) 15 | 16 | var ( 17 | address string 18 | expire int 19 | cleanup int 20 | ) 21 | 22 | func main() { 23 | // Get address from flag 24 | flag.StringVar(&address, "addr", ":5001", "Address on which you want to run server") 25 | flag.IntVar(&expire, "exp", 10, "Default expiration duration of cache is 10 min") 26 | flag.IntVar(&cleanup, "cln", 5, "Cleanup interval duration of expired cache is 5 min") 27 | flag.Parse() 28 | 29 | opts := []grpc.ServerOption{ 30 | grpc.MaxConcurrentStreams(200), 31 | } 32 | 33 | // create a gRPC server object 34 | grpcServer := grpc.NewServer(opts...) 35 | // Default expiration of cache is 10 minutes and default purge time for expired items is 5 minutes 36 | api.RegisterCacheServiceServer(grpcServer, cache.NewCacheService(time.Duration(expire)*time.Minute, time.Duration(cleanup)*time.Minute)) 37 | 38 | reflection.Register(grpcServer) 39 | 40 | lis, err := net.Listen("tcp", address) 41 | if err != nil { 42 | log.Fatalf("Error in starting server %v", err) 43 | } 44 | fmt.Println("Started the server on:", address) 45 | if err := grpcServer.Serve(lis); err != nil { 46 | log.Fatalf("err in serving gRPC %v\n", err) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /api/server/api.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | "time" 7 | 8 | "github.com/golang/protobuf/ptypes/empty" 9 | api "github.com/knrt10/grpc-cache/proto" 10 | ) 11 | 12 | // Add is used to add key/value pair to the cache. 13 | func (c *cache) Add(ctx context.Context, item *api.Item) (*api.Item, error) { 14 | var expiration int64 15 | duration, _ := time.ParseDuration(item.Expiration) 16 | // Meaning d is of form "2m30s" 17 | if duration > 0 { 18 | expiration = time.Now().Add(duration).UnixNano() 19 | } 20 | c.mu.Lock() 21 | c.items[item.Key] = Item{ 22 | Object: item.Value, 23 | Expiration: expiration, 24 | } 25 | c.mu.Unlock() 26 | return item, nil 27 | } 28 | 29 | // Get method is used to key/value pair while providing key as args 30 | func (c *cache) Get(ctx context.Context, args *api.GetKey) (*api.Item, error) { 31 | key := args.Key 32 | // Locking so that other goroutines cannot access this at the same time 33 | c.mu.RLock() 34 | value, exists := c.items[key] 35 | // No key found 36 | if !exists { 37 | c.mu.RUnlock() 38 | return nil, ErrNoKey 39 | } 40 | 41 | // This means key has some expiration 42 | if value.(Item).Expiration > 0 { 43 | if time.Now().UnixNano() > value.(Item).Expiration { 44 | c.mu.RUnlock() 45 | return nil, ErrKeyExpired 46 | } 47 | } 48 | c.mu.RUnlock() 49 | return &api.Item{ 50 | Key: key, 51 | Value: value.(Item).Object.(string), 52 | Expiration: time.Unix(0, value.(Item).Expiration).String(), 53 | }, nil 54 | } 55 | 56 | // GetByPrefix method is all keys that match the prefix while providing key as args 57 | func (c *cache) GetByPrefix(ctx context.Context, args *api.GetKey) (*api.AllItems, error) { 58 | key := args.Key 59 | c.mu.RLock() 60 | defer c.mu.RUnlock() 61 | var items []*api.Item 62 | now := time.Now().UnixNano() 63 | for k, v := range c.items { 64 | if v.(Item).Expiration > 0 { 65 | if now > v.(Item).Expiration { 66 | continue 67 | } 68 | } 69 | 70 | if strings.Contains(k.(string), key) { 71 | items = append(items, &api.Item{ 72 | Key: k.(string), 73 | Value: v.(Item).Object.(string), 74 | Expiration: time.Unix(0, v.(Item).Expiration).String(), 75 | }) 76 | } 77 | } 78 | // This means no keys were found, or all were expired 79 | if len(items) < 1 { 80 | return nil, ErrNoKey 81 | } 82 | 83 | return &api.AllItems{ 84 | Items: items, 85 | }, nil 86 | } 87 | 88 | // GetAllItems method get all unexpired keys from the cache 89 | func (c *cache) GetAllItems(ctx context.Context, in *empty.Empty) (*api.AllItems, error) { 90 | c.mu.RLock() 91 | defer c.mu.RUnlock() 92 | var items []*api.Item 93 | now := time.Now().UnixNano() 94 | for k, v := range c.items { 95 | if v.(Item).Expiration > 0 { 96 | if now > v.(Item).Expiration { 97 | continue 98 | } 99 | } 100 | 101 | items = append(items, &api.Item{ 102 | Key: k.(string), 103 | Value: v.(Item).Object.(string), 104 | Expiration: time.Unix(0, v.(Item).Expiration).String(), 105 | }) 106 | } 107 | 108 | // This means no keys were found, or all were expired 109 | if len(items) < 1 { 110 | return nil, ErrNoKey 111 | } 112 | 113 | return &api.AllItems{ 114 | Items: items, 115 | }, nil 116 | } 117 | 118 | // DeleteKey deletes an item from the cache. Does nothing if the key is not in the cache. 119 | func (c *cache) DeleteKey(ctx context.Context, args *api.GetKey) (*api.Success, error) { 120 | c.mu.Lock() 121 | c.delete(args.Key) 122 | c.mu.Unlock() 123 | return &api.Success{ 124 | Success: true, 125 | }, nil 126 | } 127 | 128 | // Delete all items from the cache. 129 | func (c *cache) DeleteAll(ctx context.Context, in *empty.Empty) (*api.Success, error) { 130 | c.mu.Lock() 131 | c.items = map[interface{}]interface{}{} 132 | c.mu.Unlock() 133 | return &api.Success{ 134 | Success: true, 135 | }, nil 136 | } 137 | -------------------------------------------------------------------------------- /api/server/api_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | "strconv" 8 | "testing" 9 | "time" 10 | 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/test/bufconn" 13 | 14 | "github.com/golang/protobuf/ptypes/empty" 15 | apis "github.com/knrt10/grpc-cache/proto" 16 | ) 17 | 18 | const ( 19 | bufSize = 1024 * 1024 20 | expire = 10 21 | cleanup = 1 22 | ) 23 | 24 | var lis *bufconn.Listener 25 | 26 | func init() { 27 | lis = bufconn.Listen(bufSize) 28 | s := grpc.NewServer() 29 | apis.RegisterCacheServiceServer(s, NewCacheService(time.Duration(expire)*time.Minute, time.Duration(cleanup)*time.Second)) 30 | go func() { 31 | if err := s.Serve(lis); err != nil { 32 | log.Fatalf("Server exited with error: %v", err) 33 | } 34 | }() 35 | } 36 | 37 | func bufDialer(context.Context, string) (net.Conn, error) { 38 | return lis.Dial() 39 | } 40 | 41 | func TestAdd(t *testing.T) { 42 | ctx := context.Background() 43 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 44 | if err != nil { 45 | t.Fatalf("Failed to dial bufnet: %v", err) 46 | } 47 | defer conn.Close() 48 | c := apis.NewCacheServiceClient(conn) 49 | keyVal1 := &apis.Item{ 50 | Key: "kautilya", 51 | Value: "knrt10", 52 | Expiration: "1m", 53 | } 54 | 55 | keyVal2 := &apis.Item{ 56 | Key: "24", 57 | Value: "palash", 58 | Expiration: "1m", 59 | } 60 | 61 | keyVal3 := &apis.Item{ 62 | Key: "foo", 63 | Value: "bar", 64 | Expiration: "1m", 65 | } 66 | 67 | keyVal4 := &apis.Item{ 68 | Key: "temp", 69 | Value: "bar", 70 | Expiration: "1µs", 71 | } 72 | 73 | c.Add(context.Background(), keyVal2) 74 | c.Add(context.Background(), keyVal3) 75 | c.Add(context.Background(), keyVal4) 76 | 77 | resp, err := c.Add(context.Background(), keyVal1) 78 | if err != nil { 79 | t.Fatalf("Adding key Failed: %v", err) 80 | } 81 | if resp.Key != "kautilya" { 82 | t.Errorf("handler returned unexpected body: got %v want %v", 83 | resp.Key, "kautilya") 84 | } 85 | if resp.Value != "knrt10" { 86 | t.Errorf("handler returned unexpected body: got %v want %v", 87 | resp.Key, "knrt10") 88 | } 89 | 90 | // Save keys 91 | // Checking for race condition 92 | for i := 0; i < 100; i++ { 93 | go c.Add(context.Background(), &apis.Item{ 94 | Key: strconv.Itoa(i), 95 | Value: "Value of i is ", 96 | Expiration: strconv.Itoa(i), 97 | }) 98 | } 99 | 100 | } 101 | 102 | func TestGet(t *testing.T) { 103 | ctx := context.Background() 104 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 105 | if err != nil { 106 | t.Fatalf("Failed to dial bufnet: %v", err) 107 | } 108 | defer conn.Close() 109 | c := apis.NewCacheServiceClient(conn) 110 | 111 | keyGet := &apis.GetKey{ 112 | Key: "kautilya", 113 | } 114 | resp, err := c.Get(context.Background(), keyGet) 115 | if err != nil { 116 | t.Fatalf("Getting key Failed: %v", err) 117 | } 118 | if resp.Key != "kautilya" { 119 | t.Errorf("handler returned unexpected body: got %v want %v", 120 | resp.Key, "kautilya") 121 | } 122 | if resp.Value != "knrt10" { 123 | t.Errorf("handler returned unexpected body: got %v want %v", 124 | resp.Key, "knrt10") 125 | } 126 | } 127 | 128 | func TestGetByPrefix(t *testing.T) { 129 | ctx := context.Background() 130 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 131 | if err != nil { 132 | t.Fatalf("Failed to dial bufnet: %v", err) 133 | } 134 | defer conn.Close() 135 | c := apis.NewCacheServiceClient(conn) 136 | 137 | keyVal1 := &apis.Item{ 138 | Key: "prefixTest", 139 | Value: "val1", 140 | Expiration: "10s", 141 | } 142 | 143 | keyVal2 := &apis.Item{ 144 | Key: "prefixTest1", 145 | Value: "val2", 146 | Expiration: "10s", 147 | } 148 | 149 | keyVal3 := &apis.Item{ 150 | Key: "prefixTest2", 151 | Value: "val3", 152 | Expiration: "10s", 153 | } 154 | 155 | c.Add(context.Background(), keyVal1) 156 | c.Add(context.Background(), keyVal2) 157 | c.Add(context.Background(), keyVal3) 158 | 159 | keyWrongPrefix := &apis.GetKey{ 160 | Key: "wrongPrefix", 161 | } 162 | _, err = c.GetByPrefix(context.Background(), keyWrongPrefix) 163 | if err.Error() != "rpc error: code = Unknown desc = No key found" { 164 | t.Errorf("No key found") 165 | } 166 | 167 | keyRightPrefix := &apis.GetKey{ 168 | Key: "prefixTest", 169 | } 170 | 171 | resp, err := c.GetByPrefix(context.Background(), keyRightPrefix) 172 | if err != nil { 173 | t.Fatalf("Getting key by prefix Failed: %v", err) 174 | } 175 | if len(resp.Items) != 3 { 176 | t.Errorf("handler returned unexpected body: got %v want %v", 177 | len(resp.Items), 3) 178 | } 179 | } 180 | 181 | func TestGetAllItems(t *testing.T) { 182 | ctx := context.Background() 183 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 184 | if err != nil { 185 | t.Fatalf("Failed to dial bufnet: %v", err) 186 | } 187 | defer conn.Close() 188 | c := apis.NewCacheServiceClient(conn) 189 | 190 | _, err = c.GetAllItems(context.Background(), &empty.Empty{}) 191 | if err != nil { 192 | t.Fatalf("Getting all keys Failed: %v", err) 193 | } 194 | } 195 | 196 | func TestDeleteKey(t *testing.T) { 197 | ctx := context.Background() 198 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 199 | if err != nil { 200 | t.Fatalf("Failed to dial bufnet: %v", err) 201 | } 202 | defer conn.Close() 203 | c := apis.NewCacheServiceClient(conn) 204 | 205 | keyGet := &apis.GetKey{ 206 | Key: "22", 207 | } 208 | resp, err := c.DeleteKey(context.Background(), keyGet) 209 | if err != nil { 210 | t.Fatalf("Deleting key Failed: %v", err) 211 | } 212 | if resp.Success != true { 213 | t.Errorf("handler returned unexpected body: got %v want %v", 214 | resp.Success, true) 215 | } 216 | } 217 | 218 | func TestDeleteAll(t *testing.T) { 219 | ctx := context.Background() 220 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 221 | if err != nil { 222 | t.Fatalf("Failed to dial bufnet: %v", err) 223 | } 224 | defer conn.Close() 225 | c := apis.NewCacheServiceClient(conn) 226 | 227 | resp, err := c.DeleteAll(context.Background(), &empty.Empty{}) 228 | if err != nil { 229 | t.Fatalf("Deleting key Failed: %v", err) 230 | } 231 | if resp.Success != true { 232 | t.Errorf("handler returned unexpected body: got %v want %v", 233 | resp.Success, true) 234 | } 235 | } 236 | 237 | // Testing deleted Key 238 | func TestGetDeletedKey(t *testing.T) { 239 | ctx := context.Background() 240 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 241 | if err != nil { 242 | t.Fatalf("Failed to dial bufnet: %v", err) 243 | } 244 | defer conn.Close() 245 | c := apis.NewCacheServiceClient(conn) 246 | 247 | // Geting expired key 248 | keyGet := &apis.GetKey{ 249 | Key: "temp", 250 | } 251 | _, err = c.Get(context.Background(), keyGet) 252 | if err.Error() != "rpc error: code = Unknown desc = No key found" { 253 | t.Errorf("Key not deleted") 254 | } 255 | } 256 | 257 | func TestDeleteKeyByExpiration(t *testing.T) { 258 | ctx := context.Background() 259 | conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure()) 260 | if err != nil { 261 | t.Fatalf("Failed to dial bufnet: %v", err) 262 | } 263 | defer conn.Close() 264 | c := apis.NewCacheServiceClient(conn) 265 | keyVal1 := &apis.Item{ 266 | Key: "expired", 267 | Value: "knrt10", 268 | Expiration: "1s", 269 | } 270 | 271 | resp, err := c.Add(context.Background(), keyVal1) 272 | if err != nil { 273 | t.Fatalf("Adding key Failed: %v", err) 274 | } 275 | if resp.Key != "expired" { 276 | t.Errorf("handler returned unexpected body: got %v want %v", 277 | resp.Key, "expired") 278 | } 279 | if resp.Value != "knrt10" { 280 | t.Errorf("handler returned unexpected body: got %v want %v", 281 | resp.Key, "knrt10") 282 | } 283 | 284 | time.Sleep(2 * time.Second) 285 | 286 | keyGet := &apis.GetKey{ 287 | Key: "expired", 288 | } 289 | _, err = c.Get(context.Background(), keyGet) 290 | if err.Error() != "rpc error: code = Unknown desc = No key found" { 291 | t.Errorf("Key not deleted") 292 | } 293 | 294 | } 295 | -------------------------------------------------------------------------------- /api/server/cache_service.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // Item items value of key and expiration 10 | type Item struct { 11 | Object interface{} 12 | Expiration int64 13 | } 14 | 15 | // Cache is a struct in which the cache's methods synchronize access to this map, so it is not 16 | // recommended to keep any references to the map around after creating a cache. 17 | type Cache struct { 18 | *cache 19 | } 20 | 21 | type worker struct { 22 | Interval time.Duration 23 | stop chan bool 24 | } 25 | 26 | type cache struct { 27 | defaultExpiration time.Duration 28 | mu sync.RWMutex 29 | items map[interface{}]interface{} 30 | worker *worker 31 | } 32 | 33 | // NewCacheService is used to initialize a new cache. 34 | // Return a new cache with a given default expiration duration and cleanup 35 | // interval. If the expiration duration is less than one, 36 | // the items in the cache never expire (by default), and must be deleted 37 | // manually. If the cleanup interval is less than one, expired items are not 38 | // deleted from the cache before calling c.deleteExpired(). 39 | func NewCacheService(defaultExpiration, cleanupInterval time.Duration) *Cache { 40 | items := make(map[interface{}]interface{}) 41 | return newCacheWithWorker(defaultExpiration, cleanupInterval, items) 42 | } 43 | 44 | // This ensures Worker goroutine (which granted it 45 | // was enabled is running deleteExpired on c forever) does not keep 46 | // the returned C object from being garbage collected. When it is 47 | // garbage collected, the finalizer stops the Worker goroutine, after 48 | // which c can be collected. 49 | func newCacheWithWorker(defaultExpiration time.Duration, cleanupInterval time.Duration, items map[interface{}]interface{}) *Cache { 50 | c := newCache(defaultExpiration, items) 51 | C := &Cache{c} 52 | if cleanupInterval > 0 { 53 | runWorker(c, cleanupInterval) 54 | runtime.SetFinalizer(C, stopWorker) 55 | } 56 | return C 57 | } 58 | 59 | func newCache(defaultExpiration time.Duration, items map[interface{}]interface{}) *cache { 60 | c := &cache{ 61 | defaultExpiration: defaultExpiration, 62 | items: items, 63 | } 64 | return c 65 | } 66 | 67 | // This stops the Worker 68 | func stopWorker(c *Cache) { 69 | c.worker.stop <- true 70 | } 71 | 72 | func runWorker(c *cache, cleanupInterval time.Duration) { 73 | w := &worker{ 74 | Interval: cleanupInterval, 75 | stop: make(chan bool), 76 | } 77 | c.worker = w 78 | // This starts the new ticker and checks for expiration keys in cache 79 | go w.Run(c) 80 | } 81 | 82 | // Run deletes keys that get expired 83 | func (w *worker) Run(c *cache) { 84 | ticker := time.NewTicker(w.Interval) 85 | for { 86 | select { 87 | case <-ticker.C: 88 | // This means key has expired 89 | c.deleteExpired() 90 | case <-w.stop: 91 | ticker.Stop() 92 | return 93 | } 94 | } 95 | } 96 | 97 | // delete is used to delete key from the cache 98 | func (c *cache) delete(k interface{}) (interface{}, bool) { 99 | delete(c.items, k) 100 | return nil, false 101 | } 102 | 103 | // deleteExpired is used to delete items from the cache when key time is expired 104 | func (c *cache) deleteExpired() { 105 | now := time.Now().UnixNano() 106 | c.mu.Lock() 107 | for k, v := range c.items { 108 | if v.(Item).Expiration > 0 && now > v.(Item).Expiration { 109 | c.delete(k) 110 | } 111 | } 112 | 113 | c.mu.Unlock() 114 | } 115 | -------------------------------------------------------------------------------- /api/server/error.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrNoKey for key not found 7 | ErrNoKey = errors.New("No key found") 8 | // ErrKeyExpired for keys expired 9 | ErrKeyExpired = errors.New("Key expired") 10 | ) 11 | -------------------------------------------------------------------------------- /examples/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "strconv" 9 | 10 | "google.golang.org/grpc" 11 | 12 | "github.com/golang/protobuf/ptypes/empty" 13 | api "github.com/knrt10/grpc-cache/proto" 14 | ) 15 | 16 | var ( 17 | address string 18 | conn *grpc.ClientConn 19 | err error 20 | ) 21 | 22 | func main() { 23 | 24 | // Get address from flag 25 | flag.StringVar(&address, "addr", "127.0.0.1:5001", "Address on which you want to run server") 26 | flag.Parse() 27 | conn, err = grpc.Dial(address, grpc.WithInsecure()) 28 | if err != nil { 29 | log.Fatalf("did not connect: %s", err) 30 | } 31 | defer conn.Close() 32 | 33 | c := api.NewCacheServiceClient(conn) 34 | 35 | // Add key 36 | keyVal1 := &api.Item{ 37 | Key: "22", 38 | Value: "knrt10", 39 | Expiration: "-1m", 40 | } 41 | 42 | keyVal2 := &api.Item{ 43 | Key: "distributed", 44 | Value: "systems", 45 | Expiration: "20s", 46 | } 47 | 48 | keyVal3 := &api.Item{ 49 | Key: "24", 50 | Value: "Palash", 51 | Expiration: "2min10s", 52 | } 53 | 54 | keyVal4 := &api.Item{ 55 | Key: "prefixTest", 56 | Value: "val1", 57 | Expiration: "10s", 58 | } 59 | 60 | keyVal5 := &api.Item{ 61 | Key: "prefixTest1", 62 | Value: "val2", 63 | Expiration: "10s", 64 | } 65 | 66 | keyVal6 := &api.Item{ 67 | Key: "prefixTest2", 68 | Value: "val3", 69 | Expiration: "10s", 70 | } 71 | 72 | c.Add(context.Background(), keyVal1) 73 | c.Add(context.Background(), keyVal2) 74 | c.Add(context.Background(), keyVal4) 75 | c.Add(context.Background(), keyVal5) 76 | c.Add(context.Background(), keyVal6) 77 | 78 | addKeyRes, err := c.Add(context.Background(), keyVal3) 79 | if err != nil { 80 | log.Fatalf("Error when calling Add: %s", err) 81 | } 82 | fmt.Println("Response from server for adding a key", addKeyRes) 83 | 84 | // Checking for race condition 85 | for i := 0; i < 50; i++ { 86 | go c.Add(context.Background(), &api.Item{ 87 | Key: strconv.Itoa(i), 88 | Value: "Value of i is ", 89 | Expiration: strconv.Itoa(i), 90 | }) 91 | } 92 | 93 | // Get key 94 | keyGet := &api.GetKey{ 95 | Key: "distributed", 96 | } 97 | 98 | getKeyRes, err := c.Get(context.Background(), keyGet) 99 | if err != nil { 100 | log.Fatalf("Error when calling Get: %s", err) 101 | } 102 | fmt.Println("Response from server for getting a key", getKeyRes) 103 | 104 | // Get keys by prefix 105 | keyGetPrefix := &api.GetKey{ 106 | Key: "prefixTest", 107 | } 108 | 109 | getKeyPrefixRes, err := c.GetByPrefix(context.Background(), keyGetPrefix) 110 | if err != nil { 111 | log.Fatalf("Error when calling Get: %s", err) 112 | } 113 | fmt.Println("Response from server for getting a keys by prefix", getKeyPrefixRes) 114 | 115 | // GetAllItems 116 | getAllKeysRes, err := c.GetAllItems(context.Background(), &empty.Empty{}) 117 | if err != nil { 118 | log.Fatalf("Error when calling GetAllItems: %s", err) 119 | } 120 | fmt.Println("Response from server for getting all keys", getAllKeysRes) 121 | 122 | // Delete Key 123 | deleteKeyRes, err := c.DeleteKey(context.Background(), keyGet) 124 | if err != nil { 125 | log.Fatalf("Error when calling DeleteKey: %s", err) 126 | } 127 | fmt.Println("Response from server after deleting a key", deleteKeyRes) 128 | 129 | // DeleteAll key 130 | deleteAllKeysResp, err := c.DeleteAll(context.Background(), &empty.Empty{}) 131 | if err != nil { 132 | log.Fatalf("Error when calling DeleteAll: %s", err) 133 | } 134 | fmt.Println("Response from server after deleting all keys", deleteAllKeysResp) 135 | 136 | // GetAllItems after deleting key 137 | _, err = c.GetAllItems(context.Background(), &empty.Empty{}) 138 | if err != nil { 139 | fmt.Println("Response from server after no key found", err.Error()) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/knrt10/grpc-cache 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/golang/protobuf v1.4.1 7 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect 8 | golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect 9 | google.golang.org/grpc v1.29.1 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 6 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 7 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 8 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 9 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 10 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 11 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 12 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 13 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 14 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 15 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 16 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 17 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 18 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 19 | github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= 20 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 21 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 22 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 23 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 24 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 25 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 26 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 27 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 28 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 29 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 30 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 31 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 32 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 33 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 34 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 35 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 36 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 37 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= 38 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 39 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 40 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 41 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 45 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 46 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 47 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 48 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 49 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 50 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 51 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 52 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 53 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 54 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 55 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 56 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 57 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 58 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 59 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 60 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 61 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 62 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 63 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 64 | google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= 65 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 66 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 67 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 68 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 69 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 70 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 71 | google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= 72 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 73 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 74 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 75 | -------------------------------------------------------------------------------- /helm-charts/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /helm-charts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: grpc-cache 3 | description: A Helm chart for Kubernetes 4 | 5 | type: application 6 | 7 | version: 0.1.0 8 | 9 | appVersion: 1.16.0 10 | -------------------------------------------------------------------------------- /helm-charts/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "helm-charts.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "helm-charts.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "helm-charts.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "helm-charts.labels" -}} 38 | helm.sh/chart: {{ include "helm-charts.chart" . }} 39 | {{ include "helm-charts.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "helm-charts.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "helm-charts.name" . }} 51 | app.kubernetes.io/instance: {{ .Release.Name }} 52 | {{- end }} 53 | 54 | {{/* 55 | Create the name of the service account to use 56 | */}} 57 | {{- define "helm-charts.serviceAccountName" -}} 58 | {{- if .Values.serviceAccount.create }} 59 | {{- default (include "helm-charts.fullname" .) .Values.serviceAccount.name }} 60 | {{- else }} 61 | {{- default "default" .Values.serviceAccount.name }} 62 | {{- end }} 63 | {{- end }} 64 | -------------------------------------------------------------------------------- /helm-charts/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Chart.Name }} 5 | namespace: {{ .Values.namespace }} 6 | spec: 7 | replicas: {{ .Values.replicaCount }} 8 | selector: 9 | matchLabels: 10 | name: {{ .Chart.Name }} 11 | template: 12 | metadata: 13 | labels: 14 | name: {{ .Chart.Name }} 15 | namespace: {{ .Values.namespace }} 16 | spec: 17 | containers: 18 | - name: {{ .Values.image.name | quote}} 19 | image: {{ .Values.image.repository | quote}} 20 | ports: 21 | - containerPort: 5001 22 | name: grpc 23 | 24 | 25 | -------------------------------------------------------------------------------- /helm-charts/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: {{ .Chart.Name }} 5 | namespace: {{ .Values.namespace }} 6 | annotations: 7 | {{- range $key, $val := .Values.ingress.annotations }} 8 | {{ $key }}: {{ $val | quote }} 9 | {{- end }} 10 | spec: 11 | rules: 12 | - host: {{ .Values.ingress.host }} 13 | http: 14 | paths: 15 | - backend: 16 | serviceName: {{ .Values.service.name }} 17 | servicePort: grpc 18 | -------------------------------------------------------------------------------- /helm-charts/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.service.name }} 5 | namespace: {{ .Values.namespace }} 6 | spec: 7 | selector: 8 | name: {{ .Values.service.name }} 9 | ports: 10 | - name: grpc 11 | port: {{ .Values.service.port }} 12 | targetPort: {{ .Values.service.targetPort }} 13 | -------------------------------------------------------------------------------- /helm-charts/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "helm-charts.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "helm-charts.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test-success 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "helm-charts.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /helm-charts/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for helm-charts. 2 | 3 | image: 4 | name: grpc-cache 5 | repository: knrt10/grpc-cache 6 | 7 | ingress: 8 | annotations: 9 | kubernetes.io/ingress.class: nginx 10 | nginx.ingress.kubernetes.io/backend-protocol: GRPC 11 | nginx.ingress.kubernetes.io/ssl-redirect: "true" 12 | host: grpc-cache.example.com 13 | 14 | namespace: grpc-cache 15 | 16 | replicaCount: 1 17 | 18 | service: 19 | name: grpc-cache 20 | port: 5001 21 | targetPort: 5001 22 | -------------------------------------------------------------------------------- /k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: grpc-cache 5 | namespace: grpc-cache 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | name: grpc-cache 11 | template: 12 | metadata: 13 | labels: 14 | name: grpc-cache 15 | namespace: grpc-cache 16 | spec: 17 | containers: 18 | - name: grpc-cache 19 | image: knrt10/grpc-cache 20 | ports: 21 | - containerPort: 5001 22 | name: grpc 23 | 24 | 25 | -------------------------------------------------------------------------------- /k8s/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: grpc-cache 5 | namespace: grpc-cache 6 | annotations: 7 | "kubernetes.io/ingress.class": "nginx" 8 | "nginx.ingress.kubernetes.io/backend-protocol": "GRPC" 9 | "nginx.ingress.kubernetes.io/ssl-redirect": "true" 10 | spec: 11 | rules: 12 | - host: grpc-cache.example.com 13 | http: 14 | paths: 15 | - backend: 16 | serviceName: grpc-cache 17 | servicePort: grpc 18 | -------------------------------------------------------------------------------- /k8s/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: grpc-cache 5 | -------------------------------------------------------------------------------- /k8s/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: grpc-cache 5 | namespace: grpc-cache 6 | spec: 7 | selector: 8 | name: grpc-cache 9 | ports: 10 | - name: grpc 11 | port: 5001 12 | targetPort: 5001 13 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kautilya Tripathi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /proto/cache-service.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: cache-service.proto 3 | 4 | package cacheService 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | empty "github.com/golang/protobuf/ptypes/empty" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | math "math" 15 | ) 16 | 17 | // Reference imports to suppress errors if they are not otherwise used. 18 | var _ = proto.Marshal 19 | var _ = fmt.Errorf 20 | var _ = math.Inf 21 | 22 | // This is a compile-time assertion to ensure that this generated file 23 | // is compatible with the proto package it is being compiled against. 24 | // A compilation error at this line likely means your copy of the 25 | // proto package needs to be updated. 26 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 27 | 28 | // Item is what is stored in the cache 29 | type Item struct { 30 | Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` 31 | Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` 32 | Expiration string `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` 33 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 34 | XXX_unrecognized []byte `json:"-"` 35 | XXX_sizecache int32 `json:"-"` 36 | } 37 | 38 | func (m *Item) Reset() { *m = Item{} } 39 | func (m *Item) String() string { return proto.CompactTextString(m) } 40 | func (*Item) ProtoMessage() {} 41 | func (*Item) Descriptor() ([]byte, []int) { 42 | return fileDescriptor_9b0ee6ef8b54c4e4, []int{0} 43 | } 44 | 45 | func (m *Item) XXX_Unmarshal(b []byte) error { 46 | return xxx_messageInfo_Item.Unmarshal(m, b) 47 | } 48 | func (m *Item) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 49 | return xxx_messageInfo_Item.Marshal(b, m, deterministic) 50 | } 51 | func (m *Item) XXX_Merge(src proto.Message) { 52 | xxx_messageInfo_Item.Merge(m, src) 53 | } 54 | func (m *Item) XXX_Size() int { 55 | return xxx_messageInfo_Item.Size(m) 56 | } 57 | func (m *Item) XXX_DiscardUnknown() { 58 | xxx_messageInfo_Item.DiscardUnknown(m) 59 | } 60 | 61 | var xxx_messageInfo_Item proto.InternalMessageInfo 62 | 63 | func (m *Item) GetKey() string { 64 | if m != nil { 65 | return m.Key 66 | } 67 | return "" 68 | } 69 | 70 | func (m *Item) GetValue() string { 71 | if m != nil { 72 | return m.Value 73 | } 74 | return "" 75 | } 76 | 77 | func (m *Item) GetExpiration() string { 78 | if m != nil { 79 | return m.Expiration 80 | } 81 | return "" 82 | } 83 | 84 | type GetKey struct { 85 | Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` 86 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 87 | XXX_unrecognized []byte `json:"-"` 88 | XXX_sizecache int32 `json:"-"` 89 | } 90 | 91 | func (m *GetKey) Reset() { *m = GetKey{} } 92 | func (m *GetKey) String() string { return proto.CompactTextString(m) } 93 | func (*GetKey) ProtoMessage() {} 94 | func (*GetKey) Descriptor() ([]byte, []int) { 95 | return fileDescriptor_9b0ee6ef8b54c4e4, []int{1} 96 | } 97 | 98 | func (m *GetKey) XXX_Unmarshal(b []byte) error { 99 | return xxx_messageInfo_GetKey.Unmarshal(m, b) 100 | } 101 | func (m *GetKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 102 | return xxx_messageInfo_GetKey.Marshal(b, m, deterministic) 103 | } 104 | func (m *GetKey) XXX_Merge(src proto.Message) { 105 | xxx_messageInfo_GetKey.Merge(m, src) 106 | } 107 | func (m *GetKey) XXX_Size() int { 108 | return xxx_messageInfo_GetKey.Size(m) 109 | } 110 | func (m *GetKey) XXX_DiscardUnknown() { 111 | xxx_messageInfo_GetKey.DiscardUnknown(m) 112 | } 113 | 114 | var xxx_messageInfo_GetKey proto.InternalMessageInfo 115 | 116 | func (m *GetKey) GetKey() string { 117 | if m != nil { 118 | return m.Key 119 | } 120 | return "" 121 | } 122 | 123 | type AllItems struct { 124 | Items []*Item `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` 125 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 126 | XXX_unrecognized []byte `json:"-"` 127 | XXX_sizecache int32 `json:"-"` 128 | } 129 | 130 | func (m *AllItems) Reset() { *m = AllItems{} } 131 | func (m *AllItems) String() string { return proto.CompactTextString(m) } 132 | func (*AllItems) ProtoMessage() {} 133 | func (*AllItems) Descriptor() ([]byte, []int) { 134 | return fileDescriptor_9b0ee6ef8b54c4e4, []int{2} 135 | } 136 | 137 | func (m *AllItems) XXX_Unmarshal(b []byte) error { 138 | return xxx_messageInfo_AllItems.Unmarshal(m, b) 139 | } 140 | func (m *AllItems) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 141 | return xxx_messageInfo_AllItems.Marshal(b, m, deterministic) 142 | } 143 | func (m *AllItems) XXX_Merge(src proto.Message) { 144 | xxx_messageInfo_AllItems.Merge(m, src) 145 | } 146 | func (m *AllItems) XXX_Size() int { 147 | return xxx_messageInfo_AllItems.Size(m) 148 | } 149 | func (m *AllItems) XXX_DiscardUnknown() { 150 | xxx_messageInfo_AllItems.DiscardUnknown(m) 151 | } 152 | 153 | var xxx_messageInfo_AllItems proto.InternalMessageInfo 154 | 155 | func (m *AllItems) GetItems() []*Item { 156 | if m != nil { 157 | return m.Items 158 | } 159 | return nil 160 | } 161 | 162 | type Success struct { 163 | Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` 164 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 165 | XXX_unrecognized []byte `json:"-"` 166 | XXX_sizecache int32 `json:"-"` 167 | } 168 | 169 | func (m *Success) Reset() { *m = Success{} } 170 | func (m *Success) String() string { return proto.CompactTextString(m) } 171 | func (*Success) ProtoMessage() {} 172 | func (*Success) Descriptor() ([]byte, []int) { 173 | return fileDescriptor_9b0ee6ef8b54c4e4, []int{3} 174 | } 175 | 176 | func (m *Success) XXX_Unmarshal(b []byte) error { 177 | return xxx_messageInfo_Success.Unmarshal(m, b) 178 | } 179 | func (m *Success) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 180 | return xxx_messageInfo_Success.Marshal(b, m, deterministic) 181 | } 182 | func (m *Success) XXX_Merge(src proto.Message) { 183 | xxx_messageInfo_Success.Merge(m, src) 184 | } 185 | func (m *Success) XXX_Size() int { 186 | return xxx_messageInfo_Success.Size(m) 187 | } 188 | func (m *Success) XXX_DiscardUnknown() { 189 | xxx_messageInfo_Success.DiscardUnknown(m) 190 | } 191 | 192 | var xxx_messageInfo_Success proto.InternalMessageInfo 193 | 194 | func (m *Success) GetSuccess() bool { 195 | if m != nil { 196 | return m.Success 197 | } 198 | return false 199 | } 200 | 201 | func init() { 202 | proto.RegisterType((*Item)(nil), "cacheService.Item") 203 | proto.RegisterType((*GetKey)(nil), "cacheService.GetKey") 204 | proto.RegisterType((*AllItems)(nil), "cacheService.AllItems") 205 | proto.RegisterType((*Success)(nil), "cacheService.Success") 206 | } 207 | 208 | func init() { proto.RegisterFile("cache-service.proto", fileDescriptor_9b0ee6ef8b54c4e4) } 209 | 210 | var fileDescriptor_9b0ee6ef8b54c4e4 = []byte{ 211 | // 307 bytes of a gzipped FileDescriptorProto 212 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xcd, 0x6a, 0x83, 0x40, 213 | 0x10, 0xc7, 0x31, 0x36, 0x5f, 0x93, 0x1c, 0xca, 0x36, 0x0d, 0x62, 0xa1, 0x04, 0x7b, 0xf1, 0x92, 214 | 0x15, 0xd2, 0x1e, 0x4a, 0x4b, 0x0f, 0xf6, 0x03, 0x29, 0x85, 0x52, 0xcc, 0x13, 0x18, 0x33, 0x49, 215 | 0xa5, 0x9b, 0x2a, 0xba, 0x86, 0xf8, 0xb6, 0x7d, 0x94, 0xb2, 0xbb, 0x18, 0x94, 0xe8, 0x6d, 0xe6, 216 | 0x37, 0xf3, 0xff, 0xcf, 0xee, 0x0c, 0x5c, 0x84, 0x41, 0xf8, 0x8d, 0xf3, 0x0c, 0xd3, 0x7d, 0x14, 217 | 0x22, 0x4d, 0xd2, 0x98, 0xc7, 0x64, 0x2c, 0xe1, 0x52, 0x31, 0xf3, 0x6a, 0x1b, 0xc7, 0x5b, 0x86, 218 | 0x8e, 0xac, 0xad, 0xf2, 0x8d, 0x83, 0xbb, 0x84, 0x17, 0xaa, 0xd5, 0xfa, 0x84, 0xb3, 0x77, 0x8e, 219 | 0x3b, 0x72, 0x0e, 0xfa, 0x0f, 0x16, 0x86, 0x36, 0xd3, 0xec, 0xa1, 0x2f, 0x42, 0x32, 0x81, 0xee, 220 | 0x3e, 0x60, 0x39, 0x1a, 0x1d, 0xc9, 0x54, 0x42, 0xae, 0x01, 0xf0, 0x90, 0x44, 0x69, 0xc0, 0xa3, 221 | 0xf8, 0xd7, 0xd0, 0x65, 0xa9, 0x42, 0x2c, 0x13, 0x7a, 0x1e, 0xf2, 0x0f, 0x2c, 0x4e, 0x1d, 0xad, 222 | 0x3b, 0x18, 0xb8, 0x8c, 0x89, 0x71, 0x19, 0xb1, 0xa1, 0x1b, 0x89, 0xc0, 0xd0, 0x66, 0xba, 0x3d, 223 | 0x5a, 0x10, 0x5a, 0x7d, 0x32, 0x15, 0x3d, 0xbe, 0x6a, 0xb0, 0x6e, 0xa0, 0xbf, 0xcc, 0xc3, 0x10, 224 | 0xb3, 0x8c, 0x18, 0xd0, 0xcf, 0x54, 0x28, 0x6d, 0x07, 0x7e, 0x99, 0x2e, 0xfe, 0x3a, 0x30, 0x7e, 225 | 0xa9, 0x38, 0x90, 0x39, 0xe8, 0xee, 0x7a, 0x4d, 0x1a, 0x7c, 0xcd, 0x06, 0x46, 0x1c, 0xd0, 0x3d, 226 | 0xe4, 0x64, 0x52, 0x2f, 0xa9, 0x9f, 0x34, 0x0a, 0x1e, 0x61, 0xe4, 0x21, 0x7f, 0x2e, 0xbe, 0x52, 227 | 0xdc, 0x44, 0x87, 0x16, 0xe1, 0xb4, 0x4e, 0x8f, 0x9f, 0x7f, 0x92, 0xe2, 0x63, 0x3a, 0xa5, 0xea, 228 | 0x42, 0xb4, 0xbc, 0x10, 0x7d, 0x13, 0x17, 0x6a, 0x95, 0xdf, 0xc3, 0xf0, 0x15, 0x19, 0x72, 0x14, 229 | 0x6b, 0x6e, 0x9e, 0x7c, 0x59, 0xa7, 0xe5, 0x02, 0x1f, 0x4a, 0xa5, 0xcb, 0x58, 0xeb, 0xd8, 0x66, 230 | 0xed, 0xaa, 0x27, 0xdb, 0x6e, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x18, 0xca, 0xb9, 0x72, 231 | 0x02, 0x00, 0x00, 232 | } 233 | 234 | // Reference imports to suppress errors if they are not otherwise used. 235 | var _ context.Context 236 | var _ grpc.ClientConn 237 | 238 | // This is a compile-time assertion to ensure that this generated file 239 | // is compatible with the grpc package it is being compiled against. 240 | const _ = grpc.SupportPackageIsVersion4 241 | 242 | // CacheServiceClient is the client API for CacheService service. 243 | // 244 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 245 | type CacheServiceClient interface { 246 | Add(ctx context.Context, in *Item, opts ...grpc.CallOption) (*Item, error) 247 | Get(ctx context.Context, in *GetKey, opts ...grpc.CallOption) (*Item, error) 248 | GetByPrefix(ctx context.Context, in *GetKey, opts ...grpc.CallOption) (*AllItems, error) 249 | GetAllItems(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*AllItems, error) 250 | DeleteKey(ctx context.Context, in *GetKey, opts ...grpc.CallOption) (*Success, error) 251 | DeleteAll(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*Success, error) 252 | } 253 | 254 | type cacheServiceClient struct { 255 | cc *grpc.ClientConn 256 | } 257 | 258 | func NewCacheServiceClient(cc *grpc.ClientConn) CacheServiceClient { 259 | return &cacheServiceClient{cc} 260 | } 261 | 262 | func (c *cacheServiceClient) Add(ctx context.Context, in *Item, opts ...grpc.CallOption) (*Item, error) { 263 | out := new(Item) 264 | err := c.cc.Invoke(ctx, "/cacheService.CacheService/Add", in, out, opts...) 265 | if err != nil { 266 | return nil, err 267 | } 268 | return out, nil 269 | } 270 | 271 | func (c *cacheServiceClient) Get(ctx context.Context, in *GetKey, opts ...grpc.CallOption) (*Item, error) { 272 | out := new(Item) 273 | err := c.cc.Invoke(ctx, "/cacheService.CacheService/Get", in, out, opts...) 274 | if err != nil { 275 | return nil, err 276 | } 277 | return out, nil 278 | } 279 | 280 | func (c *cacheServiceClient) GetByPrefix(ctx context.Context, in *GetKey, opts ...grpc.CallOption) (*AllItems, error) { 281 | out := new(AllItems) 282 | err := c.cc.Invoke(ctx, "/cacheService.CacheService/GetByPrefix", in, out, opts...) 283 | if err != nil { 284 | return nil, err 285 | } 286 | return out, nil 287 | } 288 | 289 | func (c *cacheServiceClient) GetAllItems(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*AllItems, error) { 290 | out := new(AllItems) 291 | err := c.cc.Invoke(ctx, "/cacheService.CacheService/GetAllItems", in, out, opts...) 292 | if err != nil { 293 | return nil, err 294 | } 295 | return out, nil 296 | } 297 | 298 | func (c *cacheServiceClient) DeleteKey(ctx context.Context, in *GetKey, opts ...grpc.CallOption) (*Success, error) { 299 | out := new(Success) 300 | err := c.cc.Invoke(ctx, "/cacheService.CacheService/DeleteKey", in, out, opts...) 301 | if err != nil { 302 | return nil, err 303 | } 304 | return out, nil 305 | } 306 | 307 | func (c *cacheServiceClient) DeleteAll(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*Success, error) { 308 | out := new(Success) 309 | err := c.cc.Invoke(ctx, "/cacheService.CacheService/DeleteAll", in, out, opts...) 310 | if err != nil { 311 | return nil, err 312 | } 313 | return out, nil 314 | } 315 | 316 | // CacheServiceServer is the server API for CacheService service. 317 | type CacheServiceServer interface { 318 | Add(context.Context, *Item) (*Item, error) 319 | Get(context.Context, *GetKey) (*Item, error) 320 | GetByPrefix(context.Context, *GetKey) (*AllItems, error) 321 | GetAllItems(context.Context, *empty.Empty) (*AllItems, error) 322 | DeleteKey(context.Context, *GetKey) (*Success, error) 323 | DeleteAll(context.Context, *empty.Empty) (*Success, error) 324 | } 325 | 326 | // UnimplementedCacheServiceServer can be embedded to have forward compatible implementations. 327 | type UnimplementedCacheServiceServer struct { 328 | } 329 | 330 | func (*UnimplementedCacheServiceServer) Add(ctx context.Context, req *Item) (*Item, error) { 331 | return nil, status.Errorf(codes.Unimplemented, "method Add not implemented") 332 | } 333 | func (*UnimplementedCacheServiceServer) Get(ctx context.Context, req *GetKey) (*Item, error) { 334 | return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") 335 | } 336 | func (*UnimplementedCacheServiceServer) GetByPrefix(ctx context.Context, req *GetKey) (*AllItems, error) { 337 | return nil, status.Errorf(codes.Unimplemented, "method GetByPrefix not implemented") 338 | } 339 | func (*UnimplementedCacheServiceServer) GetAllItems(ctx context.Context, req *empty.Empty) (*AllItems, error) { 340 | return nil, status.Errorf(codes.Unimplemented, "method GetAllItems not implemented") 341 | } 342 | func (*UnimplementedCacheServiceServer) DeleteKey(ctx context.Context, req *GetKey) (*Success, error) { 343 | return nil, status.Errorf(codes.Unimplemented, "method DeleteKey not implemented") 344 | } 345 | func (*UnimplementedCacheServiceServer) DeleteAll(ctx context.Context, req *empty.Empty) (*Success, error) { 346 | return nil, status.Errorf(codes.Unimplemented, "method DeleteAll not implemented") 347 | } 348 | 349 | func RegisterCacheServiceServer(s *grpc.Server, srv CacheServiceServer) { 350 | s.RegisterService(&_CacheService_serviceDesc, srv) 351 | } 352 | 353 | func _CacheService_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 354 | in := new(Item) 355 | if err := dec(in); err != nil { 356 | return nil, err 357 | } 358 | if interceptor == nil { 359 | return srv.(CacheServiceServer).Add(ctx, in) 360 | } 361 | info := &grpc.UnaryServerInfo{ 362 | Server: srv, 363 | FullMethod: "/cacheService.CacheService/Add", 364 | } 365 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 366 | return srv.(CacheServiceServer).Add(ctx, req.(*Item)) 367 | } 368 | return interceptor(ctx, in, info, handler) 369 | } 370 | 371 | func _CacheService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 372 | in := new(GetKey) 373 | if err := dec(in); err != nil { 374 | return nil, err 375 | } 376 | if interceptor == nil { 377 | return srv.(CacheServiceServer).Get(ctx, in) 378 | } 379 | info := &grpc.UnaryServerInfo{ 380 | Server: srv, 381 | FullMethod: "/cacheService.CacheService/Get", 382 | } 383 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 384 | return srv.(CacheServiceServer).Get(ctx, req.(*GetKey)) 385 | } 386 | return interceptor(ctx, in, info, handler) 387 | } 388 | 389 | func _CacheService_GetByPrefix_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 390 | in := new(GetKey) 391 | if err := dec(in); err != nil { 392 | return nil, err 393 | } 394 | if interceptor == nil { 395 | return srv.(CacheServiceServer).GetByPrefix(ctx, in) 396 | } 397 | info := &grpc.UnaryServerInfo{ 398 | Server: srv, 399 | FullMethod: "/cacheService.CacheService/GetByPrefix", 400 | } 401 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 402 | return srv.(CacheServiceServer).GetByPrefix(ctx, req.(*GetKey)) 403 | } 404 | return interceptor(ctx, in, info, handler) 405 | } 406 | 407 | func _CacheService_GetAllItems_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 408 | in := new(empty.Empty) 409 | if err := dec(in); err != nil { 410 | return nil, err 411 | } 412 | if interceptor == nil { 413 | return srv.(CacheServiceServer).GetAllItems(ctx, in) 414 | } 415 | info := &grpc.UnaryServerInfo{ 416 | Server: srv, 417 | FullMethod: "/cacheService.CacheService/GetAllItems", 418 | } 419 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 420 | return srv.(CacheServiceServer).GetAllItems(ctx, req.(*empty.Empty)) 421 | } 422 | return interceptor(ctx, in, info, handler) 423 | } 424 | 425 | func _CacheService_DeleteKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 426 | in := new(GetKey) 427 | if err := dec(in); err != nil { 428 | return nil, err 429 | } 430 | if interceptor == nil { 431 | return srv.(CacheServiceServer).DeleteKey(ctx, in) 432 | } 433 | info := &grpc.UnaryServerInfo{ 434 | Server: srv, 435 | FullMethod: "/cacheService.CacheService/DeleteKey", 436 | } 437 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 438 | return srv.(CacheServiceServer).DeleteKey(ctx, req.(*GetKey)) 439 | } 440 | return interceptor(ctx, in, info, handler) 441 | } 442 | 443 | func _CacheService_DeleteAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 444 | in := new(empty.Empty) 445 | if err := dec(in); err != nil { 446 | return nil, err 447 | } 448 | if interceptor == nil { 449 | return srv.(CacheServiceServer).DeleteAll(ctx, in) 450 | } 451 | info := &grpc.UnaryServerInfo{ 452 | Server: srv, 453 | FullMethod: "/cacheService.CacheService/DeleteAll", 454 | } 455 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 456 | return srv.(CacheServiceServer).DeleteAll(ctx, req.(*empty.Empty)) 457 | } 458 | return interceptor(ctx, in, info, handler) 459 | } 460 | 461 | var _CacheService_serviceDesc = grpc.ServiceDesc{ 462 | ServiceName: "cacheService.CacheService", 463 | HandlerType: (*CacheServiceServer)(nil), 464 | Methods: []grpc.MethodDesc{ 465 | { 466 | MethodName: "Add", 467 | Handler: _CacheService_Add_Handler, 468 | }, 469 | { 470 | MethodName: "Get", 471 | Handler: _CacheService_Get_Handler, 472 | }, 473 | { 474 | MethodName: "GetByPrefix", 475 | Handler: _CacheService_GetByPrefix_Handler, 476 | }, 477 | { 478 | MethodName: "GetAllItems", 479 | Handler: _CacheService_GetAllItems_Handler, 480 | }, 481 | { 482 | MethodName: "DeleteKey", 483 | Handler: _CacheService_DeleteKey_Handler, 484 | }, 485 | { 486 | MethodName: "DeleteAll", 487 | Handler: _CacheService_DeleteAll_Handler, 488 | }, 489 | }, 490 | Streams: []grpc.StreamDesc{}, 491 | Metadata: "cache-service.proto", 492 | } 493 | -------------------------------------------------------------------------------- /proto/cache-service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package cacheService; 4 | 5 | import "google/protobuf/empty.proto"; 6 | 7 | // Item is what is stored in the cache 8 | message Item { 9 | string key = 1; 10 | string value = 2; 11 | string expiration = 3; 12 | } 13 | 14 | message GetKey { 15 | string key = 1; 16 | } 17 | 18 | message AllItems { 19 | repeated Item items = 1; 20 | } 21 | 22 | message Success { 23 | bool success = 1; 24 | } 25 | 26 | service CacheService { 27 | rpc Add (Item) returns (Item); 28 | rpc Get (GetKey) returns (Item); 29 | rpc GetByPrefix(GetKey) returns (AllItems); 30 | rpc GetAllItems(google.protobuf.Empty) returns (AllItems); 31 | rpc DeleteKey(GetKey) returns (Success); 32 | rpc DeleteAll(google.protobuf.Empty) returns (Success); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | > In memory cache, using gRPC 6 | 7 | [![Build Status](https://travis-ci.org/knrt10/gRPC-cache.svg?branch=master)](https://travis-ci.org/knrt10/gRPC-cache) 8 | [![Coverage Status](https://coveralls.io/repos/github/knrt10/gRPC-cache/badge.svg)](https://coveralls.io/github/knrt10/gRPC-cache) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/knrt10/grpc-Cache)](https://goreportcard.com/report/github.com/knrt10/grpc-Cache) 10 | [![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/knrt10/gRPC-cache/api/server) 11 | 12 | ## Contents 13 | 14 | - [About](#about) 15 | - [Running Server](#running-server) 16 | - [Local](#local) 17 | - [Docker](#docker) 18 | - [Kubernetes](#kubernetes) 19 | - [Example](#example) 20 | - [Helm](#helm) 21 | - [Terraform](#terraform) 22 | - [API](#api) 23 | - [Add](#add) 24 | - [Get](#get) 25 | - [GetByPrefix](#getByPrefix) 26 | - [GetAllItems](#getallitems) 27 | - [DeleteKey](#deletekey) 28 | - [DeleteAll](#deleteall) 29 | - [Testing](#testing) 30 | - [Development](#development) 31 | - [Example](#example) 32 | 33 | ## About 34 | 35 | Go in memory cache using gRPC to generate API. Functionalities include 36 | 37 | - Adding/Replacing key/value 38 | - Getting value using a key 39 | - Getting all keys 40 | - Deleting a particular key 41 | - Deleting all keys 42 | - **Concurrency safe** and on client side calls can be made using goroutines 43 | 44 | ## Running server 45 | 46 | You can run server locally the following ways 47 | 48 | - Using go run 49 | - Docker 50 | - Kubernetes 51 | - Helm 52 | - Terraform 53 | 54 | ### Local 55 | 56 | You can run the server locally but make sure you have the following requirements 57 | 58 | - [Golang min-version(1.11)](https://golang.org/) 59 | - make 60 | 61 | Now you can start the server using below commands 62 | 63 | ```bash 64 | Start your server `./server-cache` or `make server` 65 | `./server-cache -addr=":5001"` to run server on port `5001` 66 | ``` 67 | 68 | ### Docker 69 | 70 | Make sure you have following installed 71 | 72 | - [Docker](https://www.docker.com/) installed 73 | 74 | After the basic requirements are met, you can easily run server from below command. 75 | 76 | - Run this command `make docker` // This will build the image 77 | - Run the server using `make dockerServer` 78 | 79 | After running the server, start your client `./client-cache` or `make client` in a different terminal 80 | 81 | `./client-cache -addr=":5001"` is server is running running on port `5001` 82 | 83 | ### Kubernetes 84 | 85 | You need to have following things installed on your OS to run server using kubernetes 86 | 87 | - [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) installed locally 88 | - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed locally 89 | - [grpcurl](https://github.com/fullstorydev/grpcurl) for testing server running on k8s 90 | 91 | **Important**:- `Make sure your ingress-controller in running`. To enable in minikube 92 | 93 | `minikube addons enable ingress` 94 | 95 | You can run server on your kubernetes cluster. All the resources are created on `grpc-cache` namespace. Before running the command below make sure your cluster is up and running. 96 | 97 | - Run this command `make run-k8s-server` 98 | 99 | This will create all the required resources needed to run your `grpc server`. You will be asked for passowrd once, to enter ingress address to host mapping automatically to `/etc/hosts` 100 | 101 | #### Example 102 | 103 | ```bash 104 | # To list all services exposed by a server, use the "list" verb. 105 | grpcurl --insecure grpc-cache.example.com:443 list 106 | 107 | # The "describe" verb will print the type of any symbol that the server knows about or that is found in a given protoset file. It also prints a description of that symbol, in the form of snippets of proto source. It won't necessarily be the original source that defined the element, but it will be equivalent. 108 | grpcurl --insecure grpc-cache.example.com:443 list cacheService.CacheService 109 | 110 | # To add a key 111 | grpcurl --insecure -d '{"key": "knrt10", "value": "pro", "expiration": "3m"}' grpc-cache.example.com:443 cacheService.CacheService/Add 112 | 113 | # To get key 114 | grpcurl --insecure -d '{"key": "knrt10"}' grpc-cache.example.com:443 cacheService.CacheService/Get 115 | 116 | # To get all keys 117 | grpcurl --insecure grpc-cache.example.com:443 cacheService.CacheService/GetAllItems 118 | ``` 119 | 120 | Similarly you can use all the methods as shown in API below 121 | 122 | ### Helm 123 | 124 | You need to have following things installed on your OS to run server using helm 125 | 126 | - [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) installed locally 127 | - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed locally 128 | - [helm](https://helm.sh/) installed locally 129 | - [grpcurl](https://github.com/fullstorydev/grpcurl) for testing server running on k8s 130 | 131 | **Important**:- `Make sure your ingress-controller in running`. To enable in minikube 132 | 133 | `minikube addons enable ingress` 134 | 135 | Just run `make run-helm-server` and it will deploy your application to your kubernetes cluster. This will create all the required resources needed to run your `grpc server`. You will be asked for passowrd once, to enter ingress address to host mapping automatically to `/etc/hosts` 136 | 137 | Now you can easily test it using [grpcurl](https://github.com/fullstorydev/grpcurl). For API usage you can refer to [example](#example) 138 | 139 | ### Terraform 140 | 141 | You need to have following things installed on your OS to run server using terraform 142 | 143 | - [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) installed locally 144 | - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed locally 145 | - [Terraform](https://www.terraform.io) installed locally 146 | - [grpcurl](https://github.com/fullstorydev/grpcurl) for testing server running on k8s 147 | 148 | **Important**:- `Make sure your ingress-controller in running`. To enable in minikube 149 | 150 | `minikube addons enable ingress` 151 | 152 | Running with Terraform is the most easiest way. Just run `make run-terraform-server` and it will deploy your application to your kubernetes cluster. 153 | 154 | Now you can easily test it using [grpcurl](https://github.com/fullstorydev/grpcurl). For API usage you can refer to [example](#example) 155 | 156 | ## API 157 | 158 | Proto syntax `proto3` is used. You can find the [proto file here](https://github.com/knrt10/gRPC-cache/tree/master/proto/cache-service.proto) 159 | 160 | ### Add 161 | 162 | This is used to add key/value to the cache 163 | 164 | ```go 165 | func (c Cache) Add(ctx context.Context, item *api.Item) (*api.Item, error) 166 | ``` 167 | 168 | ### Get 169 | 170 | This is used to get key value pair for a particular key 171 | 172 | ```go 173 | func (c Cache) Get(ctx context.Context, args *api.GetKey) (*api.Item, error) 174 | ``` 175 | 176 | ### GetByPrefix 177 | 178 | Used to get all key value pairs by prefix 179 | 180 | ```go 181 | func (c Cache) GetByPrefix(ctx context.Context, args *api.GetKey) (*api.AllItems, error) 182 | ``` 183 | 184 | ### GetAllItems 185 | 186 | Used to get all key value pairs 187 | 188 | ```go 189 | func (c Cache) GetAllItems(ctx context.Context, in *empty.Empty) (*api.AllItems, error) 190 | ``` 191 | 192 | ### DeleteKey 193 | 194 | Used to delete item by a particular key from the cache 195 | 196 | ```go 197 | func (c Cache) DeleteKey(ctx context.Context, args *api.GetKey) (*api.Success, error) 198 | ``` 199 | 200 | ### DeleteAll 201 | 202 | Used to clear the whole cache 203 | 204 | ```go 205 | func (c Cache) DeleteAll(ctx context.Context, in *empty.Empty) (*api.Success, error) 206 | ``` 207 | 208 | ## Testing 209 | 210 | After running `make build` just run `make test` to run the tests. It has **coverage of 92.7%** 211 | 212 | ```bash 213 | go test api/server/* -v -cover -race 214 | === RUN TestAdd 215 | --- PASS: TestAdd (0.03s) 216 | === RUN TestGet 217 | --- PASS: TestGet (0.01s) 218 | === RUN TestGetByPrefix 219 | --- PASS: TestGetByPrefix (0.01s) 220 | === RUN TestGetAllItems 221 | --- PASS: TestGetAllItems (0.01s) 222 | === RUN TestDeleteKey 223 | --- PASS: TestDeleteKey (0.00s) 224 | === RUN TestDeleteAll 225 | --- PASS: TestDeleteAll (0.00s) 226 | === RUN TestGetDeletedKey 227 | --- PASS: TestGetDeletedKey (0.01s) 228 | === RUN TestDeleteKeyByExpiration 229 | --- PASS: TestDeleteKeyByExpiration (2.01s) 230 | PASS 231 | coverage: 92.7% of statements 232 | ok command-line-arguments 3.709s coverage: 92.7% of statements 233 | ``` 234 | 235 | ## Development 236 | 237 | You can develop and debug the application locally, but you need to have below softwares installed locally 238 | 239 | - [Golang min-version(1.11)](https://golang.org/) 240 | - make 241 | - [protobuf](https://github.com/golang/protobuf) 242 | 243 | 244 | Once you've cloned this repo, run these commands in this directory: 245 | 246 | ```bash 247 | # Only needed the first time: 248 | $ make build 249 | 250 | # Then run to start server 251 | $ ./server-cache --help 252 | 253 | Usage of ./server-cache: 254 | -addr string 255 | Address on which you want to run server (default ":5001") 256 | -cln int 257 | Cleanup interval duration of expired cache is 5 min (default 5) 258 | -exp int 259 | Default expiration duration of cache is 10 min (default 10) 260 | 261 | # To use client 262 | $ ./client-cache --help 263 | 264 | Usage of ./client-cache: 265 | -addr string 266 | Address on which you want to run server (default ":5001") 267 | ``` 268 | 269 | ## Example 270 | 271 | Please refer to [examples](https://github.com/knrt10/gRPC-cache/tree/master/examples) directory for more information 272 | -------------------------------------------------------------------------------- /scripts/build-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker build -t knrt10/grpc-cache -f api/Dockerfile . 4 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go build -o server-cache api/main.go && go build -o client-cache examples/client.go 3 | -------------------------------------------------------------------------------- /scripts/protoc-gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | protoc --proto_path=proto --go_out=plugins=grpc:proto cache-service.proto 4 | -------------------------------------------------------------------------------- /scripts/run-helm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # check namespace if present 4 | { 5 | NAMESPACE_EXIST=$(kubectl get ns grpc-cache -o jsonpath='{.metadata.name}') 6 | } &> /dev/null 7 | 8 | if [ "$NAMESPACE_EXIST" != "grpc-cache" ]; then 9 | kubectl create ns grpc-cache 10 | fi 11 | 12 | # set current context namespace 13 | kubectl config set-context $(kubectl config current-context) --namespace=grpc-cache 14 | 15 | # create resources for k8s 16 | helm install grpc-cache ./helm-charts 17 | 18 | HOST_INGRESS=$(kubectl get ingress grpc-cache -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 19 | 20 | # setup host 21 | while true; do 22 | if [ "$HOST_INGRESS" != "" ]; then 23 | echo "Ingress address configured to: $HOST_INGRESS" 24 | break 25 | fi 26 | echo "Waiting for ingress to configure, sleeping for 10 seconds" 27 | HOST_INGRESS=$(kubectl get ingress grpc-cache -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 28 | sleep 10 29 | done 30 | 31 | ADDRESS_MAP_EXIST=$(grep "$HOST_INGRESS grpc-cache.example.com" /etc/hosts) 32 | 33 | if [ "$ADDRESS_MAP_EXIST" == "" ]; then 34 | echo "Adding ingress host mapping to /etc/hosts" 35 | sudo -- sh -c -e "echo '$HOST_INGRESS grpc-cache.example.com' >> /etc/hosts"; 36 | fi 37 | 38 | echo "Started all resources successfully, you can use the application now." 39 | -------------------------------------------------------------------------------- /scripts/run-k8-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # create resources for k8s 4 | kubectl apply -f ./k8s/namespace.yaml 5 | kubectl apply -f ./k8s/service.yaml 6 | kubectl apply -f ./k8s/deployment.yaml 7 | kubectl apply -f ./k8s/ingress.yaml 8 | 9 | # set current context namespace 10 | kubectl config set-context $(kubectl config current-context) --namespace=grpc-cache 11 | 12 | HOST_INGRESS=$(kubectl get ingress grpc-cache -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 13 | 14 | # setup host 15 | while true; do 16 | if [ "$HOST_INGRESS" != "" ]; then 17 | echo "Ingress address configured to: $HOST_INGRESS" 18 | break 19 | fi 20 | echo "Waiting for ingress to configure, sleeping for 10 seconds" 21 | HOST_INGRESS=$(kubectl get ingress grpc-cache -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 22 | sleep 10 23 | done 24 | 25 | ADDRESS_MAP_EXIST=$(grep "$HOST_INGRESS grpc-cache.example.com" /etc/hosts) 26 | 27 | if [ "$ADDRESS_MAP_EXIST" == "" ]; then 28 | echo "Adding ingress host mapping to /etc/hosts" 29 | sudo -- sh -c -e "echo '$HOST_INGRESS grpc-cache.example.com' >> /etc/hosts"; 30 | fi 31 | 32 | echo "Started all resources successfully, you can use the application now." 33 | -------------------------------------------------------------------------------- /scripts/run-terraform.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | terraform init terraform/ 4 | 5 | terraform apply -auto-approve terraform 6 | 7 | HOST_INGRESS=$(terraform output ingress_host_ip) 8 | 9 | ADDRESS_MAP_EXIST=$(grep "$HOST_INGRESS grpc-cache.example.com" /etc/hosts) 10 | 11 | if [ "$ADDRESS_MAP_EXIST" == "" ]; then 12 | echo "Adding ingress host mapping to /etc/hosts" 13 | sudo -- sh -c -e "echo '$HOST_INGRESS grpc-cache.example.com' >> /etc/hosts"; 14 | fi 15 | 16 | echo "Started all resources successfully, you can use the application now." 17 | -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | provider "kubernetes" {} 2 | 3 | # create namespace 4 | resource "kubernetes_namespace" "grpc-cache" { 5 | metadata { 6 | name = var.kube_defaultspace 7 | } 8 | } 9 | 10 | # create deployment 11 | resource "kubernetes_deployment" "grpc-cache" { 12 | metadata { 13 | name = "grpc-cache" 14 | namespace = var.kube_defaultspace 15 | } 16 | 17 | spec { 18 | replicas = var.kube_deployment_replica 19 | 20 | selector { 21 | match_labels = { 22 | name = "grpc-cache" 23 | } 24 | } 25 | 26 | template { 27 | metadata { 28 | labels = { 29 | name = "grpc-cache" 30 | } 31 | namespace = var.kube_defaultspace 32 | } 33 | 34 | spec { 35 | container { 36 | name = var.image_name 37 | image = var.image_repository 38 | port { 39 | container_port = 5001 40 | name = "grpc" 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | # create service 49 | 50 | resource "kubernetes_service" "grpc-cache" { 51 | metadata { 52 | name = "grpc-cache" 53 | namespace = var.kube_defaultspace 54 | } 55 | 56 | spec { 57 | selector = { 58 | name = "grpc-cache" 59 | } 60 | 61 | port { 62 | port = 5001 63 | target_port = 5001 64 | name = "grpc" 65 | } 66 | } 67 | } 68 | 69 | # create ingress 70 | 71 | resource "kubernetes_ingress" "grpc-cache" { 72 | metadata { 73 | name = "grpc-cache" 74 | namespace = var.kube_defaultspace 75 | annotations = { 76 | "kubernetes.io/ingress.class" = "nginx" 77 | "nginx.ingress.kubernetes.io/backend-protocol" = "GRPC" 78 | "nginx.ingress.kubernetes.io/ssl-redirect" = "true" 79 | } 80 | } 81 | 82 | wait_for_load_balancer = true 83 | 84 | spec { 85 | rule { 86 | host = "grpc-cache.example.com" 87 | http { 88 | path { 89 | backend { 90 | service_name = "grpc-cache" 91 | service_port = 5001 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | 2 | output "ingress_host_ip" { 3 | value = kubernetes_ingress.grpc-cache.load_balancer_ingress.0.ip 4 | description = "IP mapping of ingress to host" 5 | } 6 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "image_name" { 2 | type = string 3 | default = "grpc-cache" 4 | } 5 | 6 | variable "image_repository" { 7 | type = string 8 | default = "knrt10/grpc-cache" 9 | } 10 | 11 | variable "kube_defaultspace" { 12 | type = string 13 | default = "grpc-cache" 14 | } 15 | 16 | variable "kube_deployment_replica" { 17 | type = number 18 | default = 1 19 | } 20 | 21 | 22 | --------------------------------------------------------------------------------