├── .gitignore ├── .travis.settings.xml ├── .travis.yml ├── README.md ├── cart-spring-boot ├── build.gradle ├── pom.xml └── src │ └── main │ ├── config │ └── settings.xml │ ├── java │ └── com │ │ └── redhat │ │ └── coolstore │ │ ├── CartServiceApplication.java │ │ ├── model │ │ ├── Product.java │ │ ├── Promotion.java │ │ ├── ShoppingCart.java │ │ └── ShoppingCartItem.java │ │ ├── rest │ │ ├── CartEndpoint.java │ │ └── JerseyConfig.java │ │ └── service │ │ ├── CatalogService.java │ │ ├── PromoService.java │ │ ├── ShippingService.java │ │ ├── ShoppingCartService.java │ │ └── ShoppingCartServiceImpl.java │ └── resources │ └── application.properties ├── catalog-spring-boot ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── redhat │ │ └── cloudnative │ │ └── catalog │ │ ├── CatalogApplication.java │ │ ├── CatalogController.java │ │ ├── Product.java │ │ └── ProductRepository.java │ └── resources │ ├── application.properties │ └── import.sql ├── gateway-vertx ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── redhat │ │ └── cloudnative │ │ └── gateway │ │ └── GatewayVerticle.java │ └── resources │ └── vertx-default-jul-logging.properties ├── inventory-wildfly-swarm ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── redhat │ │ └── cloudnative │ │ └── inventory │ │ ├── Inventory.java │ │ ├── InventoryApplication.java │ │ └── InventoryResource.java │ └── resources │ ├── META-INF │ ├── load.sql │ └── persistence.xml │ └── project-stages.yml ├── openshift ├── cart-pipeline.yaml ├── cart-template.yaml ├── coolstore-bluegreen-template.yaml ├── coolstore-build-template.yaml ├── coolstore-deployment-template.yaml └── coolstore-template.yaml ├── solutions ├── lab-10 │ └── Dockerfile ├── lab-3 │ └── cart-template.yaml ├── lab-4 │ └── cart-pipeline.yaml ├── lab-5 │ ├── Jenkinsfile │ └── cart-pipeline-scm.yaml ├── lab-6 │ └── Jenkinsfile ├── lab-7 │ ├── cart-blue.yaml │ ├── cart-green.yaml │ └── coolstore-bluegreen-template.yaml ├── lab-8 │ └── Jenkinsfile └── support │ ├── lab-clean.sh │ └── lab-solve.sh └── web-nodejs ├── app ├── app.js ├── controllers │ └── controllers.js ├── coolstore.config.js ├── css │ └── coolstore.css ├── directives │ └── header.js ├── imgs │ ├── 16 oz. Vortex Tumbler.jpg │ ├── Forge Laptop Sticker.jpg │ ├── Lytro Camera.jpg │ ├── Oculus Rift.jpg │ ├── Ogio Caliber Polo.jpg │ ├── Pebble Smart Watch.jpg │ ├── Red Fedora.jpg │ ├── Solid Performance Polo.jpg │ └── logo.png ├── keycloak.config.js ├── routes │ └── routes.js └── services │ ├── cart.js │ └── catalog.js ├── bower.json ├── package.json ├── server.js └── views ├── index.html └── partials ├── cart.html ├── header.html └── home.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | target/ 25 | *.iml 26 | tmp/ 27 | .idea/ 28 | .gradle/ 29 | .settings/ 30 | bin/ 31 | 32 | .classpath 33 | .project 34 | -------------------------------------------------------------------------------- /.travis.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | jboss-ga-repository 8 | 9 | 10 | jboss-ga-repository 11 | http://maven.repository.redhat.com/ga 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | jboss-ga-plugin-repository 23 | http://maven.repository.redhat.com/ga 24 | 25 | true 26 | 27 | 28 | false 29 | 30 | 31 | 32 | 33 | 34 | jboss-techpreview-repository 35 | 36 | 37 | jboss-techpreview-repository 38 | http://maven.repository.redhat.com/techpreview/all 39 | 40 | true 41 | 42 | 43 | false 44 | 45 | 46 | 47 | 48 | 49 | jboss-techpreview-plugin-repository 50 | http://maven.repository.redhat.com/techpreview/all 51 | 52 | true 53 | 54 | 55 | false 56 | 57 | 58 | 59 | 60 | 61 | jboss-earlyaccess-repository 62 | 63 | 64 | jboss-earlyaccess-repository 65 | http://maven.repository.redhat.com/earlyaccess/all/ 66 | 67 | true 68 | 69 | 70 | false 71 | 72 | 73 | 74 | 75 | 76 | jboss-earlyaccess-plugin-repository 77 | http://maven.repository.redhat.com/earlyaccess/all/ 78 | 79 | true 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | 89 | jboss-ga-repository 90 | jboss-techpreview-repository 91 | jboss-earlyaccess-repository 92 | 93 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | before_install: 7 | - nvm install 4.4 8 | - cp .travis.settings.xml $HOME/.m2/settings.xml 9 | 10 | env: 11 | - BUILD_CMD="mvn -f cart-spring-boot package" 12 | - BUILD_CMD="mvn -f catalog-spring-boot package" 13 | - BUILD_CMD="mvn -f gateway-vertx package" 14 | - BUILD_CMD="mvn -f inventory-wildfly-swarm package" 15 | - BUILD_CMD="npm install --prefix web-nodejs" 16 | 17 | script: 18 | - $BUILD_CMD -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevOps Workshop Labs [](https://travis-ci.org/openshift-labs/devops-labs) 2 | 3 | CoolStore is an online store web application built using Spring Boot, WildFly Swarm, Eclipse Vert.x, 4 | Node.js and AngularJS adopting the microservices architecture. 5 | 6 | * **Web**: A Node.js/Angular front-end 7 | * **API Gateway**: vert.x service aggregates API calls to back-end services and provides a condenses REST API for front-end 8 | * **Catalog**: Spring Boot service exposing REST API for the product catalog and product information 9 | * **Inventory**: WildFly Swarm service exposing REST API for product's inventory status 10 | * **Cart**: Spring Boot service exposing REST API for shopping cart 11 | 12 | ``` 13 | +-------------+ 14 | | | 15 | | Web | 16 | | | 17 | | Node.js | 18 | | AngularJS | 19 | +------+------+ 20 | | 21 | v 22 | +------+------+ 23 | | | 24 | | API Gateway | 25 | | | 26 | | Vert.x | 27 | | | 28 | +------+------+ 29 | | 30 | +---------+---------+-------------------+ 31 | v v v 32 | +------+------+ +------+------+ +------+------+ 33 | | | | | | | 34 | | Catalog | | Inventory | | Cart | 35 | | | | | | | 36 | | Spring Boot | |WildFly Swarm| | Spring Boot | 37 | | | | | | | 38 | +-------------+ +-------------+ +-------------+ 39 | ``` 40 | -------------------------------------------------------------------------------- /cart-spring-boot/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | id "io.spring.dependency-management" version "1.0.1.RELEASE" 4 | id 'org.springframework.boot' version '1.5.2.RELEASE' 5 | } 6 | 7 | version = "1.0.0-SNAPSHOT" 8 | 9 | jar { 10 | archiveName = 'cart.jar' 11 | } 12 | 13 | repositories { 14 | mavenLocal() 15 | maven { 16 | url "http://nexus.lab-infra.svc.cluster.local:8081/repository/maven-all-public" 17 | } 18 | mavenCentral() 19 | } 20 | 21 | dependencyManagement { 22 | imports { 23 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.SR2" 24 | } 25 | } 26 | 27 | dependencies { 28 | compile("org.springframework.boot:spring-boot-starter-web") 29 | compile("org.springframework.boot:spring-boot-starter-jersey") 30 | compile("org.springframework.boot:spring-boot-starter-actuator") 31 | compile("org.springframework.cloud:spring-cloud-starter-feign") { 32 | exclude group: "com.sun.jersey", module: "jersey-client" 33 | } 34 | 35 | runtime("org.springframework.boot:spring-boot-starter-tomcat") 36 | testCompile("org.springframework.boot:spring-boot-starter-test") 37 | } -------------------------------------------------------------------------------- /cart-spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.redhat.coolstore 7 | cart 8 | 1.0.0-SNAPSHOT 9 | jar 10 | 11 | cart-service 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 1.4.2.RELEASE 17 | 18 | 19 | 20 | 21 | 22 | org.springframework.cloud 23 | spring-cloud-dependencies 24 | Camden.SR2 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | UTF-8 33 | UTF-8 34 | 1.8 35 | com.redhat.coolstore.CartServiceApplication 36 | 6.3.1.Final-redhat-1 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-web 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-jersey 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-actuator 53 | 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-starter-feign 58 | 59 | 60 | com.sun.jersey 61 | jersey-client 62 | 63 | 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-tomcat 69 | provided 70 | 71 | 72 | 73 | org.infinispan 74 | infinispan-core 75 | ${version.org.infinispan} 76 | 77 | 78 | 79 | org.infinispan 80 | infinispan-client-hotrod 81 | ${version.org.infinispan} 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-starter-test 88 | test 89 | 90 | 91 | 92 | 93 | ${project.artifactId} 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-maven-plugin 98 | 99 | 100 | io.fabric8 101 | fabric8-maven-plugin 102 | 3.2.28 103 | 104 | none 105 | -service 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | cart-tests 114 | 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-maven-plugin 119 | 120 | 121 | 122 | repackage 123 | 124 | 125 | true 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | discount-tests 135 | 136 | 137 | 138 | org.springframework.boot 139 | spring-boot-maven-plugin 140 | 141 | 142 | 143 | repackage 144 | 145 | 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/config/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | nexus.default 9 | ${NEXUS_URL}/repository/maven-all-public 10 | external:* 11 | 12 | 13 | 14 | 15 | 16 | nexus 17 | 18 | 19 | central 20 | http://central 21 | true 22 | true 23 | 24 | 25 | 26 | 27 | central 28 | http://central 29 | true 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | nexus 38 | 39 | 40 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/CartServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.feign.EnableFeignClients; 6 | 7 | @SpringBootApplication 8 | @EnableFeignClients 9 | public class CartServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(CartServiceApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/model/Product.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public class Product implements Serializable { 6 | 7 | private static final long serialVersionUID = -7304814269819778382L; 8 | private String itemId; 9 | private String name; 10 | private String desc; 11 | private double price; 12 | 13 | public Product() { 14 | 15 | } 16 | 17 | public Product(String itemId, String name, String desc, double price) { 18 | super(); 19 | this.itemId = itemId; 20 | this.name = name; 21 | this.desc = desc; 22 | this.price = price; 23 | } 24 | public String getItemId() { 25 | return itemId; 26 | } 27 | public void setItemId(String itemId) { 28 | this.itemId = itemId; 29 | } 30 | public String getName() { 31 | return name; 32 | } 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | public String getDesc() { 37 | return desc; 38 | } 39 | public void setDesc(String desc) { 40 | this.desc = desc; 41 | } 42 | public double getPrice() { 43 | return price; 44 | } 45 | public void setPrice(double price) { 46 | this.price = price; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "Product [itemId=" + itemId + ", name=" + name + ", desc=" 52 | + desc + ", price=" + price + "]"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/model/Promotion.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.model; 2 | 3 | public class Promotion { 4 | 5 | private String itemId; 6 | 7 | private double percentOff; 8 | 9 | public Promotion() { 10 | 11 | } 12 | 13 | public Promotion(String itemId, double percentOff) { 14 | super(); 15 | this.itemId = itemId; 16 | this.percentOff = percentOff; 17 | } 18 | 19 | public String getItemId() { 20 | return itemId; 21 | } 22 | 23 | public void setItemId(String itemId) { 24 | this.itemId = itemId; 25 | } 26 | 27 | public double getPercentOff() { 28 | return percentOff; 29 | } 30 | 31 | public void setPercentOff(double percentOff) { 32 | this.percentOff = percentOff; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Promotion [itemId=" + itemId + ", percentOff=" + percentOff 38 | + "]"; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/model/ShoppingCart.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class ShoppingCart implements Serializable { 8 | 9 | private static final long serialVersionUID = -1108043957592113528L; 10 | 11 | private double cartItemTotal; 12 | 13 | private double cartItemPromoSavings; 14 | 15 | private double shippingTotal; 16 | 17 | private double shippingPromoSavings; 18 | 19 | private double cartTotal; 20 | 21 | private String cartId; 22 | 23 | private List shoppingCartItemList = new ArrayList(); 24 | 25 | public ShoppingCart(String cartId) { 26 | this.cartId = cartId; 27 | } 28 | 29 | public String getCartId() { 30 | return cartId; 31 | } 32 | 33 | public void setCartId(String cartId) { 34 | this.cartId = cartId; 35 | } 36 | 37 | 38 | public List getShoppingCartItemList() { 39 | return shoppingCartItemList; 40 | } 41 | 42 | public void setShoppingCartItemList(List shoppingCartItemList) { 43 | this.shoppingCartItemList = shoppingCartItemList; 44 | } 45 | 46 | public void resetShoppingCartItemList() { 47 | shoppingCartItemList = new ArrayList(); 48 | } 49 | 50 | public void addShoppingCartItem(ShoppingCartItem sci) { 51 | 52 | if (sci != null) { 53 | 54 | shoppingCartItemList.add(sci); 55 | 56 | } 57 | 58 | } 59 | 60 | public boolean removeShoppingCartItem(ShoppingCartItem sci) { 61 | 62 | boolean removed = false; 63 | 64 | if (sci != null) { 65 | 66 | removed = shoppingCartItemList.remove(sci); 67 | 68 | } 69 | 70 | return removed; 71 | 72 | } 73 | 74 | public double getCartItemTotal() { 75 | return cartItemTotal; 76 | } 77 | 78 | public void setCartItemTotal(double cartItemTotal) { 79 | this.cartItemTotal = cartItemTotal; 80 | } 81 | 82 | public double getShippingTotal() { 83 | return shippingTotal; 84 | } 85 | 86 | public void setShippingTotal(double shippingTotal) { 87 | this.shippingTotal = shippingTotal; 88 | } 89 | 90 | public double getCartTotal() { 91 | return cartTotal; 92 | } 93 | 94 | public void setCartTotal(double cartTotal) { 95 | this.cartTotal = cartTotal; 96 | } 97 | 98 | public double getCartItemPromoSavings() { 99 | return cartItemPromoSavings; 100 | } 101 | 102 | public void setCartItemPromoSavings(double cartItemPromoSavings) { 103 | this.cartItemPromoSavings = cartItemPromoSavings; 104 | } 105 | 106 | public double getShippingPromoSavings() { 107 | return shippingPromoSavings; 108 | } 109 | 110 | public void setShippingPromoSavings(double shippingPromoSavings) { 111 | this.shippingPromoSavings = shippingPromoSavings; 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | return "ShoppingCart [cartId=" + cartId 117 | + ", cartItemTotal=" + cartItemTotal 118 | + ", cartItemPromoSavings=" + cartItemPromoSavings 119 | + ", shippingTotal=" + shippingTotal 120 | + ", shippingPromoSavings=" + shippingPromoSavings 121 | + ", cartTotal=" + cartTotal + ", shoppingCartItemList=" 122 | + shoppingCartItemList + "]"; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/model/ShoppingCartItem.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public class ShoppingCartItem implements Serializable { 6 | 7 | private static final long serialVersionUID = 6964558044240061049L; 8 | 9 | private double price; 10 | private int quantity; 11 | private double promoSavings; 12 | private Product product; 13 | 14 | public ShoppingCartItem() { 15 | 16 | } 17 | 18 | public double getPrice() { 19 | return price; 20 | } 21 | 22 | public void setPrice(double price) { 23 | this.price = price; 24 | } 25 | 26 | public Product getProduct() { 27 | return product; 28 | } 29 | 30 | public void setProduct(Product product) { 31 | this.product = product; 32 | } 33 | 34 | public int getQuantity() { 35 | return quantity; 36 | } 37 | 38 | public void setQuantity(int quantity) { 39 | this.quantity = quantity; 40 | } 41 | 42 | public double getPromoSavings() { 43 | return promoSavings; 44 | } 45 | 46 | public void setPromoSavings(double promoSavings) { 47 | this.promoSavings = promoSavings; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "ShoppingCartItem [price=" + price + ", quantity=" + quantity 53 | + ", promoSavings=" + promoSavings + ", product=" + product 54 | + "]"; 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/rest/CartEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.rest; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import javax.ws.rs.DELETE; 10 | import javax.ws.rs.GET; 11 | import javax.ws.rs.POST; 12 | import javax.ws.rs.Path; 13 | import javax.ws.rs.PathParam; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.MediaType; 16 | 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.context.annotation.Scope; 21 | import org.springframework.web.bind.annotation.RestController; 22 | import org.springframework.web.context.WebApplicationContext; 23 | 24 | import com.redhat.coolstore.model.Product; 25 | import com.redhat.coolstore.model.ShoppingCart; 26 | import com.redhat.coolstore.model.ShoppingCartItem; 27 | import com.redhat.coolstore.service.ShoppingCartService; 28 | 29 | 30 | @RestController 31 | @Scope(scopeName = WebApplicationContext.SCOPE_SESSION) 32 | @Path("/cart") 33 | public class CartEndpoint implements Serializable { 34 | private static final Logger LOG = LoggerFactory.getLogger(CartEndpoint.class); 35 | 36 | /** 37 | * 38 | */ 39 | private static final long serialVersionUID = -7227732980791688773L; 40 | 41 | @Autowired 42 | private ShoppingCartService shoppingCartService; 43 | 44 | @GET 45 | @Path("/{cartId}") 46 | @Produces(MediaType.APPLICATION_JSON) 47 | public ShoppingCart getCart(@PathParam("cartId") String cartId) { 48 | return shoppingCartService.getShoppingCart(cartId); 49 | } 50 | 51 | @POST 52 | @Path("/{cartId}/{itemId}/{quantity}") 53 | @Produces(MediaType.APPLICATION_JSON) 54 | public ShoppingCart add(@PathParam("cartId") String cartId, 55 | @PathParam("itemId") String itemId, 56 | @PathParam("quantity") int quantity) throws Exception { 57 | return shoppingCartService.addItem(cartId, itemId, quantity); 58 | } 59 | 60 | @POST 61 | @Path("/{cartId}/{tmpId}") 62 | @Produces(MediaType.APPLICATION_JSON) 63 | public ShoppingCart set(@PathParam("cartId") String cartId, 64 | @PathParam("tmpId") String tmpId) throws Exception { 65 | 66 | return shoppingCartService.set(cartId, tmpId); 67 | } 68 | 69 | @DELETE 70 | @Path("/{cartId}/{itemId}/{quantity}") 71 | @Produces(MediaType.APPLICATION_JSON) 72 | public ShoppingCart delete(@PathParam("cartId") String cartId, 73 | @PathParam("itemId") String itemId, 74 | @PathParam("quantity") int quantity) throws Exception { 75 | 76 | return shoppingCartService.deleteItem(cartId, itemId, quantity); 77 | } 78 | 79 | @POST 80 | @Path("/checkout/{cartId}") 81 | @Produces(MediaType.APPLICATION_JSON) 82 | public ShoppingCart checkout(@PathParam("cartId") String cartId) { 83 | // TODO: register purchase of shoppingCart items by specific user in session 84 | return shoppingCartService.checkout(cartId); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/rest/JerseyConfig.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.rest; 2 | 3 | import org.glassfish.jersey.server.ResourceConfig; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | public class JerseyConfig extends ResourceConfig { 8 | public JerseyConfig() { 9 | register(CartEndpoint.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/service/CatalogService.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.cloud.netflix.feign.FeignClient; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | 9 | import com.redhat.coolstore.model.Product; 10 | 11 | @FeignClient(name = "catalogService", url = "${catalog.endpoint}") 12 | interface CatalogService { 13 | @RequestMapping(method = RequestMethod.GET, value = "/api/products") 14 | List products(); 15 | } -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/service/PromoService.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.service; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | import org.springframework.stereotype.Component; 10 | 11 | import com.redhat.coolstore.model.Promotion; 12 | import com.redhat.coolstore.model.ShoppingCart; 13 | import com.redhat.coolstore.model.ShoppingCartItem; 14 | 15 | @Component 16 | public class PromoService implements Serializable { 17 | 18 | private static final long serialVersionUID = 2088590587856645568L; 19 | 20 | private String name = null; 21 | 22 | private Set promotionSet = null; 23 | 24 | public PromoService() { 25 | promotionSet = new HashSet(); 26 | promotionSet.add(new Promotion("329299", .25)); 27 | } 28 | 29 | public void applyCartItemPromotions(ShoppingCart shoppingCart) { 30 | if ( shoppingCart != null && shoppingCart.getShoppingCartItemList().size() > 0 ) { 31 | Map promoMap = new HashMap(); 32 | for (Promotion promo : getPromotions()) { 33 | promoMap.put(promo.getItemId(), promo); 34 | } 35 | 36 | for ( ShoppingCartItem sci : shoppingCart.getShoppingCartItemList() ) { 37 | String productId = sci.getProduct().getItemId(); 38 | Promotion promo = promoMap.get(productId); 39 | if ( promo != null ) { 40 | sci.setPromoSavings(sci.getProduct().getPrice() * promo.getPercentOff() * -1); 41 | sci.setPrice(sci.getProduct().getPrice() * (1-promo.getPercentOff())); 42 | } 43 | } 44 | } 45 | 46 | } 47 | 48 | public void applyShippingPromotions(ShoppingCart shoppingCart) { 49 | if ( shoppingCart != null ) { 50 | //PROMO: if cart total is greater than 75, free shipping 51 | if ( shoppingCart.getCartItemTotal() >= 75) { 52 | shoppingCart.setShippingPromoSavings(shoppingCart.getShippingTotal() * -1); 53 | shoppingCart.setShippingTotal(0); 54 | } 55 | } 56 | } 57 | 58 | public Set getPromotions() { 59 | if ( promotionSet == null ) { 60 | promotionSet = new HashSet(); 61 | } 62 | 63 | return new HashSet(promotionSet); 64 | } 65 | 66 | public void setPromotions(Set promotionSet) { 67 | if ( promotionSet != null ) { 68 | this.promotionSet = new HashSet(promotionSet); 69 | 70 | } else { 71 | this.promotionSet = new HashSet(); 72 | } 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "PromoService [name=" + name + ", promotionSet=" + promotionSet 78 | + "]"; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/service/ShippingService.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.service; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import com.redhat.coolstore.model.ShoppingCart; 6 | 7 | @Component 8 | public class ShippingService { 9 | 10 | public void calculateShipping(ShoppingCart sc) { 11 | if ( sc != null ) { 12 | if ( sc.getCartItemTotal() >= 0 && sc.getCartItemTotal() < 25) { 13 | sc.setShippingTotal(2.99); 14 | } else if ( sc.getCartItemTotal() >= 25 && sc.getCartItemTotal() < 50) { 15 | sc.setShippingTotal(4.99); 16 | } else if ( sc.getCartItemTotal() >= 50 && sc.getCartItemTotal() < 75) { 17 | sc.setShippingTotal(6.99); 18 | } else if ( sc.getCartItemTotal() >= 75 && sc.getCartItemTotal() < 100) { 19 | sc.setShippingTotal(8.99); 20 | } else if ( sc.getCartItemTotal() >= 100 && sc.getCartItemTotal() < 10000) { 21 | sc.setShippingTotal(10.99); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/service/ShoppingCartService.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.service; 2 | 3 | import com.redhat.coolstore.model.Product; 4 | import com.redhat.coolstore.model.ShoppingCart; 5 | 6 | public interface ShoppingCartService { 7 | public ShoppingCart getShoppingCart(String cartId); 8 | 9 | public Product getProduct(String itemId); 10 | 11 | public ShoppingCart deleteItem(String cartId, String itemId, int quantity); 12 | 13 | public ShoppingCart checkout(String cartId); 14 | 15 | public ShoppingCart addItem(String cartId, String itemId, int quantity); 16 | 17 | public ShoppingCart set(String cartId, String tmpId); 18 | } 19 | -------------------------------------------------------------------------------- /cart-spring-boot/src/main/java/com/redhat/coolstore/service/ShoppingCartServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.redhat.coolstore.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.function.Function; 8 | import java.util.stream.Collectors; 9 | 10 | import javax.annotation.PostConstruct; 11 | 12 | import org.infinispan.client.hotrod.RemoteCacheManager; 13 | import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; 14 | import org.infinispan.commons.api.BasicCacheContainer; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.stereotype.Component; 20 | 21 | import com.redhat.coolstore.model.Product; 22 | import com.redhat.coolstore.model.ShoppingCart; 23 | import com.redhat.coolstore.model.ShoppingCartItem; 24 | 25 | @Component 26 | public class ShoppingCartServiceImpl implements ShoppingCartService { 27 | 28 | private static final Logger LOG = LoggerFactory.getLogger(ShoppingCartServiceImpl.class); 29 | 30 | @Autowired 31 | ShippingService ss; 32 | 33 | @Autowired 34 | CatalogService catalogServie; 35 | 36 | @Autowired 37 | PromoService ps; 38 | 39 | @Value("${application.mode}") 40 | String mode; 41 | 42 | private Map carts; 43 | 44 | private Map productMap = new HashMap<>(); 45 | 46 | @PostConstruct 47 | public void init() { 48 | if ("dev".contentEquals(mode)) { 49 | // cache dummy product 50 | Product dummy = new Product(); 51 | dummy.setItemId("666"); 52 | dummy.setName("Dummy Product"); 53 | dummy.setPrice(50); 54 | dummy.setDesc("Dummy product for testing in DEV mode"); 55 | productMap.put(dummy.getItemId(), dummy); 56 | } 57 | 58 | String host = System.getenv("DATAGRID_HOST"); 59 | String port = System.getenv("DATAGRID_PORT"); 60 | 61 | if (host != null) { 62 | ConfigurationBuilder builder = new ConfigurationBuilder(); 63 | builder.addServer() 64 | .host(host) 65 | .port(Integer.parseInt(port)); 66 | BasicCacheContainer manager = new RemoteCacheManager(builder.build()); 67 | carts = manager.getCache("cart"); 68 | 69 | LOG.info("Using remote JBoss Data Grid (Hot Rod) on {}:{} for cart data", host, port); 70 | } else { 71 | carts = new HashMap<>(); 72 | LOG.info("Using local cache for cart data"); 73 | } 74 | } 75 | 76 | @Override 77 | public ShoppingCart getShoppingCart(String cartId) { 78 | if (!carts.containsKey(cartId)) { 79 | ShoppingCart cart = new ShoppingCart(cartId); 80 | carts.put(cartId, cart); 81 | return cart; 82 | } 83 | 84 | ShoppingCart cart = carts.get(cartId); 85 | priceShoppingCart(cart); 86 | carts.put(cartId, cart); 87 | return cart; 88 | } 89 | 90 | private void priceShoppingCart(ShoppingCart sc) { 91 | if (sc != null) { 92 | initShoppingCartForPricing(sc); 93 | 94 | if (sc.getShoppingCartItemList() != null && sc.getShoppingCartItemList().size() > 0) { 95 | ps.applyCartItemPromotions(sc); 96 | 97 | for (ShoppingCartItem sci : sc.getShoppingCartItemList()) { 98 | sc.setCartItemPromoSavings(sc.getCartItemPromoSavings() + sci.getPromoSavings() * sci.getQuantity()); 99 | sc.setCartItemTotal(sc.getCartItemTotal() + sci.getPrice() * sci.getQuantity()); 100 | } 101 | 102 | ss.calculateShipping(sc); 103 | } 104 | 105 | ps.applyShippingPromotions(sc); 106 | 107 | sc.setCartTotal(sc.getCartItemTotal() + sc.getShippingTotal()); 108 | } 109 | } 110 | 111 | private void initShoppingCartForPricing(ShoppingCart sc) { 112 | sc.setCartItemTotal(0); 113 | sc.setCartItemPromoSavings(0); 114 | sc.setShippingTotal(0); 115 | sc.setShippingPromoSavings(0); 116 | sc.setCartTotal(0); 117 | 118 | for (ShoppingCartItem sci : sc.getShoppingCartItemList()) { 119 | Product p = getProduct(sci.getProduct().getItemId()); 120 | 121 | //if product exist, create new product to reset price 122 | if (p != null) { 123 | sci.setProduct(new Product(p.getItemId(), p.getName(), p.getDesc(), p.getPrice())); 124 | sci.setPrice(p.getPrice()); 125 | } 126 | 127 | sci.setPromoSavings(0); 128 | } 129 | } 130 | 131 | @Override 132 | public Product getProduct(String itemId) { 133 | if (!productMap.containsKey(itemId)) { 134 | // Fetch and cache products. TODO: Cache should expire at some point! 135 | List products = catalogServie.products(); 136 | productMap = products.stream().collect(Collectors.toMap(Product::getItemId, Function.identity())); 137 | } 138 | 139 | return productMap.get(itemId); 140 | } 141 | 142 | @Override 143 | public ShoppingCart deleteItem(String cartId, String itemId, int quantity) { 144 | List toRemoveList = new ArrayList<>(); 145 | 146 | ShoppingCart cart = getShoppingCart(cartId); 147 | 148 | cart.getShoppingCartItemList().stream() 149 | .filter(sci -> sci.getProduct().getItemId().equals(itemId)) 150 | .forEach(sci -> { 151 | if (quantity >= sci.getQuantity()) { 152 | toRemoveList.add(sci); 153 | } else { 154 | sci.setQuantity(sci.getQuantity() - quantity); 155 | } 156 | }); 157 | 158 | toRemoveList.forEach(cart::removeShoppingCartItem); 159 | priceShoppingCart(cart); 160 | carts.put(cartId, cart); 161 | 162 | return cart; 163 | } 164 | 165 | @Override 166 | public ShoppingCart checkout(String cartId) { 167 | ShoppingCart cart = getShoppingCart(cartId); 168 | cart.resetShoppingCartItemList(); 169 | priceShoppingCart(cart); 170 | carts.put(cartId, cart); 171 | return cart; 172 | } 173 | 174 | @Override 175 | public ShoppingCart addItem(String cartId, String itemId, int quantity) { 176 | ShoppingCart cart = getShoppingCart(cartId); 177 | Product product = getProduct(itemId); 178 | 179 | if (product == null) { 180 | LOG.warn("Invalid product {} request to get added to the shopping cart. No product added", itemId); 181 | return cart; 182 | } 183 | 184 | ShoppingCartItem sci = new ShoppingCartItem(); 185 | sci.setProduct(product); 186 | sci.setQuantity(quantity); 187 | sci.setPrice(product.getPrice()); 188 | cart.addShoppingCartItem(sci); 189 | 190 | try { 191 | priceShoppingCart(cart); 192 | cart.setShoppingCartItemList(dedupeCartItems(cart)); 193 | } catch (Exception ex) { 194 | cart.removeShoppingCartItem(sci); 195 | throw ex; 196 | } 197 | 198 | carts.put(cartId, cart); 199 | return cart; 200 | } 201 | 202 | @Override 203 | public ShoppingCart set(String cartId, String tmpId) { 204 | 205 | ShoppingCart cart = getShoppingCart(cartId); 206 | ShoppingCart tmpCart = getShoppingCart(tmpId); 207 | 208 | if (tmpCart != null) { 209 | cart.resetShoppingCartItemList(); 210 | cart.setShoppingCartItemList(tmpCart.getShoppingCartItemList()); 211 | } 212 | 213 | try { 214 | priceShoppingCart(cart); 215 | cart.setShoppingCartItemList(dedupeCartItems(cart)); 216 | } catch (Exception ex) { 217 | throw ex; 218 | } 219 | 220 | carts.put(cartId, cart); 221 | return cart; 222 | } 223 | 224 | private List dedupeCartItems(ShoppingCart sc) { 225 | List result = new ArrayList<>(); 226 | Map quantityMap = new HashMap<>(); 227 | for (ShoppingCartItem sci : sc.getShoppingCartItemList()) { 228 | if (quantityMap.containsKey(sci.getProduct().getItemId())) { 229 | quantityMap.put(sci.getProduct().getItemId(), quantityMap.get(sci.getProduct().getItemId()) + sci.getQuantity()); 230 | } else { 231 | quantityMap.put(sci.getProduct().getItemId(), sci.getQuantity()); 232 | } 233 | } 234 | 235 | for (String itemId : quantityMap.keySet()) { 236 | Product p = getProduct(itemId); 237 | ShoppingCartItem newItem = new ShoppingCartItem(); 238 | newItem.setQuantity(quantityMap.get(itemId)); 239 | newItem.setPrice(p.getPrice()); 240 | newItem.setProduct(p); 241 | result.add(newItem); 242 | } 243 | 244 | return result; 245 | } 246 | } -------------------------------------------------------------------------------- /cart-spring-boot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=cart-service 2 | spring.jersey.application-path=/api 3 | 4 | catalog.endpoint=http://catalog:8080 5 | 6 | # dev or prod 7 | application.mode=dev 8 | -------------------------------------------------------------------------------- /catalog-spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | catalog 8 | 1.0-SNAPSHOT 9 | CoolStore Catalog Service 10 | CoolStore Catalog Service with Spring Boot 11 | 12 | 1.4.1.RELEASE 13 | 1 14 | 2.3.4 15 | 0.2.0.RELEASE 16 | 2.4.1 17 | 3.5.30 18 | 9.4.1212 19 | 20 | 21 | 22 | 23 | org.jboss.snowdrop 24 | spring-boot-1.4-bom 25 | ${spring-boot.bom.version} 26 | pom 27 | import 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-kubernetes-dependencies 32 | ${spring.k8s.bom.version} 33 | pom 34 | import 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-data-jpa 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-actuator 50 | 51 | 52 | org.springframework.cloud 53 | spring-cloud-starter-kubernetes-config 54 | 55 | 56 | io.fabric8 57 | kubernetes-client 58 | ${k8s.client.version} 59 | 60 | 61 | com.h2database 62 | h2 63 | 64 | 65 | org.postgresql 66 | postgresql 67 | ${postgresql.version} 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-compiler-plugin 75 | 3.6.1 76 | 77 | 1.8 78 | 1.8 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-maven-plugin 84 | ${spring-boot.version} 85 | 86 | 87 | 88 | repackage 89 | 90 | 91 | 92 | 93 | -Djava.net.preferIPv4Stack=true -Dserver.port=9000 -Dspring.cloud.kubernetes.enabled=false 94 | 95 | 96 | 97 | io.fabric8 98 | fabric8-maven-plugin 99 | ${fabric8.maven.plugin.version} 100 | 101 | 102 | 103 | resource 104 | build 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | microservice 114 | catalog 115 | 116 | 117 | 118 | 119 | 120 | 121 | spring-boot 122 | 123 | 124 | 125 | redhat-openjdk-18/openjdk18-openshift 126 | 127 | 128 | 129 | 130 | 131 | 132 | /health 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/CatalogApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CatalogApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CatalogApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/CatalogController.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import java.util.List; 4 | import java.util.Spliterator; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.StreamSupport; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | 15 | @Controller 16 | @RequestMapping(value = "/api/products") 17 | public class CatalogController { 18 | 19 | @Autowired 20 | private ProductRepository repository; 21 | 22 | @ResponseBody 23 | @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) 24 | public List getAll() { 25 | Spliterator products = repository.findAll().spliterator(); 26 | return StreamSupport.stream(products, false).collect(Collectors.toList()); 27 | } 28 | } -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/Product.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.Id; 7 | import javax.persistence.Table; 8 | import javax.persistence.UniqueConstraint; 9 | 10 | @Entity 11 | @Table(name = "PRODUCT", uniqueConstraints = @UniqueConstraint(columnNames = "itemId")) 12 | public class Product implements Serializable { 13 | 14 | @Id 15 | private String itemId; 16 | 17 | private String name; 18 | 19 | private String description; 20 | 21 | private double price; 22 | 23 | public Product() { 24 | } 25 | 26 | public String getItemId() { 27 | return itemId; 28 | } 29 | 30 | public void setItemId(String itemId) { 31 | this.itemId = itemId; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public String getDescription() { 43 | return description; 44 | } 45 | 46 | public void setDescription(String description) { 47 | this.description = description; 48 | } 49 | 50 | public double getPrice() { 51 | return price; 52 | } 53 | 54 | public void setPrice(double price) { 55 | this.price = price; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "Product [itemId=" + itemId + ", name=" + name + ", price=" + price + "]"; 61 | } 62 | } -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface ProductRepository extends CrudRepository { 6 | } 7 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=catalog 2 | 3 | spring.datasource.url=jdbc:h2:mem:fruits;DB_CLOSE_ON_EXIT=FALSE 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | spring.datasource.driver-class-name=org.h2.Driver 7 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/resources/import.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('329299', 'Red Fedora', 'Official Red Hat Fedora', 34.99); 2 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('329199', 'Forge Laptop Sticker', 'JBoss Community Forge Project Sticker', 8.50); 3 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165613', 'Solid Performance Polo', 'Moisture-wicking, antimicrobial 100% polyester design wicks for life of garment. No-curl, rib-knit collar...', 17.80) 4 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165614', 'Ogio Caliber Polo', 'Moisture-wicking 100% polyester. Rib-knit collar and cuffs; Ogio jacquard tape insitem_ide neck; bar-tacked three-button placket with...', 28.75) 5 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165954', '16 oz. Vortex Tumbler', 'Double-wall insulated, BPA-free, acrylic cup. Push-on litem_id with thumb-slitem_ide closure; for hot and cold beverages. Holds 16 oz. Hand wash only. Imprint. Clear.', 6.00) 6 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444434', 'Pebble Smart Watch', 'Smart glasses and smart watches are perhaps two of the most exciting developments in recent years. ', 24.00) 7 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444435', 'Oculus Rift', 'The world of gaming has also undergone some very unique and compelling tech advances in recent years. Virtual reality...', 106.00) 8 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444436', 'Lytro Camera', 'Consumers who want to up their photography game are looking at newfangled cameras like the Lytro Field camera, designed to ...', 44.30) 9 | -------------------------------------------------------------------------------- /gateway-vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | gateway 8 | 1.0-SNAPSHOT 9 | jar 10 | CoolStore Gateway Service 11 | CoolStore Gateway Service with Eclipse Vert.x 12 | 13 | 3.4.2 14 | 1.0.8 15 | com.redhat.cloudnative.gateway.GatewayVerticle 16 | 3.5.30 17 | 1.7.25 18 | 19 | 20 | 21 | 22 | io.vertx 23 | vertx-dependencies 24 | ${vertx.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | io.vertx 33 | vertx-core 34 | 35 | 36 | io.vertx 37 | vertx-web 38 | 39 | 40 | io.vertx 41 | vertx-circuit-breaker 42 | 43 | 44 | io.vertx 45 | vertx-web-client 46 | 47 | 48 | io.vertx 49 | vertx-rx-java 50 | 51 | 52 | io.vertx 53 | vertx-service-discovery 54 | 55 | 56 | io.vertx 57 | vertx-service-discovery-bridge-kubernetes 58 | 59 | 60 | org.slf4j 61 | slf4j-api 62 | ${slf4j.version} 63 | 64 | 65 | org.slf4j 66 | slf4j-jdk14 67 | ${slf4j.version} 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-compiler-plugin 75 | 3.6.1 76 | 77 | 1.8 78 | 1.8 79 | 80 | 81 | 82 | io.fabric8 83 | vertx-maven-plugin 84 | ${vertx-maven-plugin.version} 85 | 86 | 87 | vmp 88 | 89 | initialize 90 | package 91 | 92 | 93 | 94 | 95 | true 96 | -Djava.net.preferIPv4Stack=true 97 | 98 | 99 | 100 | io.fabric8 101 | fabric8-maven-plugin 102 | ${fabric8.maven.plugin.version} 103 | 104 | 105 | 106 | resource 107 | build 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | microservice 117 | gateway 118 | 119 | 120 | 121 | 122 | 123 | 124 | vertx 125 | 126 | 127 | 128 | redhat-openjdk-18/openjdk18-openshift 129 | 130 | 131 | 132 | 133 | 134 | 135 | /health 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /gateway-vertx/src/main/java/com/redhat/cloudnative/gateway/GatewayVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.gateway; 2 | 3 | 4 | import io.vertx.circuitbreaker.CircuitBreakerOptions; 5 | import io.vertx.core.http.HttpMethod; 6 | import io.vertx.core.json.Json; 7 | import io.vertx.core.json.JsonObject; 8 | import io.vertx.ext.web.client.WebClientOptions; 9 | import io.vertx.rxjava.circuitbreaker.CircuitBreaker; 10 | import io.vertx.rxjava.core.AbstractVerticle; 11 | import io.vertx.rxjava.ext.web.Router; 12 | import io.vertx.rxjava.ext.web.RoutingContext; 13 | import io.vertx.rxjava.ext.web.client.WebClient; 14 | import io.vertx.rxjava.ext.web.codec.BodyCodec; 15 | import io.vertx.rxjava.ext.web.handler.CorsHandler; 16 | import io.vertx.rxjava.servicediscovery.ServiceDiscovery; 17 | import io.vertx.rxjava.servicediscovery.types.HttpEndpoint; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import rx.Observable; 21 | import rx.Single; 22 | 23 | public class GatewayVerticle extends AbstractVerticle { 24 | private static final Logger LOG = LoggerFactory.getLogger(GatewayVerticle.class); 25 | 26 | private WebClient catalog; 27 | private WebClient inventory; 28 | private WebClient cart; 29 | private CircuitBreaker circuit; 30 | 31 | @Override 32 | public void start() { 33 | 34 | circuit = CircuitBreaker.create("inventory-circuit-breaker", vertx, 35 | new CircuitBreakerOptions() 36 | .setFallbackOnFailure(true) 37 | .setMaxFailures(3) 38 | .setResetTimeout(5000) 39 | .setTimeout(1000) 40 | ); 41 | 42 | Router router = Router.router(vertx); 43 | router.route().handler(CorsHandler.create("*") 44 | .allowedMethod(HttpMethod.GET) 45 | .allowedMethod(HttpMethod.POST) 46 | .allowedMethod(HttpMethod.DELETE) 47 | .allowedHeader("Access-Control-Allow-Method") 48 | .allowedHeader("Access-Control-Allow-Origin") 49 | .allowedHeader("Access-Control-Allow-Credentials") 50 | .allowedHeader("Content-Type")); 51 | 52 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 53 | router.get("/api/products").handler(this::products); 54 | router.get("/api/cart/:cartId").handler(this::getCart); 55 | router.post("/api/cart/:cartId/:itemId/:quantity").handler(this::addToCart); 56 | router.delete("/api/cart/:cartId/:itemId/:quantity").handler(this::deleteFromCart); 57 | 58 | ServiceDiscovery.create(vertx, discovery -> { 59 | // Catalog lookup 60 | Single catalogDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 61 | rec -> rec.getName().equals("catalog")) 62 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 63 | .setDefaultHost(System.getProperty("catalog.api.host", "localhost")) 64 | .setDefaultPort(Integer.getInteger("catalog.api.port", 9000)))); 65 | 66 | // Inventory lookup 67 | Single inventoryDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 68 | rec -> rec.getName().equals("inventory")) 69 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 70 | .setDefaultHost(System.getProperty("inventory.api.host", "localhost")) 71 | .setDefaultPort(Integer.getInteger("inventory.api.port", 9001)))); 72 | 73 | // Cart lookup 74 | Single cartDiscoveryRequest; 75 | if (Boolean.parseBoolean(System.getenv("DISABLE_CART_DISCOVERY"))) { 76 | LOG.info("Disable Cart discovery"); 77 | cartDiscoveryRequest = Single.just(null); 78 | } else { 79 | cartDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 80 | rec -> rec.getName().equals("cart")) 81 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 82 | .setDefaultHost(System.getProperty("cart.api.host", "localhost")) 83 | .setDefaultPort(Integer.getInteger("cart.api.port", 9002)))); 84 | } 85 | 86 | // Zip all 3 requests 87 | Single.zip(catalogDiscoveryRequest, inventoryDiscoveryRequest, cartDiscoveryRequest, (c, i, ct) -> { 88 | // When everything is done 89 | catalog = c; 90 | inventory = i; 91 | cart = ct; 92 | return vertx.createHttpServer() 93 | .requestHandler(router::accept) 94 | .listen(Integer.getInteger("http.port", 8080)); 95 | }).subscribe(); 96 | }); 97 | } 98 | 99 | private void products(RoutingContext rc) { 100 | // Retrieve catalog 101 | catalog.get("/api/products").as(BodyCodec.jsonArray()).rxSend() 102 | .map(resp -> { 103 | if (resp.statusCode() != 200) { 104 | new RuntimeException("Invalid response from the catalog: " + resp.statusCode()); 105 | } 106 | return resp.body(); 107 | }) 108 | .flatMap(products -> 109 | // For each item from the catalog, invoke the inventory service 110 | Observable.from(products) 111 | .cast(JsonObject.class) 112 | .flatMapSingle(product -> 113 | circuit.rxExecuteCommandWithFallback( 114 | future -> 115 | inventory.get("/api/inventory/" + product.getString("itemId")).as(BodyCodec.jsonObject()) 116 | .rxSend() 117 | .map(resp -> { 118 | if (resp.statusCode() != 200) { 119 | LOG.warn("Inventory error for {}: status code {}", 120 | product.getString("itemId"), resp.statusCode()); 121 | return product.copy(); 122 | } 123 | return product.copy().put("availability", 124 | new JsonObject().put("quantity", resp.body().getInteger("quantity"))); 125 | }) 126 | .subscribe( 127 | future::complete, 128 | future::fail), 129 | error -> { 130 | LOG.error("Inventory error for {}: {}", product.getString("itemId"), error.getMessage()); 131 | return product; 132 | } 133 | )) 134 | .toList().toSingle() 135 | ) 136 | .subscribe( 137 | list -> rc.response().end(Json.encodePrettily(list)), 138 | error -> rc.response().end(new JsonObject().put("error", error.getMessage()).toString()) 139 | ); 140 | } 141 | 142 | private void getCart(RoutingContext rc) { 143 | String cartId = rc.request().getParam("cartId"); 144 | 145 | if (cart == null) { 146 | initCartClient(rc); 147 | } 148 | 149 | circuit.executeWithFallback( 150 | future -> { 151 | cart.get("/api/cart/" + cartId).as(BodyCodec.jsonObject()) 152 | .send( ar -> { 153 | if (ar.succeeded()) { 154 | rc.response().end(ar.result().body().toString()); 155 | future.complete(); 156 | } else { 157 | rc.response().end(new JsonObject().toString()); 158 | future.fail(ar.cause()); 159 | } 160 | }); 161 | }, v -> new JsonObject()); 162 | } 163 | 164 | private void addToCart(RoutingContext rc) { 165 | String cartId = rc.request().getParam("cartId"); 166 | String itemId = rc.request().getParam("itemId"); 167 | String quantity = rc.request().getParam("quantity"); 168 | 169 | if (cart == null) { 170 | initCartClient(rc); 171 | } 172 | 173 | circuit.executeWithFallback( 174 | future -> { 175 | cart.post("/api/cart/" + cartId + "/" + itemId + "/" + quantity) 176 | .as(BodyCodec.jsonObject()) 177 | .send( ar -> { 178 | if (ar.succeeded()) { 179 | rc.response().end(ar.result().body().toString()); 180 | future.complete(); 181 | } else { 182 | rc.response().end(new JsonObject().toString()); 183 | future.fail(ar.cause()); 184 | } 185 | }); 186 | }, v -> new JsonObject()); 187 | } 188 | 189 | private void deleteFromCart(RoutingContext rc) { 190 | String cartId = rc.request().getParam("cartId"); 191 | String itemId = rc.request().getParam("itemId"); 192 | String quantity = rc.request().getParam("quantity"); 193 | 194 | if (cart == null) { 195 | initCartClient(rc); 196 | } 197 | 198 | circuit.executeWithFallback( 199 | future -> { 200 | cart.delete("/api/cart/" + cartId + "/" + itemId + "/" + quantity) 201 | .as(BodyCodec.jsonObject()) 202 | .send( ar -> { 203 | if (ar.succeeded()) { 204 | rc.response().end(ar.result().body().toString()); 205 | future.complete(); 206 | } else { 207 | rc.response().end(new JsonObject().toString()); 208 | future.fail(ar.cause()); 209 | } 210 | }); 211 | }, v -> new JsonObject()); 212 | } 213 | 214 | private void initCartClient(RoutingContext rc) { 215 | String cartRoute = rc.request().host().replaceAll("^gw-(.*)$", "cart-$1"); 216 | LOG.info("Initiaizing Cart webclient at {}", cartRoute); 217 | cart = WebClient.create(vertx, 218 | new WebClientOptions() 219 | .setDefaultHost(cartRoute) 220 | .setDefaultPort(80)); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /gateway-vertx/src/main/resources/vertx-default-jul-logging.properties: -------------------------------------------------------------------------------- 1 | 2 | handlers=java.util.logging.ConsoleHandler 3 | java.util.logging.SimpleFormatter.format=[%p] %t: %m%n\n 4 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 5 | java.util.logging.ConsoleHandler.level=FINEST 6 | 7 | .level=INFO 8 | io.vertx.ext.web.level=INFO 9 | io.vertx.level=INFO 10 | com.hazelcast.level=INFO 11 | io.netty.util.internal.PlatformDependent.level=SEVERE 12 | io.vertx.servicediscovery.level=SEVERE 13 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | inventory 8 | 1.0-SNAPSHOT 9 | war 10 | CoolStore Inventory Service 11 | CoolStore Inventory Service with WildFly Swarm 12 | 13 | 2017.8.1 14 | 2.3.4 15 | 3.5.30 16 | 1.4.187 17 | 9.4.1207 18 | false 19 | 20 | 21 | 22 | 23 | org.wildfly.swarm 24 | bom-all 25 | ${version.wildfly.swarm} 26 | pom 27 | import 28 | 29 | 30 | io.fabric8 31 | fabric8-project-bom-with-platform-deps 32 | ${fabric8.version} 33 | pom 34 | import 35 | 36 | 37 | 38 | 39 | 40 | org.wildfly.swarm 41 | jaxrs-jsonp 42 | 43 | 44 | org.wildfly.swarm 45 | cdi 46 | 47 | 48 | org.wildfly.swarm 49 | jpa 50 | 51 | 52 | org.wildfly.swarm 53 | monitor 54 | 55 | 56 | com.h2database 57 | h2 58 | ${h2.version} 59 | 60 | 61 | org.postgresql 62 | postgresql 63 | ${postgresql.version} 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-compiler-plugin 71 | 3.6.1 72 | 73 | 1.8 74 | 1.8 75 | 76 | 77 | 78 | maven-war-plugin 79 | 3.1.0 80 | 81 | false 82 | 83 | 84 | 85 | org.wildfly.swarm 86 | wildfly-swarm-plugin 87 | ${version.wildfly.swarm} 88 | 89 | 90 | 91 | package 92 | 93 | 94 | 95 | 96 | 97 | true 98 | 99 | -Dswarm.http.port=9001 100 | 101 | 102 | 103 | io.fabric8 104 | fabric8-maven-plugin 105 | ${fabric8.maven.plugin.version} 106 | 107 | 108 | 109 | resource 110 | build 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | microservice 120 | inventory 121 | 122 | 123 | 124 | 125 | 126 | 127 | wildfly-swarm 128 | 129 | 130 | 131 | redhat-openjdk-18/openjdk18-openshift 132 | 133 | 134 | 135 | 136 | 137 | 138 | /node 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/src/main/java/com/redhat/cloudnative/inventory/Inventory.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | import javax.persistence.Table; 6 | import javax.persistence.UniqueConstraint; 7 | import java.io.Serializable; 8 | 9 | @Entity 10 | @Table(name = "INVENTORY", uniqueConstraints = @UniqueConstraint(columnNames = "itemId")) 11 | public class Inventory implements Serializable { 12 | private static final long serialVersionUID = -8053933344541613739L; 13 | 14 | @Id 15 | private String itemId; 16 | 17 | private int quantity; 18 | 19 | public Inventory() { 20 | } 21 | 22 | public String getItemId() { 23 | return itemId; 24 | } 25 | 26 | public void setItemId(String itemId) { 27 | this.itemId = itemId; 28 | } 29 | 30 | public int getQuantity() { 31 | return quantity; 32 | } 33 | 34 | public void setQuantity(int quantity) { 35 | this.quantity = quantity; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "Inventory [" + 41 | "itemId='" + itemId + '\'' + 42 | ", quantity=" + quantity + 43 | ']'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/src/main/java/com/redhat/cloudnative/inventory/InventoryApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("/") 7 | public class InventoryApplication extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/src/main/java/com/redhat/cloudnative/inventory/InventoryResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.persistence.EntityManager; 5 | import javax.persistence.PersistenceContext; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.PathParam; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import org.wildfly.swarm.health.Health; 12 | import org.wildfly.swarm.health.HealthStatus; 13 | import java.util.Date; 14 | 15 | @Path("/") 16 | @ApplicationScoped 17 | public class InventoryResource { 18 | @PersistenceContext(unitName = "InventoryPU") 19 | private EntityManager em; 20 | 21 | @GET 22 | @Path("/api/inventory/{itemId}") 23 | @Produces(MediaType.APPLICATION_JSON) 24 | public Inventory getAvailability(@PathParam("itemId") String itemId) { 25 | Inventory inventory = em.find(Inventory.class, itemId); 26 | 27 | if (inventory == null) { 28 | inventory = new Inventory(); 29 | inventory.setItemId(itemId); 30 | inventory.setQuantity(0); 31 | } 32 | 33 | return inventory; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/src/main/resources/META-INF/load.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO INVENTORY(itemId, quantity) VALUES (329299, 35) 2 | INSERT INTO INVENTORY(itemId, quantity) VALUES (329199, 12) 3 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165613, 45) 4 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165614, 87) 5 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165954, 43) 6 | INSERT INTO INVENTORY(itemId, quantity) VALUES (444434, 32) 7 | INSERT INTO INVENTORY(itemId, quantity) VALUES (444435, 53) 8 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | java:/jboss/datasources/InventoryDS 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /inventory-wildfly-swarm/src/main/resources/project-stages.yml: -------------------------------------------------------------------------------- 1 | swarm: 2 | datasources: 3 | data-sources: 4 | InventoryDS: 5 | driver-name: h2 6 | connection-url: jdbc:h2:mem:fruits;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 7 | user-name: sa 8 | password: sa -------------------------------------------------------------------------------- /openshift/cart-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | metadata: {} 4 | items: 5 | - apiVersion: v1 6 | kind: BuildConfig 7 | metadata: 8 | annotations: 9 | pipeline.alpha.openshift.io/uses: '[{"name": "cart", "namespace": "", "kind": "DeploymentConfig"}]' 10 | name: cart-service-pipeline 11 | spec: 12 | source: 13 | git: 14 | ref: pipeline 15 | uri: https://github.com/siamaksade/cart-service.git 16 | type: Git 17 | strategy: 18 | type: JenkinsPipeline 19 | jenkinsPipelineStrategy: 20 | jenkinsfilePath: Jenkinsfile 21 | triggers: 22 | - generic: 23 | secret: FiArdDBH 24 | type: Generic 25 | -------------------------------------------------------------------------------- /openshift/cart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | annotations: 5 | iconClass: icon-java 6 | name: cart 7 | objects: 8 | - apiVersion: v1 9 | kind: ImageStream 10 | metadata: 11 | name: cart 12 | labels: 13 | application: cart 14 | spec: 15 | tags: 16 | - name: latest 17 | - apiVersion: v1 18 | kind: BuildConfig 19 | metadata: 20 | name: cart 21 | labels: 22 | application: cart 23 | spec: 24 | output: 25 | to: 26 | kind: ImageStreamTag 27 | name: cart:latest 28 | source: 29 | git: 30 | ref: ${GIT_REF} 31 | uri: ${GIT_URI} 32 | type: Git 33 | strategy: 34 | sourceStrategy: 35 | env: 36 | - name: MAVEN_MIRROR_URL 37 | value: ${MAVEN_MIRROR_URL} 38 | from: 39 | kind: ImageStreamTag 40 | name: java:8 41 | namespace: ${IMAGE_STREAM_NAMESPACE} 42 | type: Source 43 | triggers: 44 | - type: ConfigChange 45 | - apiVersion: v1 46 | kind: DeploymentConfig 47 | metadata: 48 | name: cart 49 | labels: 50 | application: cart 51 | spec: 52 | replicas: 1 53 | selector: 54 | deploymentconfig: cart 55 | strategy: 56 | resources: {} 57 | type: Recreate 58 | template: 59 | metadata: 60 | labels: 61 | application: cart 62 | deploymentconfig: cart 63 | name: cart 64 | spec: 65 | containers: 66 | - env: 67 | - name: CATALOG_ENDPOINT 68 | value: "http://catalog:8080" 69 | image: cart 70 | imagePullPolicy: Always 71 | livenessProbe: 72 | failureThreshold: 5 73 | httpGet: 74 | path: /health 75 | port: 8080 76 | scheme: HTTP 77 | initialDelaySeconds: 45 78 | periodSeconds: 5 79 | successThreshold: 1 80 | timeoutSeconds: 1 81 | name: cart 82 | ports: 83 | - containerPort: 8778 84 | name: jolokia 85 | protocol: TCP 86 | - containerPort: 8080 87 | name: http 88 | protocol: TCP 89 | - containerPort: 8443 90 | name: https 91 | protocol: TCP 92 | readinessProbe: 93 | failureThreshold: 10 94 | httpGet: 95 | path: /health 96 | port: 8080 97 | scheme: HTTP 98 | initialDelaySeconds: 45 99 | periodSeconds: 5 100 | successThreshold: 1 101 | timeoutSeconds: 1 102 | resources: 103 | limits: 104 | memory: 1Gi 105 | cpu: 1 106 | requests: 107 | memory: 512Mi 108 | cpu: 200m 109 | terminationMessagePath: /dev/termination-log 110 | dnsPolicy: ClusterFirst 111 | restartPolicy: Always 112 | securityContext: {} 113 | terminationGracePeriodSeconds: 75 114 | triggers: 115 | - imageChangeParams: 116 | automatic: true 117 | containerNames: 118 | - cart 119 | from: 120 | kind: ImageStreamTag 121 | name: cart:latest 122 | type: ImageChange 123 | - type: ConfigChange 124 | - apiVersion: v1 125 | kind: Service 126 | metadata: 127 | labels: 128 | app: cart 129 | application: cart 130 | name: cart 131 | spec: 132 | ports: 133 | - port: 8080 134 | protocol: TCP 135 | targetPort: 8080 136 | selector: 137 | deploymentconfig: cart 138 | - apiVersion: v1 139 | kind: Route 140 | metadata: 141 | labels: 142 | application: cart 143 | name: cart 144 | spec: 145 | to: 146 | kind: Service 147 | name: cart 148 | weight: 100 149 | parameters: 150 | - displayName: Application name 151 | name: APPLICATION_NAME 152 | required: true 153 | value: cart 154 | - description: Git source URI for application 155 | displayName: Git source repository 156 | name: GIT_URI 157 | required: true 158 | value: https://github.com/siamaksade/cart-service.git 159 | - description: Git branch/tag reference 160 | displayName: Git branch/tag reference 161 | name: GIT_REF 162 | value: master 163 | - description: Maven mirror url. If nexus is deployed locally, use nexus url (e.g. http://nexus.ci:8081/repository/maven-all-public) 164 | displayName: Maven mirror url 165 | name: MAVEN_MIRROR_URL 166 | - displayName: ImageStream Namespace 167 | description: Namespace in which the ImageStreams for Red Hat OpenJDK image is installed. These ImageStreams are normally installed in the openshift namespace. You should only need to modify this if you've installed the ImageStreams in a different namespace/project. 168 | name: IMAGE_STREAM_NAMESPACE 169 | required: true 170 | value: openshift -------------------------------------------------------------------------------- /openshift/coolstore-build-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | annotations: 5 | description: CoolStore Microservices Application Template 6 | iconClass: icon-openjdk 7 | tags: microservice,jboss,spring 8 | "openshift.io/display-name": "CoolStore Builds" 9 | name: coolstore-builds 10 | objects: 11 | # UI 12 | - apiVersion: v1 13 | kind: ImageStream 14 | metadata: 15 | name: web-ui 16 | labels: 17 | application: coolstore 18 | component: web-ui 19 | spec: 20 | tags: 21 | - name: latest 22 | - apiVersion: v1 23 | kind: BuildConfig 24 | metadata: 25 | name: web-ui 26 | labels: 27 | app: web-ui 28 | spec: 29 | output: 30 | to: 31 | kind: ImageStreamTag 32 | name: web-ui:${BUILD_TAG} 33 | source: 34 | contextDir: web-nodejs 35 | git: 36 | ref: ${GIT_REF} 37 | uri: ${GIT_URI} 38 | type: Git 39 | strategy: 40 | sourceStrategy: 41 | from: 42 | kind: ImageStreamTag 43 | name: nodejs:4 44 | namespace: openshift 45 | type: Source 46 | triggers: 47 | - imageChange: {} 48 | type: ImageChange 49 | - type: ConfigChange 50 | # Coolstore Gateway 51 | - apiVersion: v1 52 | kind: ImageStream 53 | metadata: 54 | name: coolstore-gw 55 | labels: 56 | application: coolstore 57 | component: coolstore-gw 58 | spec: 59 | tags: 60 | - name: latest 61 | - apiVersion: v1 62 | kind: BuildConfig 63 | metadata: 64 | name: coolstore-gw 65 | labels: 66 | application: coolstore 67 | component: coolstore-gw 68 | spec: 69 | output: 70 | to: 71 | kind: ImageStreamTag 72 | name: coolstore-gw:${BUILD_TAG} 73 | source: 74 | contextDir: gateway-vertx 75 | git: 76 | ref: ${GIT_REF} 77 | uri: ${GIT_URI} 78 | type: Git 79 | strategy: 80 | sourceStrategy: 81 | env: 82 | - name: MAVEN_MIRROR_URL 83 | value: ${MAVEN_MIRROR_URL} 84 | from: 85 | kind: ImageStreamTag 86 | name: java:8 87 | namespace: openshift 88 | type: Source 89 | triggers: 90 | - type: ConfigChange 91 | - imageChange: {} 92 | type: ImageChange 93 | # Catalog Service 94 | - apiVersion: v1 95 | kind: ImageStream 96 | metadata: 97 | name: catalog 98 | labels: 99 | application: coolstore 100 | component: catalog 101 | spec: 102 | tags: 103 | - name: latest 104 | - apiVersion: v1 105 | kind: BuildConfig 106 | metadata: 107 | name: catalog 108 | labels: 109 | application: coolstore 110 | component: catalog 111 | spec: 112 | output: 113 | to: 114 | kind: ImageStreamTag 115 | name: catalog:${BUILD_TAG} 116 | source: 117 | contextDir: catalog-spring-boot 118 | git: 119 | ref: ${GIT_REF} 120 | uri: ${GIT_URI} 121 | type: Git 122 | strategy: 123 | sourceStrategy: 124 | env: 125 | - name: MAVEN_MIRROR_URL 126 | value: ${MAVEN_MIRROR_URL} 127 | from: 128 | kind: ImageStreamTag 129 | name: java:8 130 | namespace: openshift 131 | type: Source 132 | triggers: 133 | - type: ConfigChange 134 | - imageChange: {} 135 | type: ImageChange 136 | # Cart Service 137 | - apiVersion: v1 138 | kind: ImageStream 139 | metadata: 140 | name: cart 141 | labels: 142 | application: coolstore 143 | component: cart 144 | spec: 145 | tags: 146 | - name: latest 147 | - apiVersion: v1 148 | kind: BuildConfig 149 | metadata: 150 | name: cart 151 | labels: 152 | application: coolstore 153 | component: cart 154 | spec: 155 | output: 156 | to: 157 | kind: ImageStreamTag 158 | name: cart:${BUILD_TAG} 159 | source: 160 | contextDir: cart-spring-boot 161 | git: 162 | ref: ${GIT_REF} 163 | uri: ${GIT_URI} 164 | type: Git 165 | strategy: 166 | sourceStrategy: 167 | env: 168 | - name: MAVEN_MIRROR_URL 169 | value: ${MAVEN_MIRROR_URL} 170 | from: 171 | kind: ImageStreamTag 172 | name: java:8 173 | namespace: openshift 174 | type: Source 175 | triggers: 176 | - type: ConfigChange 177 | - imageChange: {} 178 | type: ImageChange 179 | # Inventory Service 180 | - apiVersion: v1 181 | kind: ImageStream 182 | metadata: 183 | name: inventory 184 | labels: 185 | application: coolstore 186 | component: inventory 187 | spec: 188 | tags: 189 | - name: latest 190 | - apiVersion: v1 191 | kind: BuildConfig 192 | metadata: 193 | name: inventory 194 | labels: 195 | application: coolstore 196 | component: inventory 197 | spec: 198 | output: 199 | to: 200 | kind: ImageStreamTag 201 | name: inventory:${BUILD_TAG} 202 | source: 203 | contextDir: inventory-wildfly-swarm 204 | git: 205 | ref: ${GIT_REF} 206 | uri: ${GIT_URI} 207 | type: Git 208 | strategy: 209 | sourceStrategy: 210 | env: 211 | - name: MAVEN_MIRROR_URL 212 | value: ${MAVEN_MIRROR_URL} 213 | from: 214 | kind: ImageStreamTag 215 | name: java:8 216 | namespace: openshift 217 | type: Source 218 | triggers: 219 | - type: ConfigChange 220 | - imageChange: {} 221 | type: ImageChange 222 | parameters: 223 | - description: Git source URI for application 224 | displayName: Git source repository 225 | name: GIT_URI 226 | required: true 227 | value: https://github.com/openshift-labs/devops-labs.git 228 | - displayName: Git branch/tag reference 229 | name: GIT_REF 230 | required: true 231 | value: ocp-3.11 232 | - description: Maven mirror url. If nexus is deployed locally, use nexus url (e.g. http://nexus.ci:8081/repository/maven-all-public) 233 | displayName: Maven mirror url 234 | name: MAVEN_MIRROR_URL 235 | - displayName: Build Tag 236 | description: Built tag to push the built images to 237 | name: BUILD_TAG 238 | required: true 239 | value: latest 240 | -------------------------------------------------------------------------------- /openshift/coolstore-deployment-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | annotations: 5 | description: CoolStore Microservices Application Template 6 | iconClass: icon-openjdk 7 | tags: microservice,jboss,spring 8 | "openshift.io/display-name": "CoolStore Deployments" 9 | name: coolstore-deployments 10 | objects: 11 | - apiVersion: v1 12 | groupNames: null 13 | kind: RoleBinding 14 | metadata: 15 | name: default_edit 16 | labels: 17 | app: coolstore 18 | roleRef: 19 | name: view 20 | subjects: 21 | - kind: ServiceAccount 22 | name: default 23 | ##### IMAGESTREAMS 24 | - apiVersion: v1 25 | kind: ImageStream 26 | metadata: 27 | name: cart 28 | labels: 29 | app: coolstore 30 | component: cart 31 | spec: 32 | tags: 33 | - name: ${APP_VERSION} 34 | from: 35 | kind: ImageStreamTag 36 | name: cart:${APP_VERSION} 37 | namespace: openshift 38 | # UI 39 | - apiVersion: v1 40 | kind: DeploymentConfig 41 | metadata: 42 | name: web-ui 43 | labels: 44 | app: web-ui 45 | component: web-ui 46 | spec: 47 | replicas: 1 48 | selector: 49 | deploymentconfig: web-ui 50 | strategy: 51 | resources: {} 52 | type: Recreate 53 | template: 54 | metadata: 55 | labels: 56 | app: coolstore 57 | component: web-ui 58 | deploymentconfig: web-ui 59 | spec: 60 | containers: 61 | - env: 62 | - name: KUBERNETES_NAMESPACE 63 | valueFrom: 64 | fieldRef: 65 | fieldPath: metadata.namespace 66 | - name: COOLSTORE_GW_SERVICE 67 | value: gw 68 | - name: HOSTNAME_HTTP 69 | value: web-ui:8080 70 | image: ' ' 71 | imagePullPolicy: Always 72 | name: web-ui 73 | ports: 74 | - containerPort: 8080 75 | protocol: TCP 76 | livenessProbe: 77 | failureThreshold: 5 78 | httpGet: 79 | path: / 80 | port: 8080 81 | scheme: HTTP 82 | initialDelaySeconds: 120 83 | periodSeconds: 5 84 | successThreshold: 1 85 | timeoutSeconds: 5 86 | readinessProbe: 87 | failureThreshold: 5 88 | httpGet: 89 | path: / 90 | port: 8080 91 | scheme: HTTP 92 | initialDelaySeconds: 15 93 | periodSeconds: 5 94 | successThreshold: 1 95 | timeoutSeconds: 1 96 | resources: 97 | limits: 98 | cpu: 500m 99 | memory: 1Gi 100 | requests: 101 | cpu: 50m 102 | memory: 128Mi 103 | terminationMessagePath: /dev/termination-log 104 | dnsPolicy: ClusterFirst 105 | restartPolicy: Always 106 | securityContext: {} 107 | terminationGracePeriodSeconds: 30 108 | triggers: 109 | - imageChangeParams: 110 | automatic: true 111 | containerNames: 112 | - web-ui 113 | from: 114 | kind: ImageStreamTag 115 | name: web-ui:${APP_VERSION} 116 | namespace: openshift 117 | type: ImageChange 118 | - type: ConfigChange 119 | - apiVersion: v1 120 | kind: Service 121 | metadata: 122 | labels: 123 | app: web-ui 124 | app: coolstore 125 | component: web-ui 126 | name: web-ui 127 | spec: 128 | ports: 129 | - name: 8080-tcp 130 | port: 8080 131 | protocol: TCP 132 | targetPort: 8080 133 | selector: 134 | deploymentconfig: web-ui 135 | - apiVersion: v1 136 | kind: Route 137 | metadata: 138 | name: web-ui 139 | labels: 140 | app: coolstore 141 | component: web-ui 142 | spec: 143 | to: 144 | kind: Service 145 | name: web-ui 146 | # Coolstore Gateway 147 | - apiVersion: v1 148 | kind: DeploymentConfig 149 | metadata: 150 | name: coolstore-gw 151 | labels: 152 | app: coolstore 153 | component: coolstore-gw 154 | spec: 155 | replicas: 1 156 | selector: 157 | deploymentconfig: coolstore-gw 158 | strategy: 159 | resources: {} 160 | type: Recreate 161 | template: 162 | metadata: 163 | labels: 164 | app: coolstore-gw 165 | deploymentconfig: coolstore-gw 166 | name: coolstore-gw 167 | spec: 168 | containers: 169 | - env: 170 | - name: KUBERNETES_NAMESPACE 171 | valueFrom: 172 | fieldRef: 173 | fieldPath: metadata.namespace 174 | - name: DISABLE_CART_DISCOVERY 175 | value: "true" 176 | image: ' ' 177 | livenessProbe: 178 | httpGet: 179 | path: /health 180 | port: 8080 181 | initialDelaySeconds: 60 182 | readinessProbe: 183 | httpGet: 184 | path: /health 185 | port: 8080 186 | initialDelaySeconds: 10 187 | name: coolstore-gw 188 | ports: 189 | - containerPort: 8778 190 | name: jolokia 191 | resources: 192 | limits: 193 | cpu: 1 194 | memory: 2Gi 195 | requests: 196 | cpu: 200m 197 | memory: 512Mi 198 | triggers: 199 | - imageChangeParams: 200 | automatic: true 201 | containerNames: 202 | - coolstore-gw 203 | from: 204 | kind: ImageStreamTag 205 | name: coolstore-gw:${APP_VERSION} 206 | namespace: openshift 207 | type: ImageChange 208 | - type: ConfigChange 209 | - apiVersion: v1 210 | kind: Service 211 | metadata: 212 | labels: 213 | app: coolstore 214 | component: coolstore-gw 215 | name: gw 216 | spec: 217 | ports: 218 | - port: 8080 219 | protocol: TCP 220 | targetPort: 8080 221 | selector: 222 | deploymentconfig: coolstore-gw 223 | - apiVersion: v1 224 | kind: Route 225 | metadata: 226 | name: gw 227 | labels: 228 | app: coolstore 229 | component: coolstore-gw 230 | spec: 231 | to: 232 | kind: Service 233 | name: gw 234 | # Catalog Service 235 | - apiVersion: v1 236 | kind: DeploymentConfig 237 | metadata: 238 | name: catalog 239 | labels: 240 | app: coolstore 241 | component: catalog 242 | spec: 243 | replicas: 1 244 | selector: 245 | deploymentconfig: catalog 246 | strategy: 247 | resources: {} 248 | type: Recreate 249 | template: 250 | metadata: 251 | labels: 252 | app: coolstore 253 | component: catalog 254 | deploymentconfig: catalog 255 | name: catalog 256 | spec: 257 | containers: 258 | - env: 259 | - name: JWS_ADMIN_USERNAME 260 | value: Skq3VtCd 261 | - name: JWS_ADMIN_PASSWORD 262 | value: oktt6yhw 263 | - name: DB_USERNAME 264 | value: ${CATALOG_DB_USERNAME} 265 | - name: DB_PASSWORD 266 | value: ${CATALOG_DB_PASSWORD} 267 | - name: DB_NAME 268 | value: catalogdb 269 | - name: DB_SERVER 270 | value: catalog-mongodb 271 | image: ' ' 272 | imagePullPolicy: Always 273 | name: catalog 274 | ports: 275 | - containerPort: 8778 276 | name: jolokia 277 | protocol: TCP 278 | - containerPort: 8080 279 | name: http 280 | protocol: TCP 281 | readinessProbe: 282 | failureThreshold: 5 283 | httpGet: 284 | path: /health 285 | port: 8080 286 | scheme: HTTP 287 | initialDelaySeconds: 15 288 | periodSeconds: 5 289 | successThreshold: 1 290 | timeoutSeconds: 1 291 | livenessProbe: 292 | failureThreshold: 5 293 | httpGet: 294 | path: /health 295 | port: 8080 296 | scheme: HTTP 297 | initialDelaySeconds: 15 298 | periodSeconds: 5 299 | successThreshold: 1 300 | timeoutSeconds: 1 301 | resources: 302 | limits: 303 | cpu: 2 304 | memory: 2Gi 305 | requests: 306 | cpu: 100m 307 | memory: 256Mi 308 | terminationMessagePath: /dev/termination-log 309 | dnsPolicy: ClusterFirst 310 | restartPolicy: Always 311 | securityContext: {} 312 | terminationGracePeriodSeconds: 75 313 | triggers: 314 | - imageChangeParams: 315 | automatic: true 316 | containerNames: 317 | - catalog 318 | from: 319 | kind: ImageStreamTag 320 | name: catalog:${APP_VERSION} 321 | namespace: openshift 322 | type: ImageChange 323 | - type: ConfigChange 324 | - apiVersion: v1 325 | kind: Service 326 | metadata: 327 | annotations: 328 | service.alpha.openshift.io/dependencies: '[{"name":"catalog-mongodb","namespace":"","kind":"Service"}]' 329 | labels: 330 | app: catalog 331 | app: coolstore 332 | component: catalog 333 | name: catalog 334 | spec: 335 | ports: 336 | - port: 8080 337 | protocol: TCP 338 | targetPort: 8080 339 | selector: 340 | deploymentconfig: catalog 341 | - apiVersion: v1 342 | kind: Route 343 | metadata: 344 | labels: 345 | app: coolstore 346 | component: catalog 347 | name: catalog 348 | spec: 349 | to: 350 | kind: Service 351 | name: catalog 352 | weight: 100 353 | - apiVersion: v1 354 | kind: Service 355 | metadata: 356 | labels: 357 | app: catalog 358 | app: coolstore 359 | component: catalog 360 | name: catalog-mongodb 361 | spec: 362 | ports: 363 | - name: mongo 364 | port: 27017 365 | protocol: TCP 366 | targetPort: 27017 367 | selector: 368 | deploymentconfig: catalog-mongodb 369 | sessionAffinity: None 370 | type: ClusterIP 371 | - apiVersion: v1 372 | kind: DeploymentConfig 373 | metadata: 374 | labels: 375 | app: coolstore 376 | component: catalog 377 | name: catalog-mongodb 378 | spec: 379 | replicas: 1 380 | selector: 381 | deploymentconfig: catalog-mongodb 382 | strategy: 383 | recreateParams: 384 | timeoutSeconds: 600 385 | resources: {} 386 | type: Recreate 387 | template: 388 | metadata: 389 | labels: 390 | app: coolstore 391 | component: catalog 392 | deploymentconfig: catalog-mongodb 393 | spec: 394 | containers: 395 | - env: 396 | - name: KUBERNETES_NAMESPACE 397 | valueFrom: 398 | fieldRef: 399 | fieldPath: metadata.namespace 400 | - name: MONGODB_USER 401 | value: ${CATALOG_DB_USERNAME} 402 | - name: MONGODB_PASSWORD 403 | value: ${CATALOG_DB_PASSWORD} 404 | - name: MONGODB_DATABASE 405 | value: catalogdb 406 | - name: MONGODB_ADMIN_PASSWORD 407 | value: ${CATALOG_DB_PASSWORD} 408 | image: ' ' 409 | imagePullPolicy: IfNotPresent 410 | livenessProbe: 411 | failureThreshold: 3 412 | initialDelaySeconds: 30 413 | periodSeconds: 10 414 | successThreshold: 1 415 | tcpSocket: 416 | port: 27017 417 | timeoutSeconds: 1 418 | name: catalog-mongodb 419 | ports: 420 | - containerPort: 27017 421 | protocol: TCP 422 | readinessProbe: 423 | exec: 424 | command: 425 | - /bin/sh 426 | - -i 427 | - -c 428 | - mongo 127.0.0.1:27017/$MONGODB_DATABASE -u $MONGODB_USER -p $MONGODB_PASSWORD 429 | --eval="quit()" 430 | failureThreshold: 3 431 | initialDelaySeconds: 3 432 | periodSeconds: 10 433 | successThreshold: 1 434 | timeoutSeconds: 1 435 | resources: 436 | limits: 437 | cpu: 500m 438 | memory: 1Gi 439 | requests: 440 | cpu: 100m 441 | memory: 256Mi 442 | securityContext: 443 | capabilities: {} 444 | privileged: false 445 | terminationMessagePath: /dev/termination-log 446 | volumeMounts: 447 | - mountPath: /var/lib/mongodb/data 448 | name: mongodb-data 449 | dnsPolicy: ClusterFirst 450 | restartPolicy: Always 451 | securityContext: {} 452 | terminationGracePeriodSeconds: 30 453 | volumes: 454 | - name: mongodb-data 455 | persistentVolumeClaim: 456 | claimName: mongodb-data-pv 457 | test: false 458 | triggers: 459 | - imageChangeParams: 460 | automatic: true 461 | containerNames: 462 | - catalog-mongodb 463 | from: 464 | kind: ImageStreamTag 465 | name: mongodb:3.2 466 | namespace: openshift 467 | type: ImageChange 468 | - type: ConfigChange 469 | - apiVersion: v1 470 | kind: PersistentVolumeClaim 471 | metadata: 472 | labels: 473 | app: coolstore 474 | component: catalog 475 | name: mongodb-data-pv 476 | spec: 477 | accessModes: 478 | - ReadWriteOnce 479 | resources: 480 | requests: 481 | storage: 1Gi 482 | # Cart Service 483 | - apiVersion: v1 484 | kind: DeploymentConfig 485 | metadata: 486 | name: cart 487 | labels: 488 | app: coolstore 489 | component: cart 490 | spec: 491 | replicas: 1 492 | selector: 493 | deploymentconfig: cart 494 | strategy: 495 | resources: {} 496 | type: Recreate 497 | template: 498 | metadata: 499 | labels: 500 | app: coolstore 501 | component: cart 502 | deploymentconfig: cart 503 | name: cart 504 | spec: 505 | containers: 506 | - env: 507 | - name: CATALOG_ENDPOINT 508 | value: "http://catalog:8080" 509 | - name: APPLICATION_MODE 510 | value: prod 511 | image: ' ' 512 | imagePullPolicy: Always 513 | livenessProbe: 514 | failureThreshold: 5 515 | httpGet: 516 | path: /health 517 | port: 8080 518 | scheme: HTTP 519 | initialDelaySeconds: 45 520 | periodSeconds: 5 521 | successThreshold: 1 522 | timeoutSeconds: 1 523 | name: cart 524 | ports: 525 | - containerPort: 8778 526 | name: jolokia 527 | protocol: TCP 528 | - containerPort: 8080 529 | name: http 530 | protocol: TCP 531 | - containerPort: 8443 532 | name: https 533 | protocol: TCP 534 | readinessProbe: 535 | failureThreshold: 10 536 | httpGet: 537 | path: /health 538 | port: 8080 539 | scheme: HTTP 540 | initialDelaySeconds: 45 541 | periodSeconds: 5 542 | successThreshold: 1 543 | timeoutSeconds: 1 544 | resources: 545 | limits: 546 | memory: 1Gi 547 | requests: 548 | memory: 200Mi 549 | terminationMessagePath: /dev/termination-log 550 | dnsPolicy: ClusterFirst 551 | restartPolicy: Always 552 | securityContext: {} 553 | terminationGracePeriodSeconds: 75 554 | triggers: 555 | - imageChangeParams: 556 | automatic: true 557 | containerNames: 558 | - cart 559 | from: 560 | kind: ImageStreamTag 561 | name: cart:${APP_VERSION} 562 | type: ImageChange 563 | - type: ConfigChange 564 | - apiVersion: v1 565 | kind: Service 566 | metadata: 567 | labels: 568 | app: cart 569 | app: coolstore 570 | component: cart 571 | name: cart 572 | spec: 573 | ports: 574 | - port: 8080 575 | protocol: TCP 576 | targetPort: 8080 577 | selector: 578 | deploymentconfig: cart 579 | - apiVersion: v1 580 | kind: Route 581 | metadata: 582 | labels: 583 | app: coolstore 584 | component: cart 585 | name: cart 586 | spec: 587 | to: 588 | kind: Service 589 | name: cart 590 | weight: 100 591 | # Inventory Service 592 | - apiVersion: v1 593 | kind: DeploymentConfig 594 | metadata: 595 | name: inventory 596 | labels: 597 | app: coolstore 598 | component: inventory 599 | spec: 600 | replicas: 1 601 | selector: 602 | deploymentconfig: inventory 603 | strategy: 604 | resources: {} 605 | type: Recreate 606 | template: 607 | metadata: 608 | labels: 609 | deploymentconfig: inventory 610 | app: coolstore 611 | component: inventory 612 | name: inventory 613 | spec: 614 | containers: 615 | - env: 616 | - name: OPENSHIFT_KUBE_PING_LABELS 617 | value: application=inventory 618 | - name: OPENSHIFT_KUBE_PING_NAMESPACE 619 | valueFrom: 620 | fieldRef: 621 | fieldPath: metadata.namespace 622 | - name: MQ_CLUSTER_PASSWORD 623 | value: 7mzX0pLV03 624 | - name: JGROUPS_CLUSTER_PASSWORD 625 | value: CqUo3fYDTv 626 | - name: AUTO_DEPLOY_EXPLODED 627 | value: "false" 628 | - name: DB_SERVICE_PREFIX_MAPPING 629 | value: inventory-postgresql=DB 630 | - name: DB_JNDI 631 | value: java:jboss/datasources/InventoryDS 632 | - name: DB_USERNAME 633 | value: ${INVENTORY_DB_USERNAME} 634 | - name: DB_PASSWORD 635 | value: ${INVENTORY_DB_PASSWORD} 636 | - name: DB_DATABASE 637 | value: inventorydb 638 | image: ' ' 639 | imagePullPolicy: Always 640 | lifecycle: 641 | preStop: 642 | exec: 643 | command: 644 | - /opt/eap/bin/jboss-cli.sh 645 | - -c 646 | - :shutdown(timeout=60) 647 | livenessProbe: 648 | failureThreshold: 5 649 | httpGet: 650 | path: /node 651 | port: 8080 652 | scheme: HTTP 653 | initialDelaySeconds: 120 654 | periodSeconds: 5 655 | successThreshold: 1 656 | timeoutSeconds: 5 657 | name: inventory 658 | ports: 659 | - containerPort: 8778 660 | name: jolokia 661 | protocol: TCP 662 | - containerPort: 8080 663 | name: http 664 | protocol: TCP 665 | - containerPort: 8888 666 | name: ping 667 | protocol: TCP 668 | readinessProbe: 669 | failureThreshold: 5 670 | httpGet: 671 | path: /node 672 | port: 8080 673 | scheme: HTTP 674 | initialDelaySeconds: 15 675 | periodSeconds: 5 676 | successThreshold: 1 677 | timeoutSeconds: 1 678 | resources: 679 | limits: 680 | cpu: 500m 681 | memory: 1Gi 682 | requests: 683 | cpu: 100m 684 | memory: 512Mi 685 | terminationMessagePath: /dev/termination-log 686 | dnsPolicy: ClusterFirst 687 | restartPolicy: Always 688 | securityContext: {} 689 | terminationGracePeriodSeconds: 75 690 | triggers: 691 | - imageChangeParams: 692 | automatic: true 693 | containerNames: 694 | - inventory 695 | from: 696 | kind: ImageStreamTag 697 | name: inventory:${APP_VERSION} 698 | namespace: openshift 699 | type: ImageChange 700 | - type: ConfigChange 701 | - apiVersion: v1 702 | kind: Service 703 | metadata: 704 | annotations: 705 | service.alpha.openshift.io/dependencies: '[{"name":"inventory-postgresql","namespace":"","kind":"Service"}]' 706 | labels: 707 | app: inventory 708 | app: coolstore 709 | component: inventory 710 | name: inventory 711 | spec: 712 | ports: 713 | - port: 8080 714 | protocol: TCP 715 | targetPort: 8080 716 | selector: 717 | deploymentconfig: inventory 718 | - apiVersion: v1 719 | kind: Route 720 | metadata: 721 | labels: 722 | app: coolstore 723 | component: inventory 724 | name: inventory 725 | spec: 726 | to: 727 | kind: Service 728 | name: inventory 729 | weight: 100 730 | - apiVersion: v1 731 | kind: DeploymentConfig 732 | metadata: 733 | name: inventory-postgresql 734 | labels: 735 | component: inventory 736 | app: coolstore 737 | spec: 738 | replicas: 1 739 | selector: 740 | deploymentconfig: inventory-postgresql 741 | strategy: 742 | type: Recreate 743 | template: 744 | metadata: 745 | labels: 746 | app: coolstore 747 | component: inventory 748 | deploymentconfig: inventory-postgresql 749 | name: inventory-postgresql 750 | spec: 751 | containers: 752 | - env: 753 | - name: POSTGRESQL_USER 754 | value: ${INVENTORY_DB_USERNAME} 755 | - name: POSTGRESQL_PASSWORD 756 | value: ${INVENTORY_DB_PASSWORD} 757 | - name: POSTGRESQL_DATABASE 758 | value: inventorydb 759 | image: ' ' 760 | imagePullPolicy: Always 761 | name: inventory-postgresql 762 | ports: 763 | - containerPort: 5432 764 | protocol: TCP 765 | volumeMounts: 766 | - mountPath: /var/lib/pgsql/data 767 | name: inventory-postgresql-data 768 | livenessProbe: 769 | initialDelaySeconds: 30 770 | tcpSocket: 771 | port: 5432 772 | timeoutSeconds: 1 773 | readinessProbe: 774 | exec: 775 | command: 776 | - /bin/sh 777 | - -i 778 | - -c 779 | - psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1' 780 | initialDelaySeconds: 5 781 | timeoutSeconds: 1 782 | resources: 783 | limits: 784 | cpu: 500m 785 | memory: 512Mi 786 | requests: 787 | cpu: 50m 788 | memory: 128Mi 789 | terminationGracePeriodSeconds: 60 790 | volumes: 791 | - name: inventory-postgresql-data 792 | persistentVolumeClaim: 793 | claimName: inventory-postgresql-pv 794 | triggers: 795 | - imageChangeParams: 796 | automatic: true 797 | containerNames: 798 | - inventory-postgresql 799 | from: 800 | kind: ImageStreamTag 801 | name: postgresql:latest 802 | namespace: openshift 803 | type: ImageChange 804 | - type: ConfigChange 805 | - apiVersion: v1 806 | kind: Service 807 | metadata: 808 | labels: 809 | app: coolstore 810 | component: inventory 811 | name: inventory-postgresql 812 | spec: 813 | ports: 814 | - port: 5432 815 | targetPort: 5432 816 | selector: 817 | deploymentconfig: inventory-postgresql 818 | - apiVersion: v1 819 | kind: PersistentVolumeClaim 820 | metadata: 821 | labels: 822 | app: coolstore 823 | component: inventory 824 | name: inventory-postgresql-pv 825 | spec: 826 | accessModes: 827 | - ReadWriteOnce 828 | resources: 829 | requests: 830 | storage: 1Gi 831 | parameters: 832 | - description: CoolStore application image version to be deployed (imagestreams should exist) 833 | displayName: CoolStore Version 834 | name: APP_VERSION 835 | required: true 836 | value: prod 837 | - description: Hostname suffix used for routes e.g. cart- inventory- 838 | displayName: Hostname Suffix 839 | name: HOSTNAME_SUFFIX 840 | required: true 841 | - description: Catalog Service database user name 842 | displayName: Catalog Database Username 843 | from: user[a-zA-Z0-9]{3} 844 | generate: expression 845 | name: CATALOG_DB_USERNAME 846 | required: true 847 | - description: Catalog Service database user password 848 | displayName: Catalog Database Password 849 | from: '[a-zA-Z0-9]{8}' 850 | generate: expression 851 | name: CATALOG_DB_PASSWORD 852 | required: true 853 | - description: Inventory Service database user name 854 | displayName: Inventory Database Username 855 | from: user[a-zA-Z0-9]{3} 856 | generate: expression 857 | name: INVENTORY_DB_USERNAME 858 | required: true 859 | - description: Inventory Service database user password 860 | displayName: Inventory Database Password 861 | from: '[a-zA-Z0-9]{8}' 862 | generate: expression 863 | name: INVENTORY_DB_PASSWORD 864 | required: true 865 | -------------------------------------------------------------------------------- /solutions/lab-10/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/openshift3/jenkins-slave-maven-rhel7 2 | 3 | MAINTAINER Siamak Sadeghianfar 4 | 5 | ENV GRADLE_VERSION=4.9 6 | 7 | USER root 8 | 9 | RUN curl -skL -o /tmp/gradle-bin.zip https://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip && \ 10 | mkdir -p /opt/gradle && \ 11 | unzip -q /tmp/gradle-bin.zip -d /opt/gradle && \ 12 | ln -sf /opt/gradle/gradle-$GRADLE_VERSION/bin/gradle /usr/local/bin/gradle 13 | 14 | RUN chown -R 1001:0 /opt/gradle && \ 15 | chmod -R g+rw /opt/gradle 16 | 17 | USER 1001 -------------------------------------------------------------------------------- /solutions/lab-3/cart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | annotations: 5 | iconClass: icon-java 6 | tags: microservice,jboss 7 | name: cart 8 | objects: 9 | - apiVersion: v1 10 | kind: ImageStream 11 | metadata: 12 | name: cart 13 | labels: 14 | application: cart 15 | spec: 16 | tags: 17 | - name: latest 18 | - apiVersion: v1 19 | kind: BuildConfig 20 | metadata: 21 | name: cart 22 | labels: 23 | application: cart 24 | spec: 25 | output: 26 | to: 27 | kind: ImageStreamTag 28 | name: cart:latest 29 | source: 30 | git: 31 | ref: ${GIT_REF} 32 | uri: ${GIT_URI} 33 | type: Git 34 | strategy: 35 | sourceStrategy: 36 | env: 37 | - name: MAVEN_MIRROR_URL 38 | value: ${MAVEN_MIRROR_URL} 39 | from: 40 | kind: ImageStreamTag 41 | name: java:8 42 | namespace: openshift 43 | type: Source 44 | triggers: 45 | - type: ConfigChange 46 | - imageChange: {} 47 | type: ImageChange 48 | - apiVersion: v1 49 | kind: DeploymentConfig 50 | metadata: 51 | name: cart 52 | labels: 53 | application: cart 54 | spec: 55 | replicas: 1 56 | selector: 57 | deploymentconfig: cart 58 | strategy: 59 | resources: {} 60 | type: Recreate 61 | template: 62 | metadata: 63 | labels: 64 | application: cart 65 | deploymentconfig: cart 66 | name: cart 67 | spec: 68 | containers: 69 | - env: 70 | - name: CATALOG_ENDPOINT 71 | value: "http://catalog:8080" 72 | image: cart 73 | imagePullPolicy: Always 74 | livenessProbe: 75 | failureThreshold: 5 76 | httpGet: 77 | path: /health 78 | port: 8080 79 | scheme: HTTP 80 | initialDelaySeconds: 45 81 | periodSeconds: 5 82 | successThreshold: 1 83 | timeoutSeconds: 1 84 | name: cart 85 | ports: 86 | - containerPort: 8778 87 | name: jolokia 88 | protocol: TCP 89 | - containerPort: 8080 90 | name: http 91 | protocol: TCP 92 | - containerPort: 8443 93 | name: https 94 | protocol: TCP 95 | readinessProbe: 96 | failureThreshold: 10 97 | httpGet: 98 | path: /health 99 | port: 8080 100 | scheme: HTTP 101 | initialDelaySeconds: 45 102 | periodSeconds: 5 103 | successThreshold: 1 104 | timeoutSeconds: 1 105 | resources: 106 | limits: 107 | memory: 1Gi 108 | requests: 109 | memory: 200Mi 110 | terminationMessagePath: /dev/termination-log 111 | dnsPolicy: ClusterFirst 112 | restartPolicy: Always 113 | securityContext: {} 114 | terminationGracePeriodSeconds: 75 115 | triggers: 116 | - imageChangeParams: 117 | automatic: true 118 | containerNames: 119 | - cart 120 | from: 121 | kind: ImageStreamTag 122 | name: cart:latest 123 | type: ImageChange 124 | - type: ConfigChange 125 | - apiVersion: v1 126 | kind: Service 127 | metadata: 128 | labels: 129 | app: cart 130 | application: cart 131 | name: cart 132 | spec: 133 | ports: 134 | - port: 8080 135 | protocol: TCP 136 | targetPort: 8080 137 | selector: 138 | deploymentconfig: cart 139 | - apiVersion: v1 140 | kind: Route 141 | metadata: 142 | labels: 143 | app: cart 144 | application: cart 145 | name: cart 146 | spec: 147 | to: 148 | kind: Service 149 | name: cart 150 | weight: 100 151 | wildcardPolicy: None 152 | parameters: 153 | - displayName: Application name 154 | name: APPLICATION_NAME 155 | required: true 156 | value: cart 157 | - description: Git source URI for application 158 | displayName: Git source repository 159 | name: GIT_URI 160 | required: true 161 | - description: Git branch/tag reference 162 | displayName: Git branch/tag reference 163 | name: GIT_REF 164 | - description: Sub-directory in the Git repo for cart service 165 | displayName: Git context dir 166 | name: GIT_CONTEXT_DIR 167 | - description: Maven mirror url. If nexus is deployed locally, use nexus url (e.g. http://nexus.ci:8081/repository/maven-all-public) 168 | displayName: Maven mirror url 169 | name: MAVEN_MIRROR_URL 170 | -------------------------------------------------------------------------------- /solutions/lab-4/cart-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: BuildConfig 3 | metadata: 4 | name: cart-pipeline 5 | spec: 6 | strategy: 7 | jenkinsPipelineStrategy: 8 | jenkinsfile: |- 9 | pipeline { 10 | agent any 11 | stages { 12 | stage('Build') { 13 | steps { 14 | script { 15 | openshift.withCluster() { 16 | openshift.withProject() { 17 | openshift.startBuild("cart").logs('-f') 18 | } 19 | } 20 | } 21 | } 22 | } 23 | stage('Deploy') { 24 | steps { 25 | script { 26 | openshift.withCluster() { 27 | openshift.withProject() { 28 | dc = openshift.selector("dc", "cart") 29 | dc.rollout().latest() 30 | timeout(10) { 31 | dc.rollout().status("-w") 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | stage('Test') { 39 | steps { 40 | sh "curl -s -X POST http://cart:8080/api/cart/dummy/666/1" 41 | sh "curl -s http://cart:8080/api/cart/dummy | grep 'Dummy Product'" 42 | } 43 | } 44 | } 45 | } 46 | type: JenkinsPipeline -------------------------------------------------------------------------------- /solutions/lab-5/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | label 'maven' 4 | } 5 | stages { 6 | stage('Build App') { 7 | steps { 8 | sh "mvn clean package -s src/main/config/settings.xml" 9 | } 10 | } 11 | stage('Integration Test') { 12 | steps { 13 | sh "mvn verify -s src/main/config/settings.xml" 14 | } 15 | } 16 | stage('Build Image') { 17 | steps { 18 | script { 19 | openshift.withCluster() { 20 | openshift.withProject() { 21 | openshift.startBuild("cart", "--from-file=target/cart.jar").logs("-f") 22 | } 23 | } 24 | } 25 | } 26 | } 27 | stage('Deploy') { 28 | steps { 29 | script { 30 | openshift.withCluster() { 31 | openshift.withProject() { 32 | dc = openshift.selector("dc", "cart") 33 | dc.rollout().latest() 34 | timeout(10) { 35 | dc.rollout().status() 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | stage('Component Test') { 43 | steps { 44 | script { 45 | sh "curl -s -X POST http://cart:8080/api/cart/dummy/666/1" 46 | sh "curl -s http://cart:8080/api/cart/dummy | grep 'Dummy Product'" 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /solutions/lab-5/cart-pipeline-scm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: BuildConfig 3 | metadata: 4 | annotations: 5 | pipeline.alpha.openshift.io/uses: '[{"name": "cart", "namespace": "", "kind": "DeploymentConfig"}]' 6 | name: cart-pipeline-git 7 | spec: 8 | source: 9 | git: 10 | uri: http://gogs.lab-infra.svc.cluster.local:3000/developer/pipelines.git 11 | type: Git 12 | strategy: 13 | type: JenkinsPipeline 14 | env: 15 | - name: NEXUS_URL 16 | value: "http://nexus.lab-infra.svc:8081" 17 | jenkinsPipelineStrategy: {} 18 | triggers: 19 | - generic: 20 | secret: FiArdDBH 21 | type: Generic 22 | -------------------------------------------------------------------------------- /solutions/lab-6/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | label 'maven' 4 | } 5 | stages { 6 | stage('Build App') { 7 | steps { 8 | sh "mvn clean package -s src/main/config/settings.xml" 9 | } 10 | } 11 | stage('Integration Test') { 12 | steps { 13 | sh "mvn verify -s src/main/config/settings.xml" 14 | } 15 | } 16 | stage('Build Image') { 17 | steps { 18 | script { 19 | openshift.withCluster() { 20 | openshift.withProject() { 21 | openshift.startBuild("cart", "--from-file=target/cart.jar", "--follow") 22 | } 23 | } 24 | } 25 | } 26 | } 27 | stage('Deploy') { 28 | steps { 29 | script { 30 | openshift.withCluster() { 31 | openshift.withProject() { 32 | dc = openshift.selector("dc", "cart") 33 | dc.rollout().latest() 34 | timeout(10) { 35 | dc.rollout().status("-w") 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | stage('Component Test') { 43 | steps { 44 | script { 45 | sh "curl -s -X POST http://cart:8080/api/cart/dummy/666/1" 46 | sh "curl -s http://cart:8080/api/cart/dummy | grep 'Dummy Product'" 47 | } 48 | } 49 | } 50 | stage('Promote to Prod') { 51 | steps { 52 | timeout(time:15, unit:'MINUTES') { 53 | input message: "Approve Promotion to Prod?", ok: "Promote" 54 | } 55 | script { 56 | openshift.withCluster() { 57 | openshift.tag("dev-XX/cart:latest", "prod-XX/cart:prod") 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /solutions/lab-7/cart-blue.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: Service 6 | metadata: 7 | labels: 8 | app: cart-blue 9 | application: coolstore 10 | component: cart-blue 11 | name: cart-blue 12 | spec: 13 | ports: 14 | - port: 8080 15 | protocol: TCP 16 | targetPort: 8080 17 | selector: 18 | deploymentconfig: cart-blue 19 | sessionAffinity: None 20 | type: ClusterIP 21 | - apiVersion: v1 22 | kind: DeploymentConfig 23 | metadata: 24 | labels: 25 | application: coolstore 26 | component: cart-blue 27 | name: cart-blue 28 | spec: 29 | replicas: 1 30 | selector: 31 | deploymentconfig: cart-blue 32 | strategy: 33 | type: Recreate 34 | template: 35 | metadata: 36 | labels: 37 | application: coolstore 38 | component: cart-blue 39 | deploymentconfig: cart-blue 40 | name: cart-blue 41 | spec: 42 | containers: 43 | - env: 44 | - name: CATALOG_ENDPOINT 45 | value: http://catalog:8080 46 | image: cart 47 | imagePullPolicy: Always 48 | livenessProbe: 49 | failureThreshold: 5 50 | httpGet: 51 | path: /health 52 | port: 8080 53 | scheme: HTTP 54 | initialDelaySeconds: 45 55 | periodSeconds: 5 56 | successThreshold: 1 57 | timeoutSeconds: 1 58 | name: cart-blue 59 | ports: 60 | - containerPort: 8778 61 | name: jolokia 62 | protocol: TCP 63 | - containerPort: 8080 64 | name: http 65 | protocol: TCP 66 | - containerPort: 8443 67 | name: https 68 | protocol: TCP 69 | readinessProbe: 70 | failureThreshold: 10 71 | httpGet: 72 | path: /health 73 | port: 8080 74 | scheme: HTTP 75 | initialDelaySeconds: 45 76 | periodSeconds: 5 77 | successThreshold: 1 78 | timeoutSeconds: 1 79 | resources: 80 | limits: 81 | memory: 1Gi 82 | requests: 83 | memory: 200Mi 84 | terminationMessagePath: /dev/termination-log 85 | dnsPolicy: ClusterFirst 86 | restartPolicy: Always 87 | securityContext: {} 88 | terminationGracePeriodSeconds: 75 89 | test: false 90 | triggers: 91 | - imageChangeParams: 92 | automatic: true 93 | containerNames: 94 | - cart-blue 95 | from: 96 | kind: ImageStreamTag 97 | name: cart:prod-blue 98 | namespace: prod 99 | type: ImageChange 100 | - type: ConfigChange -------------------------------------------------------------------------------- /solutions/lab-7/cart-green.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: Service 6 | metadata: 7 | labels: 8 | app: cart-green 9 | application: coolstore 10 | component: cart-green 11 | name: cart-green 12 | spec: 13 | ports: 14 | - port: 8080 15 | protocol: TCP 16 | targetPort: 8080 17 | selector: 18 | deploymentconfig: cart-green 19 | sessionAffinity: None 20 | type: ClusterIP 21 | - apiVersion: v1 22 | kind: DeploymentConfig 23 | metadata: 24 | labels: 25 | application: coolstore 26 | component: cart-green 27 | name: cart-green 28 | spec: 29 | replicas: 1 30 | selector: 31 | deploymentconfig: cart-green 32 | strategy: 33 | type: Recreate 34 | template: 35 | metadata: 36 | labels: 37 | application: coolstore 38 | component: cart-green 39 | deploymentconfig: cart-green 40 | name: cart-green 41 | spec: 42 | containers: 43 | - env: 44 | - name: CATALOG_ENDPOINT 45 | value: http://catalog:8080 46 | image: cart 47 | imagePullPolicy: Always 48 | livenessProbe: 49 | failureThreshold: 5 50 | httpGet: 51 | path: /health 52 | port: 8080 53 | scheme: HTTP 54 | initialDelaySeconds: 45 55 | periodSeconds: 5 56 | successThreshold: 1 57 | timeoutSeconds: 1 58 | name: cart-green 59 | ports: 60 | - containerPort: 8778 61 | name: jolokia 62 | protocol: TCP 63 | - containerPort: 8080 64 | name: http 65 | protocol: TCP 66 | - containerPort: 8443 67 | name: https 68 | protocol: TCP 69 | readinessProbe: 70 | failureThreshold: 10 71 | httpGet: 72 | path: /health 73 | port: 8080 74 | scheme: HTTP 75 | initialDelaySeconds: 45 76 | periodSeconds: 5 77 | successThreshold: 1 78 | timeoutSeconds: 1 79 | resources: 80 | limits: 81 | memory: 1Gi 82 | requests: 83 | memory: 200Mi 84 | terminationMessagePath: /dev/termination-log 85 | dnsPolicy: ClusterFirst 86 | restartPolicy: Always 87 | securityContext: {} 88 | terminationGracePeriodSeconds: 75 89 | test: false 90 | triggers: 91 | - imageChangeParams: 92 | automatic: true 93 | containerNames: 94 | - cart-green 95 | from: 96 | kind: ImageStreamTag 97 | name: cart:prod-green 98 | namespace: prod 99 | type: ImageChange 100 | - type: ConfigChange -------------------------------------------------------------------------------- /solutions/lab-7/coolstore-bluegreen-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | annotations: 5 | description: CoolStore Microservices Application Template 6 | iconClass: icon-java 7 | tags: microservice,jboss,spring 8 | name: coolstore 9 | objects: 10 | - apiVersion: v1 11 | groupNames: null 12 | kind: RoleBinding 13 | metadata: 14 | name: default_edit 15 | roleRef: 16 | name: view 17 | subjects: 18 | - kind: ServiceAccount 19 | name: default 20 | # UI 21 | - apiVersion: v1 22 | kind: DeploymentConfig 23 | metadata: 24 | name: web-ui 25 | labels: 26 | application: coolstore 27 | component: web-ui 28 | spec: 29 | replicas: 1 30 | selector: 31 | deploymentconfig: web-ui 32 | strategy: 33 | resources: {} 34 | type: Recreate 35 | template: 36 | metadata: 37 | labels: 38 | application: coolstore 39 | component: web-ui 40 | deploymentconfig: web-ui 41 | spec: 42 | containers: 43 | - env: 44 | - name: COOLSTORE_GW_ENDPOINT 45 | value: http://gw-${HOSTNAME_SUFFIX} 46 | - name: HOSTNAME_HTTP 47 | value: web-ui:8080 48 | image: web-ui 49 | imagePullPolicy: Always 50 | name: web-ui 51 | ports: 52 | - containerPort: 8080 53 | protocol: TCP 54 | livenessProbe: 55 | failureThreshold: 5 56 | httpGet: 57 | path: / 58 | port: 8080 59 | scheme: HTTP 60 | initialDelaySeconds: 120 61 | periodSeconds: 5 62 | successThreshold: 1 63 | timeoutSeconds: 5 64 | readinessProbe: 65 | failureThreshold: 5 66 | httpGet: 67 | path: / 68 | port: 8080 69 | scheme: HTTP 70 | initialDelaySeconds: 15 71 | periodSeconds: 5 72 | successThreshold: 1 73 | timeoutSeconds: 1 74 | resources: 75 | limits: 76 | cpu: 500m 77 | memory: 1Gi 78 | requests: 79 | cpu: 50m 80 | memory: 128Mi 81 | terminationMessagePath: /dev/termination-log 82 | dnsPolicy: ClusterFirst 83 | restartPolicy: Always 84 | securityContext: {} 85 | terminationGracePeriodSeconds: 30 86 | triggers: 87 | - imageChangeParams: 88 | automatic: true 89 | containerNames: 90 | - web-ui 91 | from: 92 | kind: ImageStreamTag 93 | namespace: ${IMAGESTREAM_NAMESPACE} 94 | name: coolstore-web-ui:${APP_VERSION} 95 | type: ImageChange 96 | - type: ConfigChange 97 | - apiVersion: v1 98 | kind: Service 99 | metadata: 100 | labels: 101 | app: web-ui 102 | application: coolstore 103 | component: web-ui 104 | name: web-ui 105 | spec: 106 | ports: 107 | - name: 8080-tcp 108 | port: 8080 109 | protocol: TCP 110 | targetPort: 8080 111 | selector: 112 | deploymentconfig: web-ui 113 | - apiVersion: v1 114 | kind: Route 115 | metadata: 116 | name: web-ui 117 | labels: 118 | application: coolstore 119 | component: web-ui 120 | spec: 121 | host: web-ui-${HOSTNAME_SUFFIX} 122 | to: 123 | kind: Service 124 | name: web-ui 125 | # Coolstore Gateway 126 | - apiVersion: v1 127 | kind: DeploymentConfig 128 | metadata: 129 | name: coolstore-gw 130 | labels: 131 | application: coolstore 132 | component: coolstore-gw 133 | spec: 134 | replicas: 1 135 | selector: 136 | deploymentconfig: coolstore-gw 137 | strategy: 138 | resources: {} 139 | type: Recreate 140 | template: 141 | metadata: 142 | labels: 143 | application: coolstore 144 | component: coolstore-gw 145 | deploymentconfig: coolstore-gw 146 | name: coolstore-gw 147 | spec: 148 | containers: 149 | - env: 150 | - name: KUBERNETES_NAMESPACE 151 | valueFrom: 152 | fieldRef: 153 | fieldPath: metadata.namespace 154 | - name: CART_ENDPOINT 155 | value: cart-${HOSTNAME_SUFFIX} 156 | - name: INVENTORY_ENDPOINT 157 | value: inventory-${HOSTNAME_SUFFIX} 158 | - name: CATALOG_ENDPOINT 159 | value: catalog-${HOSTNAME_SUFFIX} 160 | image: library/coolstore-gw:${APP_VERSION} 161 | livenessProbe: 162 | httpGet: 163 | path: /health 164 | port: 8081 165 | initialDelaySeconds: 180 166 | name: coolstore-gw 167 | ports: 168 | - containerPort: 8778 169 | name: jolokia 170 | readinessProbe: 171 | httpGet: 172 | path: /health 173 | port: 8081 174 | initialDelaySeconds: 10 175 | resources: 176 | limits: 177 | cpu: 1 178 | memory: 2Gi 179 | requests: 180 | cpu: 100m 181 | memory: 512Mi 182 | triggers: 183 | - imageChangeParams: 184 | automatic: true 185 | containerNames: 186 | - coolstore-gw 187 | from: 188 | kind: ImageStreamTag 189 | namespace: ${IMAGESTREAM_NAMESPACE} 190 | name: coolstore-gateway:${APP_VERSION} 191 | type: ImageChange 192 | - type: ConfigChange 193 | - apiVersion: v1 194 | kind: Service 195 | metadata: 196 | labels: 197 | app: coolstore-gw 198 | application: coolstore 199 | component: coolstore-gw 200 | hystrix.enabled: "true" 201 | name: coolstore-gw 202 | spec: 203 | ports: 204 | - port: 8080 205 | protocol: TCP 206 | targetPort: 8080 207 | selector: 208 | deploymentconfig: coolstore-gw 209 | - apiVersion: v1 210 | kind: Route 211 | metadata: 212 | name: coolstore-gw 213 | labels: 214 | application: coolstore 215 | component: coolstore-gw 216 | spec: 217 | host: gw-${HOSTNAME_SUFFIX} 218 | to: 219 | kind: Service 220 | name: coolstore-gw 221 | # Catalog Service 222 | - apiVersion: v1 223 | kind: ImageStream 224 | metadata: 225 | name: catalog 226 | labels: 227 | application: coolstore 228 | component: catalog 229 | spec: 230 | tags: 231 | - name: latest 232 | - apiVersion: v1 233 | kind: DeploymentConfig 234 | metadata: 235 | name: catalog 236 | labels: 237 | application: coolstore 238 | component: catalog 239 | spec: 240 | replicas: 1 241 | selector: 242 | deploymentconfig: catalog 243 | strategy: 244 | resources: {} 245 | type: Recreate 246 | template: 247 | metadata: 248 | labels: 249 | application: coolstore 250 | component: catalog 251 | deploymentconfig: catalog 252 | name: catalog 253 | spec: 254 | containers: 255 | - env: 256 | - name: JWS_ADMIN_USERNAME 257 | value: Skq3VtCd 258 | - name: JWS_ADMIN_PASSWORD 259 | value: oktt6yhw 260 | - name: DB_USERNAME 261 | value: ${CATALOG_DB_USERNAME} 262 | - name: DB_PASSWORD 263 | value: ${CATALOG_DB_PASSWORD} 264 | - name: DB_NAME 265 | value: catalogdb 266 | - name: DB_SERVER 267 | value: catalog-mongodb 268 | image: catalog 269 | imagePullPolicy: Always 270 | name: catalog 271 | ports: 272 | - containerPort: 8778 273 | name: jolokia 274 | protocol: TCP 275 | - containerPort: 8080 276 | name: http 277 | protocol: TCP 278 | readinessProbe: 279 | failureThreshold: 10 280 | exec: 281 | command: 282 | - /bin/bash 283 | - -c 284 | - curl -s -u Skq3VtCd:oktt6yhw 'http://localhost:8080/manager/jmxproxy/?get=Catalina%3Atype%3DServer&att=stateName' 285 | |grep -iq 'stateName *= *STARTED' 286 | initialDelaySeconds: 15 287 | periodSeconds: 5 288 | successThreshold: 1 289 | timeoutSeconds: 1 290 | resources: 291 | limits: 292 | cpu: 2 293 | memory: 2Gi 294 | requests: 295 | cpu: 100m 296 | memory: 256Mi 297 | terminationMessagePath: /dev/termination-log 298 | dnsPolicy: ClusterFirst 299 | restartPolicy: Always 300 | securityContext: {} 301 | terminationGracePeriodSeconds: 75 302 | triggers: 303 | - imageChangeParams: 304 | automatic: true 305 | containerNames: 306 | - catalog 307 | from: 308 | kind: ImageStreamTag 309 | name: coolstore-catalog:${APP_VERSION} 310 | namespace: ${IMAGESTREAM_NAMESPACE} 311 | type: ImageChange 312 | - type: ConfigChange 313 | - apiVersion: v1 314 | kind: Service 315 | metadata: 316 | annotations: 317 | service.alpha.openshift.io/dependencies: '[{"name":"catalog-mongodb","namespace":"","kind":"Service"}]' 318 | labels: 319 | app: catalog 320 | application: coolstore 321 | component: catalog 322 | name: catalog 323 | spec: 324 | ports: 325 | - port: 8080 326 | protocol: TCP 327 | targetPort: 8080 328 | selector: 329 | deploymentconfig: catalog 330 | - apiVersion: v1 331 | kind: Route 332 | metadata: 333 | labels: 334 | application: coolstore 335 | component: catalog 336 | name: catalog 337 | spec: 338 | host: catalog-${HOSTNAME_SUFFIX} 339 | to: 340 | kind: Service 341 | name: catalog 342 | weight: 100 343 | - apiVersion: v1 344 | kind: Service 345 | metadata: 346 | labels: 347 | app: catalog 348 | application: coolstore 349 | component: catalog 350 | name: catalog-mongodb 351 | spec: 352 | ports: 353 | - name: mongo 354 | port: 27017 355 | protocol: TCP 356 | targetPort: 27017 357 | selector: 358 | deploymentconfig: catalog-mongodb 359 | sessionAffinity: None 360 | type: ClusterIP 361 | - apiVersion: v1 362 | kind: DeploymentConfig 363 | metadata: 364 | labels: 365 | application: coolstore 366 | component: catalog 367 | name: catalog-mongodb 368 | spec: 369 | replicas: 1 370 | selector: 371 | deploymentconfig: catalog-mongodb 372 | strategy: 373 | recreateParams: 374 | timeoutSeconds: 600 375 | resources: {} 376 | type: Recreate 377 | template: 378 | metadata: 379 | labels: 380 | application: coolstore 381 | component: catalog 382 | deploymentconfig: catalog-mongodb 383 | spec: 384 | containers: 385 | - env: 386 | - name: KUBERNETES_NAMESPACE 387 | valueFrom: 388 | fieldRef: 389 | fieldPath: metadata.namespace 390 | - name: MONGODB_USER 391 | value: ${CATALOG_DB_USERNAME} 392 | - name: MONGODB_PASSWORD 393 | value: ${CATALOG_DB_PASSWORD} 394 | - name: MONGODB_DATABASE 395 | value: catalogdb 396 | - name: MONGODB_ADMIN_PASSWORD 397 | value: ${CATALOG_DB_PASSWORD} 398 | image: mongodb 399 | imagePullPolicy: IfNotPresent 400 | livenessProbe: 401 | failureThreshold: 3 402 | initialDelaySeconds: 30 403 | periodSeconds: 10 404 | successThreshold: 1 405 | tcpSocket: 406 | port: 27017 407 | timeoutSeconds: 1 408 | name: catalog-mongodb 409 | ports: 410 | - containerPort: 27017 411 | protocol: TCP 412 | readinessProbe: 413 | exec: 414 | command: 415 | - /bin/sh 416 | - -i 417 | - -c 418 | - mongo 127.0.0.1:27017/$MONGODB_DATABASE -u $MONGODB_USER -p $MONGODB_PASSWORD 419 | --eval="quit()" 420 | failureThreshold: 3 421 | initialDelaySeconds: 3 422 | periodSeconds: 10 423 | successThreshold: 1 424 | timeoutSeconds: 1 425 | resources: 426 | limits: 427 | cpu: 500m 428 | memory: 1Gi 429 | requests: 430 | cpu: 100m 431 | memory: 256Mi 432 | securityContext: 433 | capabilities: {} 434 | privileged: false 435 | terminationMessagePath: /dev/termination-log 436 | volumeMounts: 437 | - mountPath: /var/lib/mongodb/data 438 | name: mongodb-data 439 | dnsPolicy: ClusterFirst 440 | restartPolicy: Always 441 | securityContext: {} 442 | terminationGracePeriodSeconds: 30 443 | volumes: 444 | - name: mongodb-data 445 | persistentVolumeClaim: 446 | claimName: mongodb-data-pv 447 | test: false 448 | triggers: 449 | - imageChangeParams: 450 | automatic: true 451 | containerNames: 452 | - catalog-mongodb 453 | from: 454 | kind: ImageStreamTag 455 | name: mongodb:3.2 456 | namespace: openshift 457 | type: ImageChange 458 | - type: ConfigChange 459 | - apiVersion: v1 460 | kind: PersistentVolumeClaim 461 | metadata: 462 | labels: 463 | application: coolstore 464 | component: catalog 465 | name: mongodb-data-pv 466 | spec: 467 | accessModes: 468 | - ReadWriteOnce 469 | resources: 470 | requests: 471 | storage: 512Mi 472 | # Cart Service Blue 473 | - apiVersion: v1 474 | kind: Service 475 | metadata: 476 | labels: 477 | app: cart 478 | application: coolstore 479 | component: cart-blue 480 | name: cart-blue 481 | spec: 482 | ports: 483 | - port: 8080 484 | protocol: TCP 485 | targetPort: 8080 486 | selector: 487 | deploymentconfig: cart-blue 488 | sessionAffinity: None 489 | type: ClusterIP 490 | - apiVersion: v1 491 | kind: DeploymentConfig 492 | metadata: 493 | labels: 494 | application: coolstore 495 | component: cart-blue 496 | name: cart-blue 497 | spec: 498 | replicas: 1 499 | selector: 500 | deploymentconfig: cart-blue 501 | strategy: 502 | type: Recreate 503 | template: 504 | metadata: 505 | labels: 506 | application: coolstore 507 | component: cart-blue 508 | deploymentconfig: cart-blue 509 | name: cart-blue 510 | spec: 511 | containers: 512 | - env: 513 | - name: CATALOG_ENDPOINT 514 | value: http://catalog:8080 515 | - name: APPLICATION_MODE 516 | value: prod 517 | image: cart 518 | imagePullPolicy: Always 519 | livenessProbe: 520 | failureThreshold: 5 521 | httpGet: 522 | path: /health 523 | port: 8080 524 | scheme: HTTP 525 | initialDelaySeconds: 45 526 | periodSeconds: 5 527 | successThreshold: 1 528 | timeoutSeconds: 1 529 | name: cart-blue 530 | ports: 531 | - containerPort: 8778 532 | name: jolokia 533 | protocol: TCP 534 | - containerPort: 8080 535 | name: http 536 | protocol: TCP 537 | - containerPort: 8443 538 | name: https 539 | protocol: TCP 540 | readinessProbe: 541 | failureThreshold: 10 542 | httpGet: 543 | path: /health 544 | port: 8080 545 | scheme: HTTP 546 | initialDelaySeconds: 45 547 | periodSeconds: 5 548 | successThreshold: 1 549 | timeoutSeconds: 1 550 | resources: 551 | limits: 552 | memory: 1Gi 553 | requests: 554 | memory: 200Mi 555 | terminationMessagePath: /dev/termination-log 556 | dnsPolicy: ClusterFirst 557 | restartPolicy: Always 558 | securityContext: {} 559 | terminationGracePeriodSeconds: 75 560 | test: false 561 | triggers: 562 | - imageChangeParams: 563 | automatic: true 564 | containerNames: 565 | - cart-blue 566 | from: 567 | kind: ImageStreamTag 568 | name: cart:prod-blue 569 | namespace: prod 570 | type: ImageChange 571 | - type: ConfigChange 572 | # Cart Service Green 573 | - apiVersion: v1 574 | kind: Service 575 | metadata: 576 | labels: 577 | app: cart 578 | application: coolstore 579 | component: cart-green 580 | name: cart-green 581 | spec: 582 | ports: 583 | - port: 8080 584 | protocol: TCP 585 | targetPort: 8080 586 | selector: 587 | deploymentconfig: cart-green 588 | sessionAffinity: None 589 | type: ClusterIP 590 | - apiVersion: v1 591 | kind: DeploymentConfig 592 | metadata: 593 | labels: 594 | application: coolstore 595 | component: cart-green 596 | name: cart-green 597 | spec: 598 | replicas: 1 599 | selector: 600 | deploymentconfig: cart-green 601 | strategy: 602 | type: Recreate 603 | template: 604 | metadata: 605 | labels: 606 | application: coolstore 607 | component: cart-green 608 | deploymentconfig: cart-green 609 | name: cart-green 610 | spec: 611 | containers: 612 | - env: 613 | - name: CATALOG_ENDPOINT 614 | value: http://catalog:8080 615 | - name: APPLICATION_MODE 616 | value: prod 617 | image: cart 618 | imagePullPolicy: Always 619 | livenessProbe: 620 | failureThreshold: 5 621 | httpGet: 622 | path: /health 623 | port: 8080 624 | scheme: HTTP 625 | initialDelaySeconds: 45 626 | periodSeconds: 5 627 | successThreshold: 1 628 | timeoutSeconds: 1 629 | name: cart-green 630 | ports: 631 | - containerPort: 8778 632 | name: jolokia 633 | protocol: TCP 634 | - containerPort: 8080 635 | name: http 636 | protocol: TCP 637 | - containerPort: 8443 638 | name: https 639 | protocol: TCP 640 | readinessProbe: 641 | failureThreshold: 10 642 | httpGet: 643 | path: /health 644 | port: 8080 645 | scheme: HTTP 646 | initialDelaySeconds: 45 647 | periodSeconds: 5 648 | successThreshold: 1 649 | timeoutSeconds: 1 650 | resources: 651 | limits: 652 | memory: 1Gi 653 | requests: 654 | memory: 200Mi 655 | terminationMessagePath: /dev/termination-log 656 | dnsPolicy: ClusterFirst 657 | restartPolicy: Always 658 | securityContext: {} 659 | terminationGracePeriodSeconds: 75 660 | test: false 661 | triggers: 662 | - imageChangeParams: 663 | automatic: true 664 | containerNames: 665 | - cart-green 666 | from: 667 | kind: ImageStreamTag 668 | name: cart:prod-green 669 | namespace: prod 670 | type: ImageChange 671 | - type: ConfigChange 672 | # Cart Service 673 | - apiVersion: v1 674 | kind: Route 675 | metadata: 676 | labels: 677 | application: coolstore 678 | component: cart 679 | name: cart 680 | spec: 681 | alternateBackends: 682 | - kind: Service 683 | name: cart-blue 684 | weight: 100 685 | host: cart-${HOSTNAME_SUFFIX} 686 | to: 687 | kind: Service 688 | name: cart-green 689 | weight: 0 690 | # Inventory Service 691 | - apiVersion: v1 692 | kind: DeploymentConfig 693 | metadata: 694 | name: inventory 695 | labels: 696 | application: coolstore 697 | component: inventory 698 | spec: 699 | replicas: 1 700 | selector: 701 | deploymentconfig: inventory 702 | strategy: 703 | resources: {} 704 | type: Recreate 705 | template: 706 | metadata: 707 | labels: 708 | deploymentconfig: inventory 709 | application: coolstore 710 | component: inventory 711 | name: inventory 712 | spec: 713 | containers: 714 | - env: 715 | - name: OPENSHIFT_KUBE_PING_LABELS 716 | value: application=inventory 717 | - name: OPENSHIFT_KUBE_PING_NAMESPACE 718 | valueFrom: 719 | fieldRef: 720 | fieldPath: metadata.namespace 721 | - name: MQ_CLUSTER_PASSWORD 722 | value: 7mzX0pLV03 723 | - name: JGROUPS_CLUSTER_PASSWORD 724 | value: CqUo3fYDTv 725 | - name: AUTO_DEPLOY_EXPLODED 726 | value: "false" 727 | - name: DB_SERVICE_PREFIX_MAPPING 728 | value: inventory-postgresql=DB 729 | - name: DB_JNDI 730 | value: java:jboss/datasources/InventoryDS 731 | - name: DB_USERNAME 732 | value: ${INVENTORY_DB_USERNAME} 733 | - name: DB_PASSWORD 734 | value: ${INVENTORY_DB_PASSWORD} 735 | - name: DB_DATABASE 736 | value: inventorydb 737 | image: inventory 738 | imagePullPolicy: Always 739 | lifecycle: 740 | preStop: 741 | exec: 742 | command: 743 | - /opt/eap/bin/jboss-cli.sh 744 | - -c 745 | - :shutdown(timeout=60) 746 | livenessProbe: 747 | failureThreshold: 5 748 | httpGet: 749 | path: / 750 | port: 8080 751 | scheme: HTTP 752 | initialDelaySeconds: 120 753 | periodSeconds: 5 754 | successThreshold: 1 755 | timeoutSeconds: 5 756 | name: inventory 757 | ports: 758 | - containerPort: 8778 759 | name: jolokia 760 | protocol: TCP 761 | - containerPort: 8080 762 | name: http 763 | protocol: TCP 764 | - containerPort: 8888 765 | name: ping 766 | protocol: TCP 767 | readinessProbe: 768 | failureThreshold: 5 769 | httpGet: 770 | path: / 771 | port: 8080 772 | scheme: HTTP 773 | initialDelaySeconds: 15 774 | periodSeconds: 5 775 | successThreshold: 1 776 | timeoutSeconds: 1 777 | resources: 778 | limits: 779 | cpu: 500m 780 | memory: 1Gi 781 | requests: 782 | cpu: 100m 783 | memory: 512Mi 784 | terminationMessagePath: /dev/termination-log 785 | dnsPolicy: ClusterFirst 786 | restartPolicy: Always 787 | securityContext: {} 788 | terminationGracePeriodSeconds: 75 789 | triggers: 790 | - imageChangeParams: 791 | automatic: true 792 | containerNames: 793 | - inventory 794 | from: 795 | kind: ImageStreamTag 796 | name: coolstore-inventory:${APP_VERSION} 797 | namespace: ${IMAGESTREAM_NAMESPACE} 798 | type: ImageChange 799 | - type: ConfigChange 800 | - apiVersion: v1 801 | kind: Service 802 | metadata: 803 | annotations: 804 | service.alpha.openshift.io/dependencies: '[{"name":"inventory-postgresql","namespace":"","kind":"Service"}]' 805 | labels: 806 | app: inventory 807 | application: inventory 808 | name: inventory 809 | spec: 810 | ports: 811 | - port: 8080 812 | protocol: TCP 813 | targetPort: 8080 814 | selector: 815 | deploymentconfig: inventory 816 | - apiVersion: v1 817 | kind: Route 818 | metadata: 819 | labels: 820 | application: inventory 821 | name: inventory 822 | spec: 823 | host: inventory-${HOSTNAME_SUFFIX} 824 | to: 825 | kind: Service 826 | name: inventory 827 | weight: 100 828 | - apiVersion: v1 829 | kind: DeploymentConfig 830 | metadata: 831 | name: inventory-postgresql 832 | labels: 833 | component: inventory 834 | application: coolstore 835 | spec: 836 | replicas: 1 837 | selector: 838 | deploymentconfig: inventory-postgresql 839 | strategy: 840 | type: Recreate 841 | template: 842 | metadata: 843 | labels: 844 | application: coolstore 845 | component: inventory 846 | deploymentconfig: inventory-postgresql 847 | name: inventory-postgresql 848 | spec: 849 | containers: 850 | - env: 851 | - name: POSTGRESQL_USER 852 | value: ${INVENTORY_DB_USERNAME} 853 | - name: POSTGRESQL_PASSWORD 854 | value: ${INVENTORY_DB_PASSWORD} 855 | - name: POSTGRESQL_DATABASE 856 | value: inventorydb 857 | image: postgresql 858 | imagePullPolicy: Always 859 | name: inventory-postgresql 860 | ports: 861 | - containerPort: 5432 862 | protocol: TCP 863 | volumeMounts: 864 | - mountPath: /var/lib/pgsql/data 865 | name: inventory-postgresql-data 866 | livenessProbe: 867 | initialDelaySeconds: 30 868 | tcpSocket: 869 | port: 5432 870 | timeoutSeconds: 1 871 | readinessProbe: 872 | exec: 873 | command: 874 | - /bin/sh 875 | - -i 876 | - -c 877 | - psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1' 878 | initialDelaySeconds: 5 879 | timeoutSeconds: 1 880 | resources: 881 | limits: 882 | cpu: 500m 883 | memory: 512Mi 884 | requests: 885 | cpu: 50m 886 | memory: 128Mi 887 | terminationGracePeriodSeconds: 60 888 | volumes: 889 | - name: inventory-postgresql-data 890 | persistentVolumeClaim: 891 | claimName: inventory-postgresql-pv 892 | triggers: 893 | - imageChangeParams: 894 | automatic: true 895 | containerNames: 896 | - inventory-postgresql 897 | from: 898 | kind: ImageStreamTag 899 | name: postgresql:latest 900 | namespace: openshift 901 | type: ImageChange 902 | - type: ConfigChange 903 | - apiVersion: v1 904 | kind: Service 905 | metadata: 906 | labels: 907 | application: inventory 908 | name: inventory-postgresql 909 | spec: 910 | ports: 911 | - port: 5432 912 | targetPort: 5432 913 | selector: 914 | deploymentconfig: inventory-postgresql 915 | - apiVersion: v1 916 | kind: PersistentVolumeClaim 917 | metadata: 918 | labels: 919 | application: inventory 920 | name: inventory-postgresql-pv 921 | spec: 922 | accessModes: 923 | - ReadWriteOnce 924 | resources: 925 | requests: 926 | storage: 1Gi 927 | parameters: 928 | - description: Namespace in which the ImageStreams for CoolStore services are installed 929 | displayName: ImageStream Namespace 930 | name: IMAGESTREAM_NAMESPACE 931 | required: true 932 | value: openshift 933 | - description: CoolStore application image version to be deployed (imagestreams should exist) 934 | displayName: CoolStore Version 935 | name: APP_VERSION 936 | required: true 937 | value: prod 938 | - description: Hostname suffix used for routes e.g. cart- inventory- 939 | displayName: Hostname Suffix 940 | name: HOSTNAME_SUFFIX 941 | required: true 942 | - description: Catalog Service database user name 943 | displayName: Catalog Database Username 944 | from: user[a-zA-Z0-9]{3} 945 | generate: expression 946 | name: CATALOG_DB_USERNAME 947 | required: true 948 | - description: Catalog Service database user password 949 | displayName: Catalog Database Password 950 | from: '[a-zA-Z0-9]{8}' 951 | generate: expression 952 | name: CATALOG_DB_PASSWORD 953 | required: true 954 | - description: Inventory Service database user name 955 | displayName: Inventory Database Username 956 | from: user[a-zA-Z0-9]{3} 957 | generate: expression 958 | name: INVENTORY_DB_USERNAME 959 | required: true 960 | - description: Inventory Service database user password 961 | displayName: Inventory Database Password 962 | from: '[a-zA-Z0-9]{8}' 963 | generate: expression 964 | name: INVENTORY_DB_PASSWORD 965 | required: true 966 | -------------------------------------------------------------------------------- /solutions/lab-8/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def tag,altTag 2 | 3 | pipeline { 4 | agent { 5 | label 'maven' 6 | } 7 | stages { 8 | stage('Build App') { 9 | steps { 10 | sh "mvn clean package -s src/main/config/settings.xml" 11 | } 12 | } 13 | stage('Integration Test') { 14 | steps { 15 | sh "mvn verify -s src/main/config/settings.xml" 16 | } 17 | } 18 | stage('Build Image') { 19 | steps { 20 | script { 21 | openshift.withCluster() { 22 | openshift.withProject() { 23 | openshift.startBuild("cart", "--from-file=target/cart.jar").logs("-f") 24 | } 25 | } 26 | } 27 | } 28 | } 29 | stage('Deploy') { 30 | steps { 31 | script { 32 | openshift.withCluster() { 33 | openshift.withProject() { 34 | dc = openshift.selector("dc", "cart") 35 | dc.rollout().latest() 36 | timeout(10) { 37 | dc.rollout().status() 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | stage('Component Test') { 45 | steps { 46 | script { 47 | sh "curl -s -X POST http://cart:8080/api/cart/dummy/666/1" 48 | sh "curl -s http://cart:8080/api/cart/dummy | grep 'Dummy Product'" 49 | } 50 | } 51 | } 52 | stage('Promote') { 53 | steps { 54 | script { 55 | openshift.withCluster() { 56 | openshift.withProject("prod-XX") { 57 | def route = openshift.selector("route", "cart").object() 58 | def backends = [] 59 | backends.add(route.spec.to) 60 | backends.addAll(route.spec.alternateBackends) 61 | def svc = backends.find {it.weight == 100} 62 | tag = svc.name == "cart-green" ? "blue" : "green" 63 | altTag = svc.name == "cart-green" ? "green" : "blue" 64 | 65 | openshift.tag("dev-XX/cart:latest", "prod-XX/cart:prod-${tag}") 66 | openshift.selector("dc", "cart-${tag}").rollout().status() 67 | } 68 | } 69 | } 70 | } 71 | } 72 | 73 | stage('End-To-End Test') { 74 | steps { 75 | script { 76 | sh "curl -s -X POST http://cart-${tag}.prod-XX.svc:8080/api/cart/dummy/444434/1" 77 | sh "curl -s http://cart-${tag}.prod-XX.svc:8080/api/cart/dummy | grep 'Pebble Smart Watch'" 78 | } 79 | } 80 | } 81 | 82 | stage('Approve Go Live') { 83 | steps { 84 | timeout(time:15, unit:'MINUTES') { 85 | input message: "Approve Promotion to Prod?", ok: "Promote" 86 | } 87 | script { 88 | openshift.withCluster() { 89 | openshift.withProject("prod-XX") { 90 | openshift.set("route-backends", "cart" , "cart-${tag}=100", "cart-${altTag}=0") 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /solutions/support/lab-clean.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Delete Lab Resources 4 | oc delete project dev prod lab-infra 2>/dev/null 5 | oc delete bc --all -n openshift --as=system:admin 6 | oc delete is -l demo=coolstore-microservice -n openshift --as=system:admin 7 | oc delete template coolstore -n openshift --as=system:admin 8 | echo "Done!" -------------------------------------------------------------------------------- /solutions/support/lab-solve.sh: -------------------------------------------------------------------------------- 1 | 2 | # Set Vars 3 | GOGS_ROUTE=$(oc get route gogs -o template --template='{{.spec.host}}' -n lab-infra) 4 | CART_REPO=http://$GOGS_ROUTE/developer/cart-service.git 5 | PIPELINES_REPO=http://$GOGS_ROUTE/developer/pipelines.git 6 | 7 | # Create Projects 8 | oc new-project dev --display-name="Cart Dev" 9 | oc new-project prod --display-name="Coolstore Prod" 10 | 11 | # Deploy Dev 12 | oc process -f https://raw.githubusercontent.com/openshift-labs/devops-labs/ocp-3.5/lab-3/cart-template.yaml \ 13 | --param=GIT_URI=$CART_REPO \ 14 | --param=MAVEN_MIRROR_URL=http://nexus.lab-infra.svc.cluster.local:8081/repository/maven-all-public \ 15 | | oc create -f - -n dev 16 | 17 | # Deploy Prod 18 | HOSTNAME=$(echo "$GOGS_ROUTE" | sed "s/gogs-lab-infra.//g") 19 | oc process -f https://raw.githubusercontent.com/openshift-labs/devops-labs/ocp-3.5/lab-7/coolstore-bluegreen-template.yaml \ 20 | --param=HOSTNAME_SUFFIX=prod.$HOSTNAME \ 21 | | oc create -f - -n prod 22 | sleep 5 23 | 24 | # Save Resources 25 | oc scale dc/inventory --replicas=0 -n prod 26 | oc scale dc/inventory-postgresql --replicas=0 -n prod 27 | 28 | # Deploy Pipeline 29 | rm -rf /tmp/pipelines && \ 30 | git clone http://$GOGS_ROUTE/developer/pipelines.git /tmp/pipelines && \ 31 | pushd /tmp/pipelines && \ 32 | curl -sL https://raw.githubusercontent.com/openshift-labs/devops-labs/ocp-3.5/lab-8/Jenkinsfile | sed "s|git url: .*|git url: '$CART_REPO'|g" > Jenkinsfile && \ 33 | git add Jenkinsfile && \ 34 | git commit -m "pipeline added" && \ 35 | git push -f http://developer:developer@$GOGS_ROUTE/developer/pipelines.git master && \ 36 | popd && \ 37 | rm -rf /tmp/pipelines 38 | 39 | curl -sL https://raw.githubusercontent.com/openshift-labs/devops-labs/ocp-3.5/lab-4/cart-pipeline-scm.yaml | sed "s|uri: .*|uri: $PIPELINES_REPO|g" | oc create -f - -n dev 40 | 41 | oc policy add-role-to-user edit system:serviceaccount:dev:jenkins -n prod 42 | 43 | echo "Done!" -------------------------------------------------------------------------------- /web-nodejs/app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var module = angular.module('app', ['ngRoute', 'patternfly']), auth = { 4 | loggedIn: false, 5 | ssoEnabled: false, 6 | logout: function () { 7 | } 8 | }; 9 | 10 | module.factory('Auth', function () { 11 | return auth; 12 | }); 13 | 14 | angular.element(document).ready(function () { 15 | 16 | // get config 17 | var initInjector = angular.injector(["ng"]); 18 | var $http = initInjector.get("$http"); 19 | 20 | $http.get("coolstore.json").then(function (response) { 21 | module.constant("COOLSTORE_CONFIG", response.data); 22 | 23 | if (!response.data.SSO_ENABLED) { 24 | angular.bootstrap(document, ["app"], { 25 | strictDi: true 26 | }); 27 | } else { 28 | auth.ssoEnabled = true; 29 | var keycloakAuth = new Keycloak('keycloak.json'); 30 | auth.loggedIn = false; 31 | 32 | auth.login = function () { 33 | keycloakAuth.login({ 34 | loginHint: 'appuser' 35 | }); 36 | }; 37 | 38 | keycloakAuth.init({ 39 | onLoad: 'check-sso' 40 | }).success(function () { 41 | if (keycloakAuth.authenticated) { 42 | keycloakAuth.loadUserInfo().success(function (userInfo) { 43 | auth.userInfo = userInfo; 44 | angular.bootstrap(document, ["app"], { 45 | strictDi: true 46 | }); 47 | auth.loggedIn = true; 48 | auth.authz = keycloakAuth; 49 | auth.logout = function () { 50 | auth.loggedIn = false; 51 | auth.authz = null; 52 | auth.userInfo = {}; 53 | keycloakAuth.logout(); 54 | }; 55 | }).error(function () { 56 | angular.bootstrap(document, ["app"], { 57 | strictDi: true 58 | }); 59 | 60 | }); 61 | } else { 62 | angular.bootstrap(document, ["app"], { 63 | strictDi: true 64 | }); 65 | } 66 | }).error(function (msg) { 67 | angular.bootstrap(document, ["app"], { 68 | strictDi: true 69 | }); 70 | }); 71 | } 72 | }); 73 | }); 74 | 75 | 76 | // setup interceptors 77 | module.config(['$httpProvider', function ($httpProvider) { 78 | 79 | $httpProvider.interceptors.push(['$q', 'Auth', function ($q, Auth) { 80 | return { 81 | 'request': function (config) { 82 | var deferred = $q.defer(); 83 | if (Auth.authz && Auth.authz.token) { 84 | Auth.authz.updateToken(5).success(function () { 85 | config.headers = config.headers || {}; 86 | config.headers.Authorization = 'Bearer ' + Auth.authz.token; 87 | config.withCredentials = true; 88 | deferred.resolve(config); 89 | }).error(function () { 90 | deferred.reject('Failed to refresh token'); 91 | }); 92 | } else { 93 | config.withCredentials = false; 94 | deferred.resolve(config); 95 | } 96 | return deferred.promise; 97 | 98 | }, 99 | 'responseError': function (response) { 100 | if (response.status == 401) { 101 | auth.logout(); 102 | } 103 | return $q.reject(response); 104 | 105 | } 106 | } 107 | }]); 108 | }]); 109 | 110 | -------------------------------------------------------------------------------- /web-nodejs/app/controllers/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | 5 | .controller("HomeController", 6 | ['$scope', '$http', '$filter', 'Notifications', 'cart', 'catalog', 'Auth', 7 | function ($scope, $http, $filter, Notifications, cart, catalog, $auth) { 8 | 9 | $scope.products = []; 10 | $scope.addToCart = function (item) { 11 | cart.addToCart(item.product, parseInt(item.quantity)).then(function (data) { 12 | Notifications.success("Added! Your total is " + $filter('currency')(data.cartTotal)); 13 | }, function (err) { 14 | Notifications.error("Error adding to cart: " + err.statusText); 15 | }); 16 | }; 17 | 18 | $scope.isLoggedIn = function () { 19 | return $auth.loggedIn; 20 | }; 21 | $scope.ssoEnabled = function () { 22 | return $auth.ssoEnabled; 23 | }; 24 | 25 | $scope.login = function () { 26 | $auth.login(); 27 | }; 28 | 29 | 30 | // initialize products 31 | catalog.getProducts().then(function (data) { 32 | if (data.error != undefined && data.error != "") { 33 | Notifications.error("Error retrieving products: " + data.error); 34 | return; 35 | } 36 | $scope.products = data.map(function (el) { 37 | return { 38 | quantity: "1", 39 | product: el 40 | } 41 | }) 42 | }, function (err) { 43 | Notifications.error("Error retrieving products: " + err.statusText); 44 | }); 45 | 46 | 47 | }]) 48 | 49 | .controller("CartController", 50 | ['$scope', '$http', 'Notifications', 'cart', 'Auth', 51 | function ($scope, $http, Notifications, cart, $auth) { 52 | 53 | function reset() { 54 | $scope.cart = cart.getCart(); 55 | $scope.items = $scope.cart.shoppingCartItemList; 56 | 57 | $scope.subtotal = 0; 58 | $scope.cart.shoppingCartItemList.forEach(function (item) { 59 | $scope.subtotal += (item.quantity * item.product.price); 60 | }); 61 | } 62 | 63 | $scope.config = { 64 | selectItems: false, 65 | multiSelect: false, 66 | dblClick: false, 67 | showSelectBox: false 68 | }; 69 | 70 | function performAction(action, item) { 71 | cart.removeFromCart(item.product, item.quantity).then(function (newCart) { 72 | reset(); 73 | }, function (err) { 74 | Notifications.error("Error removing from cart: " + err.statusText); 75 | }); 76 | }; 77 | 78 | $scope.actionButtons = [ 79 | { 80 | name: 'Remove', 81 | title: 'Remove', 82 | actionFn: performAction 83 | } 84 | ]; 85 | 86 | 87 | $scope.$watch(function () { 88 | return cart.getCart(); 89 | }, function (newValue) { 90 | reset(); 91 | }); 92 | 93 | $scope.$watch(function () { 94 | return $auth.userInfo; 95 | }, function (newValue) { 96 | cart.reset(); 97 | }); 98 | 99 | $scope.checkout = function () { 100 | cart.checkout().then(function (cartData) { 101 | }, function (err) { 102 | Notifications.error("Error checking out: " + err.statusText); 103 | }); 104 | }; 105 | 106 | $scope.isLoggedIn = function () { 107 | return $auth.loggedIn; 108 | }; 109 | $scope.ssoEnabled = function () { 110 | return $auth.ssoEnabled; 111 | }; 112 | 113 | reset(); 114 | }]) 115 | 116 | .controller("HeaderController", 117 | ['$scope', '$location', '$http', 'Notifications', 'cart', 'Auth', 118 | function ($scope, $location, $http, Notifications, cart, $auth) { 119 | $scope.userInfo = $auth.userInfo; 120 | 121 | $scope.cartTotal = 0.0; 122 | $scope.itemCount = 0; 123 | 124 | $scope.isLoggedIn = function () { 125 | return $auth.loggedIn; 126 | }; 127 | 128 | $scope.login = function () { 129 | $auth.login(); 130 | }; 131 | $scope.logout = function () { 132 | $auth.logout(); 133 | }; 134 | $scope.isLoggedIn = function () { 135 | return $auth.loggedIn; 136 | }; 137 | $scope.ssoEnabled = function () { 138 | return $auth.ssoEnabled; 139 | }; 140 | $scope.profile = function () { 141 | $auth.authz.accountManagement(); 142 | }; 143 | $scope.$watch(function () { 144 | return cart.getCart().cartTotal || 0.0; 145 | }, function (newValue) { 146 | $scope.cartTotal = newValue; 147 | }); 148 | 149 | $scope.$watch(function () { 150 | var totalItems = 0; 151 | cart.getCart().shoppingCartItemList.forEach(function (el) { 152 | totalItems += el.quantity; 153 | }); 154 | return totalItems; 155 | }, function (newValue) { 156 | $scope.itemCount = newValue; 157 | }); 158 | 159 | $scope.$watch(function () { 160 | return $auth.userInfo; 161 | }, function (newValue) { 162 | $scope.userInfo = newValue; 163 | }); 164 | 165 | $scope.isActive = function (loc) { 166 | return loc === $location.path(); 167 | } 168 | }]); 169 | -------------------------------------------------------------------------------- /web-nodejs/app/coolstore.config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | API_ENDPOINT: 'gateway-' + process.env.KUBERNETES_NAMESPACE, 3 | SECURE_API_ENDPOINT: 'secure-gateway-' + process.env.SECURE_COOLSTORE_GW_SERVICE, 4 | SSO_ENABLED: process.env.SSO_URL ? true : false 5 | }; 6 | 7 | if (process.env.COOLSTORE_GW_ENDPOINT != null) { 8 | config.API_ENDPOINT = process.env.COOLSTORE_GW_ENDPOINT; 9 | } else if (process.env.COOLSTORE_GW_SERVICE != null) { 10 | config.API_ENDPOINT = process.env.COOLSTORE_GW_SERVICE + '-' + process.env.KUBERNETES_NAMESPACE; 11 | } 12 | 13 | if (process.env.SECURE_COOLSTORE_GW_ENDPOINT != null) { 14 | config.SECURE_API_ENDPOINT = process.env.SECURE_COOLSTORE_GW_ENDPOINT; 15 | } else if (process.env.SECURE_COOLSTORE_GW_SERVICE != null) { 16 | config.SECURE_API_ENDPOINT = process.env.SECURE_COOLSTORE_GW_SERVICE + '-' + process.env.KUBERNETES_NAMESPACE; 17 | } 18 | 19 | module.exports = config; 20 | -------------------------------------------------------------------------------- /web-nodejs/app/css/coolstore.css: -------------------------------------------------------------------------------- 1 | .media img { 2 | height: 100px; 3 | } 4 | 5 | .p-t-8 { 6 | padding-top: 8px !important; 7 | } 8 | 9 | [ng-click], 10 | [data-ng-click], 11 | [x-ng-click] { 12 | cursor: pointer; 13 | } -------------------------------------------------------------------------------- /web-nodejs/app/directives/header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | angular.module('app').directive('header', function() { 5 | return { 6 | restrict: 'A', 7 | replace: true, 8 | templateUrl: 'partials/header.html', 9 | controller: 'HeaderController' 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /web-nodejs/app/imgs/16 oz. Vortex Tumbler.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/16 oz. Vortex Tumbler.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Forge Laptop Sticker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Forge Laptop Sticker.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Lytro Camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Lytro Camera.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Oculus Rift.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Oculus Rift.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Ogio Caliber Polo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Ogio Caliber Polo.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Pebble Smart Watch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Pebble Smart Watch.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Red Fedora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Red Fedora.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Solid Performance Polo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/Solid Performance Polo.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/devops-labs/d1e253fb9b1a290eca9e56b354848e8f9e219037/web-nodejs/app/imgs/logo.png -------------------------------------------------------------------------------- /web-nodejs/app/keycloak.config.js: -------------------------------------------------------------------------------- 1 | var config = 2 | { 3 | "auth-server-url" : process.env.SSO_URL, 4 | "realm": process.env.SSO_REALM, 5 | "realm-public-key": process.env.SSO_PUBLIC_KEY , 6 | "resource": process.env.SSO_CLIENT_ID, 7 | "ssl-required": 'external', 8 | "public-client": true, 9 | "enable-cors": true 10 | }; 11 | 12 | module.exports = config; -------------------------------------------------------------------------------- /web-nodejs/app/routes/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app').config([ '$routeProvider', function($routeProvider) { 4 | $routeProvider.when('/', { 5 | templateUrl : 'partials/home.html', 6 | controller : 'HomeController' 7 | }).when('/cart', { 8 | templateUrl : 'partials/cart.html', 9 | controller : 'CartController' 10 | }).otherwise({ 11 | redirectTo : '/' 12 | }); 13 | } ]); 14 | -------------------------------------------------------------------------------- /web-nodejs/app/services/cart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module("app") 4 | 5 | .factory('cart', ['$http', '$q', 'COOLSTORE_CONFIG', 'Auth', '$location', function($http, $q, COOLSTORE_CONFIG, $auth, $location) { 6 | var factory = {}, cart, products, cartId, baseUrl; 7 | if ($location.protocol() === 'https') { 8 | baseUrl = (COOLSTORE_CONFIG.SECURE_API_ENDPOINT.startsWith("https://") ? COOLSTORE_CONFIG.SECURE_API_ENDPOINT : "https://" + COOLSTORE_CONFIG.SECURE_API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/cart'; 9 | } else { 10 | baseUrl = (COOLSTORE_CONFIG.API_ENDPOINT.startsWith("http://") ? COOLSTORE_CONFIG.API_ENDPOINT : "http://" + COOLSTORE_CONFIG.API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/cart'; 11 | } 12 | 13 | factory.checkout = function() { 14 | var deferred = $q.defer(); 15 | $http({ 16 | method: 'POST', 17 | url: baseUrl + '/checkout/' + cartId 18 | }).then(function(resp) { 19 | cart = resp.data; 20 | deferred.resolve(resp.data); 21 | }, function(err) { 22 | deferred.reject(err); 23 | }); 24 | return deferred.promise; 25 | }; 26 | 27 | factory.reset = function() { 28 | cart = { 29 | shoppingCartItemList: [] 30 | }; 31 | var tmpId = localStorage.getItem('cartId'); 32 | var authId = $auth.userInfo ? $auth.userInfo.sub : null; 33 | 34 | if (tmpId && authId) { 35 | // transfer cart 36 | cartId = authId; 37 | this.setCart(tmpId).then(function(result) { 38 | localStorage.removeItem('cartId'); 39 | }, function(err) { 40 | console.log("could not transfer cart " + tmpId + " to cart " + authId + ": " + err); 41 | }); 42 | return; 43 | } 44 | 45 | if (tmpId && !authId) { 46 | cartId = tmpId; 47 | } 48 | 49 | if (!tmpId && authId) { 50 | cartId = authId; 51 | } 52 | 53 | if (!tmpId && !authId) { 54 | tmpId = 'id-' + Math.random(); 55 | localStorage.setItem('cartId', tmpId); 56 | cartId = tmpId; 57 | } 58 | 59 | cart.shoppingCartItemList = []; 60 | $http({ 61 | method: 'GET', 62 | url: baseUrl + '/' + cartId 63 | }).then(function(resp) { 64 | cart = resp.data; 65 | }, function(err) { 66 | }); 67 | 68 | }; 69 | 70 | factory.getCart = function() { 71 | return cart; 72 | }; 73 | 74 | factory.removeFromCart = function(product, quantity) { 75 | var deferred = $q.defer(); 76 | $http({ 77 | method: 'DELETE', 78 | url: baseUrl + '/' + cartId + '/' + product.itemId + '/' + quantity 79 | }).then(function(resp) { 80 | cart = resp.data; 81 | deferred.resolve(resp.data); 82 | }, function(err) { 83 | deferred.reject(err); 84 | }); 85 | return deferred.promise; 86 | 87 | }; 88 | 89 | factory.setCart = function(id) { 90 | var deferred = $q.defer(); 91 | $http({ 92 | method: 'POST', 93 | url: baseUrl + '/' + cartId + '/' + id 94 | }).then(function(resp) { 95 | cart = resp.data; 96 | deferred.resolve(resp.data); 97 | }, function(err) { 98 | deferred.reject(err); 99 | }); 100 | return deferred.promise; 101 | 102 | }; 103 | 104 | factory.addToCart = function(product, quantity) { 105 | var deferred = $q.defer(); 106 | $http({ 107 | method: 'POST', 108 | url: baseUrl + '/' + cartId + '/' + product.itemId + '/' + quantity 109 | }).then(function(resp) { 110 | cart = resp.data; 111 | deferred.resolve(resp.data); 112 | }, function(err) { 113 | deferred.reject(err); 114 | }); 115 | return deferred.promise; 116 | 117 | }; 118 | 119 | factory.reset(); 120 | return factory; 121 | }]); 122 | -------------------------------------------------------------------------------- /web-nodejs/app/services/catalog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module("app") 4 | 5 | .factory('catalog', ['$http', '$q', 'COOLSTORE_CONFIG', 'Auth', '$location', function($http, $q, COOLSTORE_CONFIG, $auth, $location) { 6 | var factory = {}, products, baseUrl; 7 | 8 | if ($location.protocol() === 'https') { 9 | baseUrl = (COOLSTORE_CONFIG.SECURE_API_ENDPOINT.startsWith("https://") ? COOLSTORE_CONFIG.SECURE_API_ENDPOINT : "https://" + COOLSTORE_CONFIG.SECURE_API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/products'; 10 | } else { 11 | baseUrl = (COOLSTORE_CONFIG.API_ENDPOINT.startsWith("http://") ? COOLSTORE_CONFIG.API_ENDPOINT : "http://" + COOLSTORE_CONFIG.API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/products'; 12 | } 13 | 14 | factory.getProducts = function() { 15 | var deferred = $q.defer(); 16 | if (products) { 17 | deferred.resolve(products); 18 | } else { 19 | $http({ 20 | method: 'GET', 21 | url: baseUrl 22 | }).then(function(resp) { 23 | products = resp.data; 24 | deferred.resolve(resp.data); 25 | }, function(err) { 26 | deferred.reject(err); 27 | }); 28 | } 29 | return deferred.promise; 30 | }; 31 | 32 | return factory; 33 | }]); 34 | -------------------------------------------------------------------------------- /web-nodejs/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coolstore-microservice", 3 | "description": "Web UI for the CoolStore Microservices App", 4 | "authors": [ 5 | "Red Hat" 6 | ], 7 | "license": "Apache-2.0", 8 | "private": true, 9 | "devDependencies": { 10 | "patternfly": "3.17.0", 11 | "angular-patternfly": "3.17.0", 12 | "bootstrap-switch": "3.3.2", 13 | "isotope": "2.2.2", 14 | "imagesloaded": "4.1.1", 15 | "angular-route": "1.5.10", 16 | "keycloak": "1.9.8" 17 | }, 18 | "resolutions": { 19 | "angular": "1.5.10", 20 | "bootstrap": "3.3.7", 21 | "jquery": "3.2.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Red Hat", 3 | "contributors": [ 4 | "James Falkner (https://developers.redhat.com)" 5 | ], 6 | "name": "web", 7 | "repository": "jbossdemocentral/coolstore-microservice", 8 | "version": "1.0.0", 9 | "license": "Apache-2.0", 10 | "private": true, 11 | "description": "Web UI for the CoolStore Microservices App", 12 | "homepage": "https://developers.redhat.com", 13 | "dependencies": { 14 | "cors": "^2.8.3", 15 | "express": "^4.13.4", 16 | "keycloak-connect": "^3.1.0", 17 | "request": "^2.74.0" 18 | }, 19 | "devDependencies": { 20 | "bower": ">=1.7.9", 21 | "bower-nexus3-resolver": "*" 22 | }, 23 | "engines": { 24 | "node": ">=0.10.10" 25 | }, 26 | "scripts": { 27 | "postinstall": "node_modules/.bin/bower --config.registry.search=${BOWER_MIRROR} install" 28 | } 29 | } -------------------------------------------------------------------------------- /web-nodejs/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | http = require('http'), 3 | request = require('request'), 4 | fs = require('fs'), 5 | app = express(), 6 | path = require("path"), 7 | keycloakConfig = require('./app/keycloak.config.js'), 8 | coolstoreConfig = require('./app/coolstore.config.js'), 9 | Keycloak = require('keycloak-connect'), 10 | cors = require('cors'); 11 | 12 | 13 | var port = process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8080, 14 | ip = process.env.IP || process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0', 15 | secport = process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8443; 16 | 17 | // Enable CORS support 18 | app.use(cors()); 19 | 20 | // error handling 21 | app.use(function(err, req, res, next) { 22 | console.error(err.stack); 23 | res.status(500).send('Something bad happened!'); 24 | }); 25 | 26 | // keycloak config server 27 | app.get('/keycloak.json', function(req, res, next) { 28 | res.json(keycloakConfig); 29 | }); 30 | // coolstore config server 31 | app.get('/coolstore.json', function(req, res, next) { 32 | res.json(coolstoreConfig); 33 | }); 34 | 35 | app.use(express.static(path.join(__dirname, '/views'))); 36 | app.use('/app', express.static(path.join(__dirname, '/app'))); 37 | app.use('/bower_components', express.static(path.join(__dirname, '/bower_components'))); 38 | 39 | console.log("coolstore config: " + JSON.stringify(coolstoreConfig)); 40 | console.log("keycloak config: " + JSON.stringify(keycloakConfig)); 41 | 42 | 43 | http.createServer(app).listen(port); 44 | 45 | console.log('HTTP Server running on http://%s:%s', ip, port); 46 | 47 | module.exports = app; -------------------------------------------------------------------------------- /web-nodejs/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Red Hat CoolStore Microservices App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web-nodejs/views/partials/cart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{item.product.name}} 18 | 19 | Quantity: {{item.quantity}} 20 | 21 | 22 | {{item.product.description}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | You have not added any items to your Shopping Cart! 31 | Return to Store 32 | 33 | 34 | 35 | 36 | 37 | 38 | Shopping Summary 39 | 40 | 41 | 42 | Cart Total: {{subtotal | currency}} 43 | Promotional Item Savings: {{cart.cartItemPromoSavings | currency}} 44 | 45 | Subtotal: {{cart.cartItemTotal | currency}} 46 | Shipping: {{cart.shippingTotal | currency}} 47 | Promotional Shipping Savings: {{cart.shippingPromoSavings | currency}} 48 | 49 | 50 | Total Order Amount: {{cart.cartTotal | currency}} 51 | 52 | 53 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Final Order Summary 74 | 75 | 76 | Thank you for your order! 77 | 78 | Your order total of {{cart.cartTotal | currency}} will be processed when you click Checkout. 79 | 80 | 81 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /web-nodejs/views/partials/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Toggle navigation 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Shopping Cart {{cartTotal | currency}} ({{itemCount}} item(s)) 17 | 18 | 19 | 20 | 21 | 22 | {{userInfo.name}} 23 | 24 | 25 | 26 | Profile 27 | 28 | 29 | Sign Out 30 | 31 | 32 | 33 | 34 | 35 | Sign In 36 | 37 | 38 | Sign In Unavailable 39 | 40 | 41 | 42 | 43 | Red Hat Cool Store 44 | 45 | 46 | Your Shopping Cart 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /web-nodejs/views/partials/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{item.product.price | currency}} 15 | {{item.product.availability.quantity}} left! 16 | 17 | 18 | 19 | Not in Stock 20 | 21 | 22 | 23 | 24 | 25 | 1 26 | 2 27 | 3 28 | 4 29 | 5 30 | 31 | 32 | 33 | Add To Cart 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------
78 | Your order total of {{cart.cartTotal | currency}} will be processed when you click Checkout. 79 |