diff --git a/kubernetes-manifests/cartservice.yaml b/kubernetes-manifests/cartservice.yaml new file mode 100644 index 0000000..83cb82d --- /dev/null +++ b/kubernetes-manifests/cartservice.yaml @@ -0,0 +1,39 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: cartservice +spec: + template: + metadata: + labels: + app: cartservice + spec: + containers: + - name: server + image: cartservice + ports: + - containerPort: 7070 + env: + - name: REDIS_ADDR + value: "redis-cart:6379" + - name: CART_SERVICE_PORT + value: "7070" + resources: + requests: + cpu: 200m + memory: 64Mi + limits: + cpu: 300m + memory: 128Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: cartservice +spec: + type: ClusterIP + selector: + app: cartservice + ports: + - port: 7070 + targetPort: 7070 diff --git a/kubernetes-manifests/checkoutservice.yaml b/kubernetes-manifests/checkoutservice.yaml index 0c05a5c..5bc6ec9 100644 --- a/kubernetes-manifests/checkoutservice.yaml +++ b/kubernetes-manifests/checkoutservice.yaml @@ -25,7 +25,7 @@ spec: - name: CURRENCY_SERVICE_ADDR value: "currencyservice:7000" - name: CART_SERVICE_ADDR - value: "cartservice:9999" + value: "cartservice:7070" resources: requests: cpu: 100m diff --git a/skaffold.yaml b/skaffold.yaml index 9f37978..366200c 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -16,6 +16,8 @@ build: workspace: src/paymentservice - imageName: currencyservice workspace: src/currencyservice + - imageName: cartservice + workspace: src/cartservice deploy: kubectl: manifests: diff --git a/src/frontend/handlers.go b/src/frontend/handlers.go index 410dd7c..e0893f8 100644 --- a/src/frontend/handlers.go +++ b/src/frontend/handlers.go @@ -43,15 +43,20 @@ func ensureSessionID(next http.HandlerFunc) http.HandlerFunc { } func (fe *frontendServer) homeHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("[home] session_id=%+v", r.Context().Value(ctxKeySessionID{})) + log.Printf("[home] session_id=%+v", sessionID(r)) currencies, err := fe.getCurrencies(r.Context()) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("could not retrieve currencies: %+v", err), http.StatusInternalServerError) return } products, err := fe.getProducts(r.Context()) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("could not retrieve products: %+v", err), http.StatusInternalServerError) + return + } + cart, err := fe.getCart(r.Context(), sessionID(r)) + if err != nil { + http.Error(w, fmt.Sprintf("could not retrieve cart: %+v", err), http.StatusInternalServerError) return } @@ -76,7 +81,8 @@ func (fe *frontendServer) homeHandler(w http.ResponseWriter, r *http.Request) { "user_currency": currentCurrency(r), "currencies": currencies, "products": ps, - "session_id": r.Context().Value(ctxKeySessionID{}), + "session_id": sessionID(r), + "cart_size": len(cart), }); err != nil { log.Println(err) } @@ -97,7 +103,7 @@ func (fe *frontendServer) productHandler(w http.ResponseWriter, r *http.Request) currencies, err := fe.getCurrencies(r.Context()) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("could not retrieve currencies: %+v", err), http.StatusInternalServerError) return } @@ -118,14 +124,14 @@ func (fe *frontendServer) productHandler(w http.ResponseWriter, r *http.Request) "user_currency": currentCurrency(r), "currencies": currencies, "product": product, - "session_id": r.Context().Value(ctxKeySessionID{}), + "session_id": sessionID(r), }); err != nil { log.Println(err) } } func (fe *frontendServer) logoutHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("[home] session_id=%+v", r.Context().Value(ctxKeySessionID{})) + log.Printf("[home] session_id=%+v", sessionID(r)) for _, c := range r.Cookies() { c.Expires = time.Now().Add(-time.Hour * 24 * 365) c.MaxAge = -1 @@ -137,7 +143,7 @@ func (fe *frontendServer) logoutHandler(w http.ResponseWriter, r *http.Request) func (fe *frontendServer) setCurrencyHandler(w http.ResponseWriter, r *http.Request) { cur := r.FormValue("currency_code") - log.Printf("[setCurrency] session_id=%+v code=%s", r.Context().Value(ctxKeySessionID{}), cur) + log.Printf("[setCurrency] session_id=%+v code=%s", sessionID(r), cur) if cur != "" { http.SetCookie(w, &http.Cookie{ Name: cookieCurrency, @@ -160,3 +166,11 @@ func currentCurrency(r *http.Request) string { } return defaultCurrency } + +func sessionID(r *http.Request) string { + v := r.Context().Value(ctxKeySessionID{}) + if v != nil { + return v.(string) + } + return "" +} diff --git a/src/frontend/main.go b/src/frontend/main.go index 4ca4d03..6b3584d 100644 --- a/src/frontend/main.go +++ b/src/frontend/main.go @@ -50,7 +50,7 @@ func main() { svc := new(frontendServer) mustMapEnv(&svc.productCatalogSvcAddr, "PRODUCT_CATALOG_SERVICE_ADDR") mustMapEnv(&svc.currencySvcAddr, "CURRENCY_SERVICE_ADDR") - // mustMapEnv(&svc.cartSvcAddr, "CART_SERVICE_ADDR") + mustMapEnv(&svc.cartSvcAddr, "CART_SERVICE_ADDR") var err error svc.currencySvcConn, err = grpc.DialContext(ctx, svc.currencySvcAddr, grpc.WithInsecure()) @@ -61,6 +61,10 @@ func main() { if err != nil { log.Fatalf("failed to connect productcatalog service: %+v", err) } + svc.cartSvcConn, err = grpc.DialContext(ctx, svc.cartSvcAddr, grpc.WithInsecure()) + if err != nil { + log.Fatalf("failed to connect cart service at %s: %+v", svc.cartSvcAddr, err) + } r := mux.NewRouter() r.HandleFunc("/", ensureSessionID(svc.homeHandler)).Methods(http.MethodGet, http.MethodHead) diff --git a/src/frontend/rpc.go b/src/frontend/rpc.go index 1a08956..54c32bf 100644 --- a/src/frontend/rpc.go +++ b/src/frontend/rpc.go @@ -3,7 +3,11 @@ package main import ( "context" + "google.golang.org/grpc/codes" + pb "frontend/genproto" + + "google.golang.org/grpc/status" ) const ( @@ -37,6 +41,15 @@ func (fe *frontendServer) getProduct(ctx context.Context, id string) (*pb.Produc return resp, err } +func (fe *frontendServer) getCart(ctx context.Context, userID string) ([]*pb.CartItem, error) { + resp, err := pb.NewCartServiceClient(fe.cartSvcConn).GetCart(ctx, &pb.GetCartRequest{UserId: userID}) + if status.Code(err) == codes.Canceled { + // TODO(ahmetb) remove this workaround when cartservice returns ok response to GetCart() with non-existing users + return nil, nil + } + return resp.GetItems(), err +} + func (fe *frontendServer) convertCurrency(ctx context.Context, money *pb.Money, currency string) (*pb.Money, error) { if avoidNoopCurrencyConversionRPC && money.GetCurrencyCode() == currency { return money, nil diff --git a/src/frontend/templates/header.html b/src/frontend/templates/header.html index 5b4110f..51ba64b 100644 --- a/src/frontend/templates/header.html +++ b/src/frontend/templates/header.html @@ -23,7 +23,7 @@ {{end}} - View Cart + View Cart ({{$.cart_size}}) diff --git a/src/test-cli/main.go b/src/test-cli/main.go index 25bab2c..b66503b 100644 --- a/src/test-cli/main.go +++ b/src/test-cli/main.go @@ -299,8 +299,16 @@ func testCartService() error { defer conn.Close() cl := pb.NewCartServiceClient(conn) - log.Println("--- rpc AddItem()") userID := "smoke-test-user" + log.Println("--- rpc GetCart()") + cartResp, err := cl.GetCart(context.TODO(), &pb.GetCartRequest{ + UserId: userID}) + if err != nil { + return err + } + log.Printf("--> %d items in cart for user %q", len(cartResp.Items), cartResp.UserId) + + log.Println("--- rpc AddItem()") _, err = cl.AddItem(context.TODO(), &pb.AddItemRequest{ UserId: userID, Item: &pb.CartItem{ProductId: "1", Quantity: 2}, @@ -319,14 +327,13 @@ func testCartService() error { log.Printf("--> added item") log.Println("--- rpc GetCart()") - cartResp, err := cl.GetCart(context.TODO(), &pb.GetCartRequest{ + cartResp, err = cl.GetCart(context.TODO(), &pb.GetCartRequest{ UserId: userID}) if err != nil { return err } log.Printf("--> %d items in cart for user %q", len(cartResp.Items), cartResp.UserId) log.Printf("--> cart: %v", cartResp.Items) - log.Println("--- rpc EmptyCart()") _, err = cl.EmptyCart(context.TODO(), &pb.EmptyCartRequest{ UserId: userID})