From ae82b76d5bc6850a07256810759bad59bae8784c Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Tue, 2 Jan 2024 16:53:22 -0700 Subject: [PATCH 1/8] Migrated orders component away from JPA to Spring Data JDBC --- src/orders/pom.xml | 334 +++++++++--------- .../sample/orders/OrdersApplication.java | 1 - .../orders/config/PersistenceConfig.java | 22 ++ .../config/persistence/ReaderConfig.java | 74 ---- .../config/persistence/WriterConfig.java | 77 ---- .../sample/orders/entities/OrderEntity.java | 74 +++- .../orders/entities/OrderItemEntity.java | 78 ++-- .../orders/messaging/OrdersEventHandler.java | 2 +- .../orders/repositories/OrderRepository.java | 2 + .../sample/orders/services/OrderService.java | 21 +- .../sample/orders/web/OrderController.java | 1 + .../sample/orders/web/payload/Order.java | 1 + .../orders/web/payload/OrderItemMapper.java | 12 + .../orders/web/payload/OrderMapper.java | 8 +- src/orders/src/main/resources/schema.sql | 16 + 15 files changed, 345 insertions(+), 378 deletions(-) create mode 100644 src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java delete mode 100644 src/orders/src/main/java/com/amazon/sample/orders/config/persistence/ReaderConfig.java delete mode 100644 src/orders/src/main/java/com/amazon/sample/orders/config/persistence/WriterConfig.java create mode 100644 src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderItemMapper.java create mode 100644 src/orders/src/main/resources/schema.sql diff --git a/src/orders/pom.xml b/src/orders/pom.xml index 53831d97d..df2abe12c 100644 --- a/src/orders/pom.xml +++ b/src/orders/pom.xml @@ -1,176 +1,176 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.1 - - - com.amazon.sample - orders - 0.0.1-SNAPSHOT - orders - Orders component + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.1 + + + com.amazon.sample + orders + 0.0.1-SNAPSHOT + orders + Orders component - - 17 - 1.5.5.Final - + + 17 + 1.5.5.Final + - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.springframework.boot - spring-boot-properties-migrator - runtime - - - org.springframework.boot - spring-boot-starter-validation - runtime - - - io.micrometer - micrometer-registry-prometheus - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.mariadb.jdbc - mariadb-java-client - 3.3.2 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.springframework.boot - spring-boot-starter-log4j2 - - - org.springframework.boot - spring-boot-starter-amqp - - - org.springdoc - springdoc-openapi-ui - 1.7.0 - - - com.fasterxml.jackson.core - jackson-databind - 2.16.1 - + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-properties-migrator + runtime + + + org.springframework.boot + spring-boot-starter-validation + runtime + + + io.micrometer + micrometer-registry-prometheus + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.mariadb.jdbc + mariadb-java-client + 3.3.2 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springdoc + springdoc-openapi-ui + 1.7.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.16.1 + - - org.mapstruct - mapstruct - ${mapstruct.version} - - - org.mapstruct - mapstruct-processor - ${mapstruct.version} - provided - + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + - - com.h2database - h2 - runtime - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - - start - stop - - - - - - org.springdoc - springdoc-openapi-maven-plugin - 1.4 - - - integration-test - - generate - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.12.1 - - ${java.version} - ${java.version} - - - org.mapstruct - mapstruct-processor - ${mapstruct.version} - - - org.projectlombok - lombok-mapstruct-binding - 0.2.0 - - - org.projectlombok - lombok - ${lombok.version} - - - - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + + start + stop + + + + + + org.springdoc + springdoc-openapi-maven-plugin + 1.4 + + + integration-test + + generate + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.12.1 + + ${java.version} + ${java.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.projectlombok + lombok + ${lombok.version} + + + + + + diff --git a/src/orders/src/main/java/com/amazon/sample/orders/OrdersApplication.java b/src/orders/src/main/java/com/amazon/sample/orders/OrdersApplication.java index 8ee07e6a3..159c3cc40 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/OrdersApplication.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/OrdersApplication.java @@ -27,5 +27,4 @@ public class OrdersApplication { public static void main(String[] args) { SpringApplication.run(OrdersApplication.class, args); } - } diff --git a/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java b/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java new file mode 100644 index 000000000..39dd4af10 --- /dev/null +++ b/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java @@ -0,0 +1,22 @@ +package com.amazon.sample.orders.config; + + +import com.amazon.sample.orders.entities.OrderEntity; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback; + +import java.util.UUID; + +@Configuration +public class PersistenceConfig { + @Bean + BeforeConvertCallback beforeSaveCallback() { + return (entity) -> { + if (entity.getId() == null) { + entity.setId(UUID.randomUUID().toString()); + } + return entity; + }; + } +} diff --git a/src/orders/src/main/java/com/amazon/sample/orders/config/persistence/ReaderConfig.java b/src/orders/src/main/java/com/amazon/sample/orders/config/persistence/ReaderConfig.java deleted file mode 100644 index 0acde1140..000000000 --- a/src/orders/src/main/java/com/amazon/sample/orders/config/persistence/ReaderConfig.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: MIT-0 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.amazon.sample.orders.config.persistence; - -import com.amazon.sample.orders.repositories.ReadOnlyRepository; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.jdbc.DataSourceBuilder; -import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; -import org.springframework.context.annotation.*; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import jakarta.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -@Configuration -@Profile("mysql") -@EnableTransactionManagement -@EnableJpaRepositories( - entityManagerFactoryRef = "readerEntityManagerFactory", - transactionManagerRef = "readerTransactionManager", - includeFilters = @ComponentScan.Filter(ReadOnlyRepository.class), - basePackages = { - "com.amazon.sample.orders" - } -) -public class ReaderConfig { - - @Bean(name = "readerDataSource") - @ConfigurationProperties(prefix = "spring.datasource.reader") - public DataSource customerDataSource() { - return DataSourceBuilder.create().build(); - } - - @Bean(name = "readerEntityManagerFactory") - public LocalContainerEntityManagerFactoryBean - entityManagerFactory( - EntityManagerFactoryBuilder builder, - @Qualifier("readerDataSource") DataSource dataSource - ) { - return builder - .dataSource(dataSource) - .packages("com.amazon.sample.orders") - .persistenceUnit("reader") - .build(); - } - - @Bean(name = "readerTransactionManager") - public PlatformTransactionManager customerTransactionManager( - @Qualifier("readerEntityManagerFactory") EntityManagerFactory customerEntityManagerFactory - ) { - return new JpaTransactionManager(customerEntityManagerFactory); - } -} diff --git a/src/orders/src/main/java/com/amazon/sample/orders/config/persistence/WriterConfig.java b/src/orders/src/main/java/com/amazon/sample/orders/config/persistence/WriterConfig.java deleted file mode 100644 index 09d100bae..000000000 --- a/src/orders/src/main/java/com/amazon/sample/orders/config/persistence/WriterConfig.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: MIT-0 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.amazon.sample.orders.config.persistence; - -import com.amazon.sample.orders.repositories.ReadOnlyRepository; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.jdbc.DataSourceBuilder; -import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; -import org.springframework.context.annotation.*; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import jakarta.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -@Configuration -@Profile("mysql") -@EnableTransactionManagement -@EnableJpaRepositories( - entityManagerFactoryRef = "writerEntityManagerFactory", - transactionManagerRef = "writerTransactionManager", - excludeFilters = @ComponentScan.Filter(ReadOnlyRepository.class), - basePackages = { - "com.amazon.sample.orders" - } -) -public class WriterConfig { - - @Primary - @Bean(name = "writerDataSource") - @ConfigurationProperties(prefix = "spring.datasource.writer") - public DataSource customerDataSource() { - return DataSourceBuilder.create().build(); - } - - @Primary - @Bean(name = "writerEntityManagerFactory") - public LocalContainerEntityManagerFactoryBean - entityManagerFactory( - EntityManagerFactoryBuilder builder, - @Qualifier("writerDataSource") DataSource dataSource - ) { - return builder - .dataSource(dataSource) - .packages("com.amazon.sample.orders") - .persistenceUnit("writer") - .build(); - } - - @Primary - @Bean(name = "writerTransactionManager") - public PlatformTransactionManager writerTransactionManager( - @Qualifier("writerEntityManagerFactory") EntityManagerFactory writerEntityManagerFactory - ) { - return new JpaTransactionManager(writerEntityManagerFactory); - } -} diff --git a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java index 668d8d31e..b35cbdfc7 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java @@ -18,30 +18,76 @@ package com.amazon.sample.orders.entities; -import lombok.Data; -import org.hibernate.annotations.GenericGenerator; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.MappedCollection; +import org.springframework.data.relational.core.mapping.Table; -import jakarta.persistence.*; import java.util.ArrayList; import java.util.List; -@Entity -@Table(name="CUSTOMER_ORDER") -@Data +@Table public class OrderEntity { - @Id - @GeneratedValue(generator = "uuid") - @GenericGenerator(name = "uuid", strategy = "uuid2") private String id; private String firstName; private String lastName; private String email; - @OneToMany( - mappedBy = "order", - orphanRemoval = true, - fetch = FetchType.EAGER - ) + @MappedCollection(keyColumn = "PRODUCT_ID") private List items = new ArrayList<>(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public void addItem(OrderItemEntity item) { + this.items.add(item); + } + + @Override + public String toString() { + return "OrderEntity{" + + "id='" + id + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", items=" + items + + '}'; + } } \ No newline at end of file diff --git a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java index 329c6f366..367ebcf47 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java @@ -18,42 +18,70 @@ package com.amazon.sample.orders.entities; -import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; -import jakarta.persistence.*; -import java.io.Serializable; - -@Entity -@Table(name="CUSTOMER_ORDER_ITEM") -@Data +@Table public class OrderItemEntity { - @Embeddable - @Data - public static class Key implements Serializable { - private String orderId; - private String productId; - } - - @EmbeddedId - @AttributeOverrides({ - @AttributeOverride(name="productId", - column=@Column(length=64)) - }) - private Key id; - @Transient private String productId; private int quantity; - private int price; + private int unitCost; private String name; private int totalCost; - @ManyToOne(fetch = FetchType.LAZY) - @MapsId("orderId") + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public int getUnitCost() { + return unitCost; + } - private OrderEntity order; + public void setUnitCost(int unitCost) { + this.unitCost = unitCost; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getTotalCost() { + return totalCost; + } + + public void setTotalCost(int totalCost) { + this.totalCost = totalCost; + } + + @Override + public String toString() { + return "OrderItemEntity{" + + ", productId='" + productId + '\'' + + ", quantity=" + quantity + + ", unitCost=" + unitCost + + ", name='" + name + '\'' + + ", totalCost=" + totalCost + + '}'; + } } diff --git a/src/orders/src/main/java/com/amazon/sample/orders/messaging/OrdersEventHandler.java b/src/orders/src/main/java/com/amazon/sample/orders/messaging/OrdersEventHandler.java index 35e2a6fd5..a7feed46c 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/messaging/OrdersEventHandler.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/messaging/OrdersEventHandler.java @@ -47,7 +47,7 @@ public void postCreatedEvent(OrderEntity entity) { order.setFirstName(entity.getFirstName()); order.setLastName(entity.getLastName()); order.setEmail(entity.getEmail()); - order.setOrderItems(entity.getItems()); + order.setOrderItems(entity.getItems().stream().toList()); OrderCreatedEvent event = new OrderCreatedEvent(); event.setOrder(order); diff --git a/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderRepository.java b/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderRepository.java index b40bd6278..d53eb2245 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderRepository.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderRepository.java @@ -20,7 +20,9 @@ import com.amazon.sample.orders.entities.OrderEntity; import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; +@Repository public interface OrderRepository extends CrudRepository { } diff --git a/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java b/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java index 13d0b8d2d..44189ab06 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java @@ -25,16 +25,18 @@ import com.amazon.sample.orders.repositories.OrderRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.relational.core.mapping.event.AbstractRelationalEventListener; +import org.springframework.data.relational.core.mapping.event.AfterSaveEvent; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import jakarta.transaction.Transactional; import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @Service @Slf4j -public class OrderService { +public class OrderService extends AbstractRelationalEventListener { @Autowired private OrderRepository repository; @@ -47,19 +49,8 @@ public class OrderService { @Transactional public OrderEntity create(OrderEntity order) { - for(OrderItemEntity item : order.getItems()) { - item.setOrder(order); - - OrderItemEntity.Key key = new OrderItemEntity.Key(); - //key.setProductId(item.getProductId()); - - item.setId(key); - } - OrderEntity entity = repository.save(order); - eventHandler.postCreatedEvent(entity); - return entity; } @@ -67,4 +58,8 @@ public List list() { return StreamSupport.stream(this.readRepository.findAll().spliterator(), false) .collect(Collectors.toList()); } + + protected void onAfterSave(AfterSaveEvent orderCreated) { + this.eventHandler.postCreatedEvent(orderCreated.getEntity()); + } } diff --git a/src/orders/src/main/java/com/amazon/sample/orders/web/OrderController.java b/src/orders/src/main/java/com/amazon/sample/orders/web/OrderController.java index c3959c7aa..cedcc1194 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/web/OrderController.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/web/OrderController.java @@ -18,6 +18,7 @@ package com.amazon.sample.orders.web; +import com.amazon.sample.orders.entities.OrderEntity; import com.amazon.sample.orders.services.OrderService; import com.amazon.sample.orders.web.payload.*; diff --git a/src/orders/src/main/java/com/amazon/sample/orders/web/payload/Order.java b/src/orders/src/main/java/com/amazon/sample/orders/web/payload/Order.java index 50e4d9c81..9a13925f2 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/web/payload/Order.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/web/payload/Order.java @@ -21,6 +21,7 @@ import lombok.Data; import java.util.List; +import java.util.Set; @Data public class Order { diff --git a/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderItemMapper.java b/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderItemMapper.java new file mode 100644 index 000000000..a9fa1c836 --- /dev/null +++ b/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderItemMapper.java @@ -0,0 +1,12 @@ +package com.amazon.sample.orders.web.payload; + +import com.amazon.sample.orders.entities.OrderItemEntity; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface OrderItemMapper { + + OrderItem toOrderItem(OrderItemEntity entity); + + OrderItemEntity toOrderItemEntity(OrderItem item); +} diff --git a/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderMapper.java b/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderMapper.java index d0e9c876c..e796f6bb3 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderMapper.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/web/payload/OrderMapper.java @@ -22,15 +22,11 @@ import com.amazon.sample.orders.entities.OrderItemEntity; import org.mapstruct.Mapper; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = OrderItemMapper.class) public interface OrderMapper { - Order toOrder(OrderEntity entity); - - OrderItem toOrderItem(OrderItemEntity entity); ExistingOrder toExistingOrder(OrderEntity entity); OrderEntity toOrderEntity(Order order); - - OrderItemEntity toOrderItemEntity(OrderItem item); } + diff --git a/src/orders/src/main/resources/schema.sql b/src/orders/src/main/resources/schema.sql new file mode 100644 index 000000000..6b15ff502 --- /dev/null +++ b/src/orders/src/main/resources/schema.sql @@ -0,0 +1,16 @@ +create table IF NOT EXISTS order_entity ( + id varchar(255) not null, + first_name varchar(255) not null, + last_name varchar(255), + email varchar(15), + primary key (id) +); + +create table IF NOT EXISTS order_item_entity ( + product_id varchar(255) not null, + name varchar(255) not null, + quantity int, + unit_cost int, + total_cost int, + order_entity varchar(255) not null +); \ No newline at end of file From e25d3108d2528bc220c50dc40341fd84b797f261 Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 03:28:50 +0000 Subject: [PATCH 2/8] Fixed tests and simplified repository model --- deploy/docker-compose/docker-compose.yml | 19 ++++++------- .../charts/orders/templates/configmap.yml | 3 +- .../charts/orders/templates/deployment.yaml | 14 ++-------- deploy/terraform/lib/apprunner/orders.tf | 25 ++++++++--------- src/orders/docker-compose.yml | 25 +++++++++-------- .../sample/orders/metrics/OrdersMetrics.java | 1 - .../repositories/OrderReadRepository.java | 27 ------------------ .../repositories/ReadOnlyRepository.java | 28 ------------------- .../sample/orders/services/OrderService.java | 9 ++---- .../src/main/resources/application-mysql.yml | 21 ++------------ src/orders/src/main/resources/schema.sql | 2 +- .../orders/metrics/OrdersMetricsTest.java | 16 ++++------- 12 files changed, 48 insertions(+), 142 deletions(-) delete mode 100644 src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderReadRepository.java delete mode 100644 src/orders/src/main/java/com/amazon/sample/orders/repositories/ReadOnlyRepository.java diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml index 9104d6aab..000837e54 100644 --- a/deploy/docker-compose/docker-compose.yml +++ b/deploy/docker-compose/docker-compose.yml @@ -1,12 +1,12 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT @@ -19,7 +19,7 @@ version: '2' services: ui: ports: - - 8888:8080 + - 8888:8080 environment: - JAVA_OPTS=-XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom - SERVER_TOMCAT_ACCESSLOG_ENABLED=true @@ -79,7 +79,7 @@ services: hostname: carts-db restart: always mem_limit: 256m - + orders: hostname: orders image: retail-store-sample-orders:${TAG:-latest} @@ -88,12 +88,9 @@ services: - JAVA_OPTS=-XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom - SERVER_TOMCAT_ACCESSLOG_ENABLED=true - SPRING_PROFILES_ACTIVE=mysql,rabbitmq - - SPRING_DATASOURCE_WRITER_URL=jdbc:mariadb://orders-db:3306/orders - - SPRING_DATASOURCE_WRITER_USERNAME=orders_user - - SPRING_DATASOURCE_WRITER_PASSWORD=${MYSQL_PASSWORD} - - SPRING_DATASOURCE_READER_URL=jdbc:mariadb://orders-db:3306/orders - - SPRING_DATASOURCE_READER_USERNAME=orders_user - - SPRING_DATASOURCE_READER_PASSWORD=${MYSQL_PASSWORD} + - SPRING_DATASOURCE_URL=jdbc:mariadb://orders-db:3306/orders + - SPRING_DATASOURCE_USERNAME=orders_user + - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD} - SPRING_RABBITMQ_HOST=rabbitmq mem_limit: 512m cap_drop: @@ -145,4 +142,4 @@ services: image: rabbitmq:3-management ports: - "5672:5672" - - "15672:15672" \ No newline at end of file + - "15672:15672" diff --git a/deploy/kubernetes/charts/orders/templates/configmap.yml b/deploy/kubernetes/charts/orders/templates/configmap.yml index 179f90799..8d9bfef23 100644 --- a/deploy/kubernetes/charts/orders/templates/configmap.yml +++ b/deploy/kubernetes/charts/orders/templates/configmap.yml @@ -5,7 +5,6 @@ metadata: name: {{ include "orders.configMapName" . }} data: SPRING_PROFILES_ACTIVE: mysql,rabbitmq - SPRING_DATASOURCE_READER_URL: {{ include "orders.mysql.endpoint" . }} - SPRING_DATASOURCE_WRITER_URL: {{ include "orders.mysql.reader.endpoint" . }} + SPRING_DATASOURCE_URL: {{ include "orders.mysql.endpoint" . }} SPRING_RABBITMQ_ADDRESSES: {{ include "orders.rabbitmq.addresses" . }} {{- end }} diff --git a/deploy/kubernetes/charts/orders/templates/deployment.yaml b/deploy/kubernetes/charts/orders/templates/deployment.yaml index 3eb5bb802..1f5cc601c 100644 --- a/deploy/kubernetes/charts/orders/templates/deployment.yaml +++ b/deploy/kubernetes/charts/orders/templates/deployment.yaml @@ -39,26 +39,16 @@ spec: env: - name: JAVA_OPTS value: -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom - - name: SPRING_DATASOURCE_WRITER_USERNAME + - name: SPRING_DATASOURCE_USERNAME valueFrom: secretKeyRef: name: {{ .Values.mysql.secret.name }} key: username - - name: SPRING_DATASOURCE_WRITER_PASSWORD + - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.mysql.secret.name }} key: password - - name: SPRING_DATASOURCE_READER_USERNAME - valueFrom: - secretKeyRef: - name: {{ .Values.mysql.reader.secret.name }} - key: username - - name: SPRING_DATASOURCE_READER_PASSWORD - valueFrom: - secretKeyRef: - name: {{ .Values.mysql.reader.secret.name }} - key: password envFrom: - secretRef: name: {{ .Values.rabbitmq.secret.name }} diff --git a/deploy/terraform/lib/apprunner/orders.tf b/deploy/terraform/lib/apprunner/orders.tf index ccedda7f1..cff7e73dc 100644 --- a/deploy/terraform/lib/apprunner/orders.tf +++ b/deploy/terraform/lib/apprunner/orders.tf @@ -1,21 +1,18 @@ resource "aws_apprunner_service" "orders" { service_name = "${var.environment_name}-orders" - + source_configuration { auto_deployments_enabled = false image_repository { image_configuration { port = 8080 runtime_environment_secrets = { - SPRING_DATASOURCE_WRITER_URL = "${aws_secretsmanager_secret.orders_db.arn}:host::" - SPRING_DATASOURCE_WRITER_USERNAME = "${aws_secretsmanager_secret.orders_db.arn}:username::" - SPRING_DATASOURCE_WRITER_PASSWORD = "${aws_secretsmanager_secret.orders_db.arn}:password::" - SPRING_DATASOURCE_READER_URL = "${aws_secretsmanager_secret.orders_db.arn}:host::" - SPRING_DATASOURCE_READER_USERNAME = "${aws_secretsmanager_secret.orders_db.arn}:username::" - SPRING_DATASOURCE_READER_PASSWORD = "${aws_secretsmanager_secret.orders_db.arn}:password::" - SPRING_RABBITMQ_ADDRESSES = "${aws_secretsmanager_secret.mq.arn}:host::" - SPRING_RABBITMQ_USER = "${aws_secretsmanager_secret.mq.arn}:username::" - SPRING_RABBITMQ_PASSWORD = "${aws_secretsmanager_secret.mq.arn}:password::" + SPRING_DATASOURCE_URL = "${aws_secretsmanager_secret.orders_db.arn}:host::" + SPRING_DATASOURCE_USERNAME = "${aws_secretsmanager_secret.orders_db.arn}:username::" + SPRING_DATASOURCE_PASSWORD = "${aws_secretsmanager_secret.orders_db.arn}:password::" + SPRING_RABBITMQ_ADDRESSES = "${aws_secretsmanager_secret.mq.arn}:host::" + SPRING_RABBITMQ_USER = "${aws_secretsmanager_secret.mq.arn}:username::" + SPRING_RABBITMQ_PASSWORD = "${aws_secretsmanager_secret.mq.arn}:password::" } } image_identifier = module.container_images.result.orders @@ -59,8 +56,8 @@ resource "aws_apprunner_vpc_ingress_connection" "orders" { } resource "random_string" "random_orders_secret" { - length = 4 - special = false + length = 4 + special = false } resource "aws_secretsmanager_secret" "orders_db" { @@ -106,7 +103,7 @@ data "aws_iam_policy_document" "orders_db_secret" { "secretsmanager:GetSecretValue", "kms:Decrypt*" ] - effect = "Allow" + effect = "Allow" resources = [ aws_secretsmanager_secret.orders_db.arn, aws_secretsmanager_secret.mq.arn, @@ -135,4 +132,4 @@ resource "aws_security_group" "orders" { } tags = var.tags -} \ No newline at end of file +} diff --git a/src/orders/docker-compose.yml b/src/orders/docker-compose.yml index 4f5c7250c..6c27cf406 100644 --- a/src/orders/docker-compose.yml +++ b/src/orders/docker-compose.yml @@ -22,19 +22,19 @@ services: environment: - reschedule=on-node-failure - SERVER_TOMCAT_ACCESSLOG_ENABLED=true - - SPRING_PROFILES_ACTIVE=mysql,activemq - - SPRING_DATASOURCE_WRITER_URL=jdbc:mysql://orders-db:3306/orders - - SPRING_DATASOURCE_WRITER_USERNAME=orders_user - - SPRING_DATASOURCE_WRITER_PASSWORD=${MYSQL_PASSWORD} - - SPRING_DATASOURCE_READER_URL=jdbc:mysql://orders-db:3306/orders - - SPRING_DATASOURCE_READER_USERNAME=orders_user - - SPRING_DATASOURCE_READER_PASSWORD=${MYSQL_PASSWORD} - - SPRING_DATASOURCE_WRITER_MAXIMUMPOOLSIZE=20 + - SPRING_PROFILES_ACTIVE=mysql,rabbitmq + - SPRING_DATASOURCE_URL=jdbc:mariadb://orders-db:3306/orders + - SPRING_DATASOURCE_USERNAME=orders_user + - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD} - SPRING_RABBITMQ_HOST=rabbitmq ports: - "8083:8080" healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"] + test: + [ + "CMD-SHELL", + "curl -f http://localhost:8080/actuator/health || exit 1" + ] interval: 10s timeout: 10s retries: 3 @@ -57,7 +57,11 @@ services: ports: - "3306:3306" healthcheck: - test: ["CMD-SHELL", "mysql -u root -p${MYSQL_PASSWORD} -e 'SELECT 1 cache'"] + test: + [ + "CMD-SHELL", + "mysql -u root -p${MYSQL_PASSWORD} -e 'SELECT 1 cache'" + ] interval: 1s timeout: 3s retries: 30 @@ -72,4 +76,3 @@ services: ports: - "61616:61616" - "8161:8161" - diff --git a/src/orders/src/main/java/com/amazon/sample/orders/metrics/OrdersMetrics.java b/src/orders/src/main/java/com/amazon/sample/orders/metrics/OrdersMetrics.java index 4b4f6af54..a8b18c2b8 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/metrics/OrdersMetrics.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/metrics/OrdersMetrics.java @@ -21,7 +21,6 @@ import com.amazon.sample.events.orders.OrderCreatedEvent; import com.amazon.sample.orders.entities.OrderItemEntity; import io.micrometer.core.instrument.Counter; -import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionalEventListener; diff --git a/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderReadRepository.java b/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderReadRepository.java deleted file mode 100644 index 80654179e..000000000 --- a/src/orders/src/main/java/com/amazon/sample/orders/repositories/OrderReadRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: MIT-0 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.amazon.sample.orders.repositories; - -import com.amazon.sample.orders.entities.OrderEntity; -import org.springframework.data.repository.CrudRepository; - -@ReadOnlyRepository -public interface OrderReadRepository extends CrudRepository { - -} diff --git a/src/orders/src/main/java/com/amazon/sample/orders/repositories/ReadOnlyRepository.java b/src/orders/src/main/java/com/amazon/sample/orders/repositories/ReadOnlyRepository.java deleted file mode 100644 index 0589ece65..000000000 --- a/src/orders/src/main/java/com/amazon/sample/orders/repositories/ReadOnlyRepository.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: MIT-0 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.amazon.sample.orders.repositories; - - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE}) -@Documented -public @interface ReadOnlyRepository { -} diff --git a/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java b/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java index 44189ab06..6b32acb87 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/services/OrderService.java @@ -19,9 +19,7 @@ package com.amazon.sample.orders.services; import com.amazon.sample.orders.entities.OrderEntity; -import com.amazon.sample.orders.entities.OrderItemEntity; import com.amazon.sample.orders.messaging.OrdersEventHandler; -import com.amazon.sample.orders.repositories.OrderReadRepository; import com.amazon.sample.orders.repositories.OrderRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -41,9 +39,6 @@ public class OrderService extends AbstractRelationalEventListener { @Autowired private OrderRepository repository; - @Autowired - private OrderReadRepository readRepository; - @Autowired private OrdersEventHandler eventHandler; @@ -55,8 +50,8 @@ public OrderEntity create(OrderEntity order) { } public List list() { - return StreamSupport.stream(this.readRepository.findAll().spliterator(), false) - .collect(Collectors.toList()); + return StreamSupport.stream(this.repository.findAll().spliterator(), false) + .collect(Collectors.toList()); } protected void onAfterSave(AfterSaveEvent orderCreated) { diff --git a/src/orders/src/main/resources/application-mysql.yml b/src/orders/src/main/resources/application-mysql.yml index 7a5208999..6ed773730 100644 --- a/src/orders/src/main/resources/application-mysql.yml +++ b/src/orders/src/main/resources/application-mysql.yml @@ -1,19 +1,4 @@ spring: - jpa: - generate-ddl: true - show-sql: false - hibernate: - ddl-auto: update - - datasource: - writer: - jdbc-url: ${spring.datasource.writer.url} - username: - password: - driverClassName: org.mariadb.jdbc.Driver - - reader: - jdbc-url: ${spring.datasource.reader.url} - username: - password: - driverClassName: org.mariadb.jdbc.Driver \ No newline at end of file + sql: + init: + mode: always \ No newline at end of file diff --git a/src/orders/src/main/resources/schema.sql b/src/orders/src/main/resources/schema.sql index 6b15ff502..8b6f020da 100644 --- a/src/orders/src/main/resources/schema.sql +++ b/src/orders/src/main/resources/schema.sql @@ -2,7 +2,7 @@ create table IF NOT EXISTS order_entity ( id varchar(255) not null, first_name varchar(255) not null, last_name varchar(255), - email varchar(15), + email varchar(255), primary key (id) ); diff --git a/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java b/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java index bf5d245f7..bf64a4881 100644 --- a/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java +++ b/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java @@ -26,22 +26,18 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionalEventListener; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.BDDAssertions.then; - public class OrdersMetricsTest { private MeterRegistry meterRegistry; private final String PRODUCT_1 = "Product1"; private final String PRODUCT_2 = "Product2"; + @BeforeEach void setUp() { meterRegistry = new SimpleMeterRegistry(); @@ -69,14 +65,14 @@ void testCreateCounterAndIncrement() { OrderItemEntity item = new OrderItemEntity(); item.setName("Pocket Watch"); item.setQuantity(5); - item.setPrice(100); + item.setUnitCost(100); item.setTotalCost(500); item.setProductId(PRODUCT_1); orderItems.add(item); item = new OrderItemEntity(); item.setName("Wood Watch"); item.setQuantity(2); - item.setPrice(50); + item.setUnitCost(50); item.setTotalCost(100); item.setProductId(PRODUCT_2); orderItems.add(item); @@ -85,15 +81,15 @@ void testCreateCounterAndIncrement() { event.setOrder(order); ordersMetrics.onOrderCreated(event); - var counter = meterRegistry.get("watch.orders").tags("productId","*").counter(); + var counter = meterRegistry.get("watch.orders").tags("productId", "*").counter(); then(counter).isNotNull(); then(counter.count()).isEqualTo(1); - var woodWatchCounter = meterRegistry.get("watch.orders").tags("productId",PRODUCT_2).counter(); + var woodWatchCounter = meterRegistry.get("watch.orders").tags("productId", PRODUCT_2).counter(); then(woodWatchCounter).isNotNull(); then(woodWatchCounter.count()).isEqualTo(2); - var pocketWatchCounter = meterRegistry.get("watch.orders").tags("productId",PRODUCT_1).counter(); + var pocketWatchCounter = meterRegistry.get("watch.orders").tags("productId", PRODUCT_1).counter(); then(pocketWatchCounter).isNotNull(); then(pocketWatchCounter.count()).isEqualTo(5); From 9c6a185d61a40fa9d63af82e207ed60e6365db29 Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 17:05:27 +0000 Subject: [PATCH 3/8] Migrated orders to postgres and added some basic tests --- deploy/docker-compose/docker-compose.yml | 22 ++-- src/catalog/docker-compose.yml | 2 +- src/orders/docker-compose.yml | 26 ++--- src/orders/pom.xml | 29 +++-- .../orders/config/PersistenceConfig.java | 17 ++- .../sample/orders/entities/OrderEntity.java | 16 ++- .../orders/entities/OrderItemEntity.java | 1 - .../src/main/resources/application-mysql.yml | 4 - src/orders/src/main/resources/application.yml | 4 +- .../migration/V1__Initial.sql} | 0 ...tricsTest.java => OrdersMetricsTests.java} | 2 +- .../services/OrderServicePostgresTests.java | 104 ++++++++++++++++++ 12 files changed, 183 insertions(+), 44 deletions(-) delete mode 100644 src/orders/src/main/resources/application-mysql.yml rename src/orders/src/main/resources/{schema.sql => db/migration/V1__Initial.sql} (100%) rename src/orders/src/test/java/com/amazon/sample/orders/metrics/{OrdersMetricsTest.java => OrdersMetricsTests.java} (99%) create mode 100644 src/orders/src/test/java/com/amazon/sample/orders/services/OrderServicePostgresTests.java diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml index 000837e54..664335653 100644 --- a/deploy/docker-compose/docker-compose.yml +++ b/deploy/docker-compose/docker-compose.yml @@ -87,8 +87,8 @@ services: environment: - JAVA_OPTS=-XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/urandom - SERVER_TOMCAT_ACCESSLOG_ENABLED=true - - SPRING_PROFILES_ACTIVE=mysql,rabbitmq - - SPRING_DATASOURCE_URL=jdbc:mariadb://orders-db:3306/orders + - SPRING_PROFILES_ACTIVE=rabbitmq + - SPRING_DATASOURCE_URL=jdbc:postgresql://orders-db:5432/orders - SPRING_DATASOURCE_USERNAME=orders_user - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD} - SPRING_RABBITMQ_HOST=rabbitmq @@ -97,15 +97,21 @@ services: - ALL orders-db: - image: mariadb:10.9 + image: postgres:16.1 hostname: orders-db restart: always + security_opt: + - no-new-privileges:true environment: - - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD} - - MYSQL_ALLOW_EMPTY_PASSWORD=true - - MYSQL_DATABASE=orders - - MYSQL_USER=orders_user - - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - reschedule=on-node-failure + - POSTGRES_PASSWORD=${MYSQL_PASSWORD} + - POSTGRES_DB=orders + - POSTGRES_USER=orders_user + healthcheck: + test: [ "CMD-SHELL", "pg_isready -d orders -U orders_user" ] + interval: 10s + timeout: 5s + retries: 30 mem_limit: 128m checkout: diff --git a/src/catalog/docker-compose.yml b/src/catalog/docker-compose.yml index f456e9b40..704e20080 100644 --- a/src/catalog/docker-compose.yml +++ b/src/catalog/docker-compose.yml @@ -10,7 +10,7 @@ services: image: microservices-demo/catalog hostname: catalog depends_on: - - catalog-db + - catalog-db restart: always cap_drop: - all diff --git a/src/orders/docker-compose.yml b/src/orders/docker-compose.yml index 6c27cf406..0bb8d6af1 100644 --- a/src/orders/docker-compose.yml +++ b/src/orders/docker-compose.yml @@ -22,8 +22,8 @@ services: environment: - reschedule=on-node-failure - SERVER_TOMCAT_ACCESSLOG_ENABLED=true - - SPRING_PROFILES_ACTIVE=mysql,rabbitmq - - SPRING_DATASOURCE_URL=jdbc:mariadb://orders-db:3306/orders + - SPRING_PROFILES_ACTIVE=rabbitmq + - SPRING_DATASOURCE_URL=jdbc:postgresql://orders-db:5432/orders - SPRING_DATASOURCE_USERNAME=orders_user - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD} - SPRING_RABBITMQ_HOST=rabbitmq @@ -42,28 +42,22 @@ services: # nosemgrep: yaml.docker-compose.security.writable-filesystem-service.writable-filesystem-service orders-db: - image: mysql:5.7 + image: postgres:16.1 hostname: orders-db restart: always security_opt: - no-new-privileges:true environment: - reschedule=on-node-failure - - MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD} - - MYSQL_ALLOW_EMPTY_PASSWORD=true - - MYSQL_DATABASE=orders - - MYSQL_USER=orders_user - - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - POSTGRES_PASSWORD=${MYSQL_PASSWORD} + - POSTGRES_DB=orders + - POSTGRES_USER=orders_user ports: - - "3306:3306" + - "5432:5432" healthcheck: - test: - [ - "CMD-SHELL", - "mysql -u root -p${MYSQL_PASSWORD} -e 'SELECT 1 cache'" - ] - interval: 1s - timeout: 3s + test: [ "CMD-SHELL", "pg_isready -d orders -U orders_user" ] + interval: 10s + timeout: 5s retries: 30 # nosemgrep: yaml.docker-compose.security.writable-filesystem-service.writable-filesystem-service diff --git a/src/orders/pom.xml b/src/orders/pom.xml index df2abe12c..a834eeea1 100644 --- a/src/orders/pom.xml +++ b/src/orders/pom.xml @@ -35,11 +35,6 @@ spring-boot-properties-migrator runtime - - org.springframework.boot - spring-boot-starter-validation - runtime - io.micrometer micrometer-registry-prometheus @@ -49,9 +44,27 @@ spring-boot-starter-data-jdbc - org.mariadb.jdbc - mariadb-java-client - 3.3.2 + org.flywaydb + flyway-core + + + org.postgresql + postgresql + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + postgresql + test + + + io.rest-assured + rest-assured + test org.springframework.boot diff --git a/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java b/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java index 39dd4af10..2f5453d1a 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/config/PersistenceConfig.java @@ -1,15 +1,20 @@ package com.amazon.sample.orders.config; - import com.amazon.sample.orders.entities.OrderEntity; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; +import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; +import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration; +import org.springframework.data.relational.RelationalManagedTypes; +import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback; +import java.util.Optional; import java.util.UUID; @Configuration -public class PersistenceConfig { +public class PersistenceConfig extends AbstractJdbcConfiguration { @Bean BeforeConvertCallback beforeSaveCallback() { return (entity) -> { @@ -19,4 +24,12 @@ BeforeConvertCallback beforeSaveCallback() { return entity; }; } + + @Override + public JdbcMappingContext jdbcMappingContext(Optional namingStrategy, + JdbcCustomConversions customConversions, RelationalManagedTypes jdbcManagedTypes) { + JdbcMappingContext context = super.jdbcMappingContext(namingStrategy, customConversions, jdbcManagedTypes); + context.setForceQuote(false); + return context; + } } diff --git a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java index b35cbdfc7..04c652eab 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderEntity.java @@ -33,9 +33,19 @@ public class OrderEntity { private String lastName; private String email; - @MappedCollection(keyColumn = "PRODUCT_ID") + @MappedCollection(keyColumn = "product_id") private List items = new ArrayList<>(); + public OrderEntity() { + + } + + public OrderEntity(String firstName, String lastName, String email) { + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + } + public String getId() { return id; } @@ -76,8 +86,10 @@ public void setItems(List items) { this.items = items; } - public void addItem(OrderItemEntity item) { + public OrderEntity addItem(OrderItemEntity item) { this.items.add(item); + + return this; } @Override diff --git a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java index 367ebcf47..dc921c7c2 100644 --- a/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java +++ b/src/orders/src/main/java/com/amazon/sample/orders/entities/OrderItemEntity.java @@ -18,7 +18,6 @@ package com.amazon.sample.orders.entities; -import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; @Table diff --git a/src/orders/src/main/resources/application-mysql.yml b/src/orders/src/main/resources/application-mysql.yml deleted file mode 100644 index 6ed773730..000000000 --- a/src/orders/src/main/resources/application-mysql.yml +++ /dev/null @@ -1,4 +0,0 @@ -spring: - sql: - init: - mode: always \ No newline at end of file diff --git a/src/orders/src/main/resources/application.yml b/src/orders/src/main/resources/application.yml index b59a3fef9..98b6b50a1 100644 --- a/src/orders/src/main/resources/application.yml +++ b/src/orders/src/main/resources/application.yml @@ -5,4 +5,6 @@ management: include: '*' server: - port: ${port:8080} \ No newline at end of file + port: ${port:8080} + +spring.flyway.baseline-on-migrate: true \ No newline at end of file diff --git a/src/orders/src/main/resources/schema.sql b/src/orders/src/main/resources/db/migration/V1__Initial.sql similarity index 100% rename from src/orders/src/main/resources/schema.sql rename to src/orders/src/main/resources/db/migration/V1__Initial.sql diff --git a/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java b/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTests.java similarity index 99% rename from src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java rename to src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTests.java index bf64a4881..ea499d18d 100644 --- a/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTest.java +++ b/src/orders/src/test/java/com/amazon/sample/orders/metrics/OrdersMetricsTests.java @@ -32,7 +32,7 @@ import static org.assertj.core.api.BDDAssertions.then; -public class OrdersMetricsTest { +public class OrdersMetricsTests { private MeterRegistry meterRegistry; private final String PRODUCT_1 = "Product1"; diff --git a/src/orders/src/test/java/com/amazon/sample/orders/services/OrderServicePostgresTests.java b/src/orders/src/test/java/com/amazon/sample/orders/services/OrderServicePostgresTests.java new file mode 100644 index 000000000..246e69317 --- /dev/null +++ b/src/orders/src/test/java/com/amazon/sample/orders/services/OrderServicePostgresTests.java @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.amazon.sample.orders.services; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.hasSize; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; + +import com.amazon.sample.orders.entities.OrderEntity; +import com.amazon.sample.orders.repositories.OrderRepository; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class OrderServicePostgresTests { + + @LocalServerPort + private Integer port; + + static PostgreSQLContainer postgres = new PostgreSQLContainer<>( + "postgres:16.1"); + + @BeforeAll + static void beforeAll() { + postgres.start(); + } + + @AfterAll + static void afterAll() { + postgres.stop(); + } + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + + @Autowired + OrderRepository orderRepository; + + @BeforeEach + void setUp() { + RestAssured.baseURI = "http://localhost:" + port; + orderRepository.deleteAll(); + } + + @Test + void shouldGetEmptyOrders() { + given() + .contentType(ContentType.JSON) + .when() + .get("/orders") + .then() + .statusCode(200) + .body(".", hasSize(0)); + } + + @Test + void shouldGetAllOrders() { + List orders = List.of( + new OrderEntity("first", "last", "email@example.com"), + new OrderEntity("first", "last", "email@example.com")); + orderRepository.saveAll(orders); + + given() + .contentType(ContentType.JSON) + .when() + .get("/orders") + .then() + .statusCode(200) + .body(".", hasSize(2)); + } +} \ No newline at end of file From 9e3f6db908fa2e764999731ef69a7a80930327ff Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 17:23:58 +0000 Subject: [PATCH 4/8] Update orders helm chart --- .../charts/orders/templates/_helpers.tpl | 40 +++------- .../charts/orders/templates/configmap.yml | 4 +- .../charts/orders/templates/deployment.yaml | 4 +- .../orders/templates/mysql-reader-secret.yaml | 9 --- .../charts/orders/templates/mysql-secret.yaml | 9 --- .../orders/templates/mysql-service.yaml | 17 ----- .../orders/templates/postgresql-secret.yaml | 9 +++ .../orders/templates/postgresql-service.yaml | 17 +++++ ...ulset.yaml => postgresql-statefulset.yaml} | 50 ++++++------ deploy/kubernetes/charts/orders/values.yaml | 12 +-- .../charts/templates/orders.yaml.gotmpl | 2 +- deploy/terraform/eks/default/kubernetes.tf | 76 ++++++++++--------- .../terraform/eks/default/values/orders.yaml | 6 +- .../terraform/lib/dependencies/orders_rds.tf | 16 ++-- 14 files changed, 127 insertions(+), 144 deletions(-) delete mode 100644 deploy/kubernetes/charts/orders/templates/mysql-reader-secret.yaml delete mode 100644 deploy/kubernetes/charts/orders/templates/mysql-secret.yaml delete mode 100644 deploy/kubernetes/charts/orders/templates/mysql-service.yaml create mode 100644 deploy/kubernetes/charts/orders/templates/postgresql-secret.yaml create mode 100644 deploy/kubernetes/charts/orders/templates/postgresql-service.yaml rename deploy/kubernetes/charts/orders/templates/{mysql-statefulset.yaml => postgresql-statefulset.yaml} (50%) diff --git a/deploy/kubernetes/charts/orders/templates/_helpers.tpl b/deploy/kubernetes/charts/orders/templates/_helpers.tpl index 3a9ad8fdc..a2287bdb8 100644 --- a/deploy/kubernetes/charts/orders/templates/_helpers.tpl +++ b/deploy/kubernetes/charts/orders/templates/_helpers.tpl @@ -86,16 +86,16 @@ Create the name of the config map to use {{- end }} {{- end -}} -{{- define "orders.mysql.fullname" -}} +{{- define "orders.postgresql.fullname" -}} {{- include "orders.fullname" . }}-mysql {{- end -}} {{/* Common labels for mysql */}} -{{- define "orders.mysql.labels" -}} +{{- define "orders.postgresql.labels" -}} helm.sh/chart: {{ include "orders.chart" . }} -{{ include "orders.mysql.selectorLabels" . }} +{{ include "orders.postgresql.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -105,7 +105,7 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels for mysql */}} -{{- define "orders.mysql.selectorLabels" -}} +{{- define "orders.postgresql.selectorLabels" -}} app.kubernetes.io/name: {{ include "orders.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/component: mysql @@ -123,35 +123,19 @@ app.kubernetes.io/component: mysql {{- end -}} {{- end }} -{{- define "orders.mysql.password" -}} -{{- if not (empty .Values.mysql.secret.password) -}} - {{- .Values.mysql.secret.password | b64enc -}} +{{- define "orders.postgresql.password" -}} +{{- if not (empty .Values.postgresql.secret.password) -}} + {{- .Values.postgresql.secret.password | b64enc -}} {{- else -}} - {{- include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" .Values.mysql.secret.name "Key" "password") -}} + {{- include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" .Values.postgresql.secret.name "Key" "password") -}} {{- end -}} {{- end -}} -{{- define "orders.mysql.reader.password" -}} -{{- if not (empty .Values.mysql.reader.secret.password) -}} - {{- .Values.mysql.reader.secret.password | b64enc -}} +{{- define "orders.postgresql.endpoint" -}} +{{- if not (empty .Values.postgresql.endpoint) -}} + {{- .Values.postgresql.endpoint -}} {{- else -}} - {{- include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" .Values.mysql.reader.secret.name "Key" "password") -}} -{{- end -}} -{{- end -}} - -{{- define "orders.mysql.endpoint" -}} -{{- if not (empty .Values.mysql.endpoint) -}} - {{- .Values.mysql.endpoint -}} -{{- else -}} -jdbc:mariadb://{{ include "orders.mysql.fullname" . }}:{{ .Values.mysql.service.port }}/{{ .Values.mysql.database }} -{{- end -}} -{{- end -}} - -{{- define "orders.mysql.reader.endpoint" -}} -{{- if not (empty .Values.mysql.reader.endpoint) -}} - {{- .Values.mysql.reader.endpoint -}} -{{- else -}} -{{- include "orders.mysql.endpoint" . -}} +jdbc:mariadb://{{ include "orders.postgresql.fullname" . }}:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.database }} {{- end -}} {{- end -}} diff --git a/deploy/kubernetes/charts/orders/templates/configmap.yml b/deploy/kubernetes/charts/orders/templates/configmap.yml index 8d9bfef23..391badf22 100644 --- a/deploy/kubernetes/charts/orders/templates/configmap.yml +++ b/deploy/kubernetes/charts/orders/templates/configmap.yml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "orders.configMapName" . }} data: - SPRING_PROFILES_ACTIVE: mysql,rabbitmq - SPRING_DATASOURCE_URL: {{ include "orders.mysql.endpoint" . }} + SPRING_PROFILES_ACTIVE: rabbitmq + SPRING_DATASOURCE_URL: jdbc:postgresql://{{ .Values.postgresql.endpoint.host }}:{{ .Values.postgresql.endpoint.port }}/{{ .Values.postgresql.database }} SPRING_RABBITMQ_ADDRESSES: {{ include "orders.rabbitmq.addresses" . }} {{- end }} diff --git a/deploy/kubernetes/charts/orders/templates/deployment.yaml b/deploy/kubernetes/charts/orders/templates/deployment.yaml index 1f5cc601c..f28f23ff9 100644 --- a/deploy/kubernetes/charts/orders/templates/deployment.yaml +++ b/deploy/kubernetes/charts/orders/templates/deployment.yaml @@ -42,12 +42,12 @@ spec: - name: SPRING_DATASOURCE_USERNAME valueFrom: secretKeyRef: - name: {{ .Values.mysql.secret.name }} + name: {{ .Values.postgresql.secret.name }} key: username - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.mysql.secret.name }} + name: {{ .Values.postgresql.secret.name }} key: password envFrom: - secretRef: diff --git a/deploy/kubernetes/charts/orders/templates/mysql-reader-secret.yaml b/deploy/kubernetes/charts/orders/templates/mysql-reader-secret.yaml deleted file mode 100644 index 17f2a1ff0..000000000 --- a/deploy/kubernetes/charts/orders/templates/mysql-reader-secret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- if .Values.mysql.reader.secret.create }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Values.mysql.reader.secret.name }} -data: - username: {{ .Values.mysql.reader.secret.username | b64enc | quote }} - password: "{{ include "orders.mysql.reader.password" . }}" -{{- end }} \ No newline at end of file diff --git a/deploy/kubernetes/charts/orders/templates/mysql-secret.yaml b/deploy/kubernetes/charts/orders/templates/mysql-secret.yaml deleted file mode 100644 index 44faebc4d..000000000 --- a/deploy/kubernetes/charts/orders/templates/mysql-secret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- if .Values.mysql.secret.create }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Values.mysql.secret.name }} -data: - username: {{ .Values.mysql.secret.username | b64enc | quote }} - password: "{{ include "orders.mysql.password" . }}" -{{- end }} \ No newline at end of file diff --git a/deploy/kubernetes/charts/orders/templates/mysql-service.yaml b/deploy/kubernetes/charts/orders/templates/mysql-service.yaml deleted file mode 100644 index 3bd08918f..000000000 --- a/deploy/kubernetes/charts/orders/templates/mysql-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.mysql.create }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "orders.mysql.fullname" . }} - labels: - {{- include "orders.mysql.labels" . | nindent 4 }} -spec: - type: {{ .Values.mysql.service.type }} - ports: - - port: {{ .Values.mysql.service.port }} - targetPort: mysql - protocol: TCP - name: mysql - selector: - {{- include "orders.mysql.selectorLabels" . | nindent 4 }} -{{- end }} \ No newline at end of file diff --git a/deploy/kubernetes/charts/orders/templates/postgresql-secret.yaml b/deploy/kubernetes/charts/orders/templates/postgresql-secret.yaml new file mode 100644 index 000000000..e088e59c5 --- /dev/null +++ b/deploy/kubernetes/charts/orders/templates/postgresql-secret.yaml @@ -0,0 +1,9 @@ +{{- if .Values.postgresql.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.postgresql.secret.name }} +data: + username: {{ .Values.postgresql.secret.username | b64enc | quote }} + password: "{{ include "orders.postgresql.password" . }}" +{{- end }} \ No newline at end of file diff --git a/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml b/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml new file mode 100644 index 000000000..699339fb2 --- /dev/null +++ b/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.postgresql.create }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "orders.postgresql.fullname" . }} + labels: + {{- include "orders.postgresql.labels" . | nindent 4 }} +spec: + type: {{ .Values.postgresql.service.type }} + ports: + - port: {{ .Values.postgresql.service.port }} + targetPort: mysql + protocol: TCP + name: mysql + selector: + {{- include "orders.postgresql.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/deploy/kubernetes/charts/orders/templates/mysql-statefulset.yaml b/deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml similarity index 50% rename from deploy/kubernetes/charts/orders/templates/mysql-statefulset.yaml rename to deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml index eaf85423b..479a15949 100644 --- a/deploy/kubernetes/charts/orders/templates/mysql-statefulset.yaml +++ b/deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml @@ -1,24 +1,24 @@ -{{- if .Values.mysql.create }} +{{- if .Values.postgresql.create }} apiVersion: apps/v1 kind: StatefulSet metadata: - name: {{ include "orders.mysql.fullname" . }} + name: {{ include "orders.postgresql.fullname" . }} labels: - {{- include "orders.mysql.labels" . | nindent 4 }} + {{- include "orders.postgresql.labels" . | nindent 4 }} spec: replicas: 1 - serviceName: {{ include "orders.mysql.fullname" . }} + serviceName: {{ include "orders.postgresql.fullname" . }} selector: matchLabels: - {{- include "orders.mysql.selectorLabels" . | nindent 6 }} + {{- include "orders.postgresql.selectorLabels" . | nindent 6 }} template: metadata: - {{- with .Values.mysql.podAnnotations }} + {{- with .Values.postgresql.podAnnotations }} annotations: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "orders.mysql.selectorLabels" . | nindent 8 }} + {{- include "orders.postgresql.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: @@ -26,22 +26,22 @@ spec: {{- end }} containers: - name: mysql - image: "{{ .Values.mysql.image.repository }}:{{ .Values.mysql.image.tag }}" - imagePullPolicy: {{ .Values.mysql.image.pullPolicy }} + image: "{{ .Values.postgresql.image.repository }}:{{ .Values.postgresql.image.tag }}" + imagePullPolicy: {{ .Values.postgresql.image.pullPolicy }} env: - name: MYSQL_ROOT_PASSWORD value: my-secret-pw - name: MYSQL_DATABASE - value: {{ .Values.mysql.database }} + value: {{ .Values.postgresql.database }} - name: MYSQL_USER valueFrom: secretKeyRef: - name: {{ .Values.mysql.secret.name }} + name: {{ .Values.postgresql.secret.name }} key: username - name: MYSQL_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.mysql.secret.name }} + name: {{ .Values.postgresql.secret.name }} key: password args: - "--ignore-db-dir=lost+found" @@ -52,41 +52,41 @@ spec: - name: mysql containerPort: 3306 protocol: TCP - {{- with .Values.mysql.nodeSelector }} + {{- with .Values.postgresql.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.mysql.affinity }} + {{- with .Values.postgresql.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.mysql.tolerations }} + {{- with .Values.postgresql.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} -{{- if .Values.mysql.persistentVolume.enabled }} +{{- if .Values.postgresql.persistentVolume.enabled }} volumeClaimTemplates: - metadata: name: data - {{- if .Values.mysql.persistentVolume.annotations }} + {{- if .Values.postgresql.persistentVolume.annotations }} annotations: -{{ toYaml .Values.mysql.persistentVolume.annotations | indent 10 }} +{{ toYaml .Values.postgresql.persistentVolume.annotations | indent 10 }} {{- end }} - {{- if .Values.mysql.persistentVolume.labels }} + {{- if .Values.postgresql.persistentVolume.labels }} labels: -{{ toYaml .Values.mysql.persistentVolume.labels | indent 10 }} +{{ toYaml .Values.postgresql.persistentVolume.labels | indent 10 }} {{- end }} spec: accessModes: -{{ toYaml .Values.mysql.persistentVolume.accessModes | indent 8 }} +{{ toYaml .Values.postgresql.persistentVolume.accessModes | indent 8 }} resources: requests: - storage: "{{ .Values.mysql.persistentVolume.size }}" - {{- if .Values.mysql.persistentVolume.storageClass }} - {{- if (eq "-" .Values.mysql.persistentVolume.storageClass) }} + storage: "{{ .Values.postgresql.persistentVolume.size }}" + {{- if .Values.postgresql.persistentVolume.storageClass }} + {{- if (eq "-" .Values.postgresql.persistentVolume.storageClass) }} storageClassName: "" {{- else }} - storageClassName: "{{ .Values.mysql.persistentVolume.storageClass }}" + storageClassName: "{{ .Values.postgresql.persistentVolume.storageClass }}" {{- end }} {{- end }} {{- else }} diff --git a/deploy/kubernetes/charts/orders/values.yaml b/deploy/kubernetes/charts/orders/values.yaml index ea30cb748..5df511981 100644 --- a/deploy/kubernetes/charts/orders/values.yaml +++ b/deploy/kubernetes/charts/orders/values.yaml @@ -70,12 +70,14 @@ configMap: create: true name: -mysql: +postgresql: create: true database: orders - endpoint: "" + endpoint: + host: "" + port: "" secret: create: true @@ -92,13 +94,13 @@ mysql: password: "" image: - repository: mysql + repository: postgres pullPolicy: IfNotPresent - tag: "5.7" + tag: "16.1" service: type: ClusterIP - port: 3306 + port: 5432 podAnnotations: {} diff --git a/deploy/kubernetes/charts/templates/orders.yaml.gotmpl b/deploy/kubernetes/charts/templates/orders.yaml.gotmpl index 163df87a2..6b36ab44a 100644 --- a/deploy/kubernetes/charts/templates/orders.yaml.gotmpl +++ b/deploy/kubernetes/charts/templates/orders.yaml.gotmpl @@ -5,7 +5,7 @@ image: {{end}} {{if env "RANDOM_PASSWORD" }} -mysql: +postgresql: secret: password: {{ env "RANDOM_PASSWORD" | default "" }} {{end}} \ No newline at end of file diff --git a/deploy/terraform/eks/default/kubernetes.tf b/deploy/terraform/eks/default/kubernetes.tf index 8818b1713..f733786c5 100644 --- a/deploy/terraform/eks/default/kubernetes.tf +++ b/deploy/terraform/eks/default/kubernetes.tf @@ -46,7 +46,7 @@ resource "time_sleep" "workloads" { create_duration = "30s" destroy_duration = "60s" - depends_on = [ + depends_on = [ null_resource.addons_blocker ] } @@ -64,12 +64,12 @@ resource "kubernetes_namespace_v1" "assets" { } resource "helm_release" "assets" { - name = "assets" - chart = "../../../kubernetes/charts/assets" + name = "assets" + chart = "../../../kubernetes/charts/assets" - namespace = kubernetes_namespace_v1.assets.metadata[0].name + namespace = kubernetes_namespace_v1.assets.metadata[0].name values = [ - templatefile("${path.module}/values/assets.yaml", { + templatefile("${path.module}/values/assets.yaml", { opentelemetry_enabled = var.opentelemetry_enabled }) ] @@ -88,13 +88,13 @@ resource "kubernetes_namespace_v1" "catalog" { } resource "helm_release" "catalog" { - name = "catalog" - chart = "../../../kubernetes/charts/catalog" + name = "catalog" + chart = "../../../kubernetes/charts/catalog" - namespace = kubernetes_namespace_v1.catalog.metadata[0].name + namespace = kubernetes_namespace_v1.catalog.metadata[0].name values = [ - templatefile("${path.module}/values/catalog.yaml", { + templatefile("${path.module}/values/catalog.yaml", { opentelemetry_enabled = var.opentelemetry_enabled database_endpoint = "${module.dependencies.catalog_db_endpoint}:${module.dependencies.catalog_db_port}" database_username = module.dependencies.catalog_db_master_username @@ -117,16 +117,16 @@ resource "kubernetes_namespace_v1" "carts" { } resource "helm_release" "carts" { - name = "carts" - chart = "../../../kubernetes/charts/carts" + name = "carts" + chart = "../../../kubernetes/charts/carts" - namespace = kubernetes_namespace_v1.carts.metadata[0].name + namespace = kubernetes_namespace_v1.carts.metadata[0].name values = [ - templatefile("${path.module}/values/carts.yaml", { + templatefile("${path.module}/values/carts.yaml", { opentelemetry_enabled = var.opentelemetry_enabled role_arn = module.iam_assumable_role_carts.iam_role_arn - table_name = module.dependencies.carts_dynamodb_table_name + table_name = module.dependencies.carts_dynamodb_table_name }) ] } @@ -144,13 +144,13 @@ resource "kubernetes_namespace_v1" "checkout" { } resource "helm_release" "checkout" { - name = "checkout" - chart = "../../../kubernetes/charts/checkout" + name = "checkout" + chart = "../../../kubernetes/charts/checkout" - namespace = kubernetes_namespace_v1.checkout.metadata[0].name + namespace = kubernetes_namespace_v1.checkout.metadata[0].name values = [ - templatefile("${path.module}/values/checkout.yaml", { + templatefile("${path.module}/values/checkout.yaml", { opentelemetry_enabled = var.opentelemetry_enabled redis_address = module.dependencies.checkout_elasticache_primary_endpoint redis_port = module.dependencies.checkout_elasticache_port @@ -172,21 +172,23 @@ resource "kubernetes_namespace_v1" "orders" { } resource "helm_release" "orders" { - name = "orders" - chart = "../../../kubernetes/charts/orders" + name = "orders" + chart = "../../../kubernetes/charts/orders" - namespace = kubernetes_namespace_v1.orders.metadata[0].name + namespace = kubernetes_namespace_v1.orders.metadata[0].name values = [ - templatefile("${path.module}/values/orders.yaml", { - opentelemetry_enabled = var.opentelemetry_enabled - database_endpoint = "jdbc:mariadb://${module.dependencies.orders_db_endpoint}:${module.dependencies.orders_db_port}/${module.dependencies.orders_db_database_name}" - database_username = module.dependencies.orders_db_master_username - database_password = module.dependencies.orders_db_master_password - rabbitmq_endpoint = module.dependencies.mq_broker_endpoint - rabbitmq_username = module.dependencies.mq_user - rabbitmq_password = module.dependencies.mq_password - security_group_id = aws_security_group.orders.id + templatefile("${path.module}/values/orders.yaml", { + opentelemetry_enabled = var.opentelemetry_enabled + database_endpoint_host = module.dependencies.orders_db_endpoint + database_endpoint_port = module.dependencies.orders_db_port + database_name = module.dependencies.orders_db_database_name + database_username = module.dependencies.orders_db_master_username + database_password = module.dependencies.orders_db_master_password + rabbitmq_endpoint = module.dependencies.mq_broker_endpoint + rabbitmq_username = module.dependencies.mq_user + rabbitmq_password = module.dependencies.mq_password + security_group_id = aws_security_group.orders.id }) ] } @@ -204,10 +206,10 @@ resource "kubernetes_namespace_v1" "ui" { } resource "helm_release" "ui" { - name = "ui" - chart = "../../../kubernetes/charts/ui" + name = "ui" + chart = "../../../kubernetes/charts/ui" - namespace = kubernetes_namespace_v1.ui.metadata[0].name + namespace = kubernetes_namespace_v1.ui.metadata[0].name values = [ templatefile("${path.module}/values/ui.yaml", { @@ -220,23 +222,23 @@ resource "helm_release" "ui" { resource "time_sleep" "restart_pods" { create_duration = "30s" - depends_on = [ + depends_on = [ helm_release.ui, helm_release.opentelemetry ] } resource "null_resource" "restart_pods" { - depends_on = [ time_sleep.restart_pods ] + depends_on = [time_sleep.restart_pods] provisioner "local-exec" { interpreter = ["/bin/bash", "-c"] environment = { KUBECONFIG = base64encode(local.kubeconfig) } - + command = <<-EOT kubectl delete pod -A -l app.kuberneres.io/owner=retail-store-sample --kubeconfig <(echo $KUBECONFIG | base64 -d) EOT } -} \ No newline at end of file +} diff --git a/deploy/terraform/eks/default/values/orders.yaml b/deploy/terraform/eks/default/values/orders.yaml index 103fc9fbc..7882ac714 100644 --- a/deploy/terraform/eks/default/values/orders.yaml +++ b/deploy/terraform/eks/default/values/orders.yaml @@ -1,7 +1,11 @@ mysql: create: false - endpoint: ${database_endpoint} + database: ${database_name} + + endpoint: + host: ${database_endpoint_host} + port: "${database_endpoint_port}" secret: username: ${database_username} diff --git a/deploy/terraform/lib/dependencies/orders_rds.tf b/deploy/terraform/lib/dependencies/orders_rds.tf index 219cbcfb6..7f3a4d993 100644 --- a/deploy/terraform/lib/dependencies/orders_rds.tf +++ b/deploy/terraform/lib/dependencies/orders_rds.tf @@ -2,16 +2,16 @@ module "orders_rds" { source = "terraform-aws-modules/rds-aurora/aws" version = "7.7.1" - name = "${var.environment_name}-orders" - engine = "aurora-mysql" - engine_version = "5.7" + name = "${var.environment_name}-orders" + engine = "aurora-postgresql" + engine_version = "15.5" instance_class = "db.t3.small" instances = { one = {} } - vpc_id = var.vpc_id + vpc_id = var.vpc_id subnets = var.subnet_ids allowed_security_groups = concat(var.allowed_security_group_ids, [var.orders_security_group_id]) @@ -25,16 +25,16 @@ module "orders_rds" { create_db_parameter_group = true db_parameter_group_name = "${var.environment_name}-orders" - db_parameter_group_family = "aurora-mysql5.7" + db_parameter_group_family = "aurora-postgresql15" create_db_cluster_parameter_group = true db_cluster_parameter_group_name = "${var.environment_name}-orders" - db_cluster_parameter_group_family = "aurora-mysql5.7" + db_cluster_parameter_group_family = "aurora-postgresql15" - tags = var.tags + tags = var.tags } resource "random_string" "orders_db_master" { length = 10 special = false -} \ No newline at end of file +} From 17f2bcf1c83b80cff2cfe3970795cd52a630fb23 Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 18:24:39 +0000 Subject: [PATCH 5/8] Fixed orders helm chart --- .../charts/orders/templates/_helpers.tpl | 14 ++++++------- .../charts/orders/templates/configmap.yml | 2 +- .../orders/templates/postgresql-service.yaml | 4 ++-- .../templates/postgresql-statefulset.yaml | 20 +++++++++---------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/deploy/kubernetes/charts/orders/templates/_helpers.tpl b/deploy/kubernetes/charts/orders/templates/_helpers.tpl index a2287bdb8..4089356f7 100644 --- a/deploy/kubernetes/charts/orders/templates/_helpers.tpl +++ b/deploy/kubernetes/charts/orders/templates/_helpers.tpl @@ -87,11 +87,11 @@ Create the name of the config map to use {{- end -}} {{- define "orders.postgresql.fullname" -}} -{{- include "orders.fullname" . }}-mysql +{{- include "orders.fullname" . }}-postgresql {{- end -}} {{/* -Common labels for mysql +Common labels for postgresql */}} {{- define "orders.postgresql.labels" -}} helm.sh/chart: {{ include "orders.chart" . }} @@ -103,12 +103,12 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* -Selector labels for mysql +Selector labels for postgresql */}} {{- define "orders.postgresql.selectorLabels" -}} app.kubernetes.io/name: {{ include "orders.fullname" . }} app.kubernetes.io/instance: {{ .Release.Name }} -app.kubernetes.io/component: mysql +app.kubernetes.io/component: postgresql {{- end }} {{- define "getOrGeneratePass" }} @@ -132,10 +132,10 @@ app.kubernetes.io/component: mysql {{- end -}} {{- define "orders.postgresql.endpoint" -}} -{{- if not (empty .Values.postgresql.endpoint) -}} - {{- .Values.postgresql.endpoint -}} +{{- if not (empty .Values.postgresql.endpoint.host) -}} +jdbc:postgresql://{{- .Values.postgresql.endpoint.host -}}:{{- .Values.postgresql.port -}}/{{ .Values.postgresql.database }} {{- else -}} -jdbc:mariadb://{{ include "orders.postgresql.fullname" . }}:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.database }} +jdbc:postgresql://{{ include "orders.postgresql.fullname" . }}:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.database }} {{- end -}} {{- end -}} diff --git a/deploy/kubernetes/charts/orders/templates/configmap.yml b/deploy/kubernetes/charts/orders/templates/configmap.yml index 391badf22..ab3f8f0f6 100644 --- a/deploy/kubernetes/charts/orders/templates/configmap.yml +++ b/deploy/kubernetes/charts/orders/templates/configmap.yml @@ -5,6 +5,6 @@ metadata: name: {{ include "orders.configMapName" . }} data: SPRING_PROFILES_ACTIVE: rabbitmq - SPRING_DATASOURCE_URL: jdbc:postgresql://{{ .Values.postgresql.endpoint.host }}:{{ .Values.postgresql.endpoint.port }}/{{ .Values.postgresql.database }} + SPRING_DATASOURCE_URL: {{ include "orders.postgresql.endpoint" . }} SPRING_RABBITMQ_ADDRESSES: {{ include "orders.rabbitmq.addresses" . }} {{- end }} diff --git a/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml b/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml index 699339fb2..fd9c780b1 100644 --- a/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml +++ b/deploy/kubernetes/charts/orders/templates/postgresql-service.yaml @@ -9,9 +9,9 @@ spec: type: {{ .Values.postgresql.service.type }} ports: - port: {{ .Values.postgresql.service.port }} - targetPort: mysql + targetPort: postgresql protocol: TCP - name: mysql + name: postgresql selector: {{- include "orders.postgresql.selectorLabels" . | nindent 4 }} {{- end }} \ No newline at end of file diff --git a/deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml b/deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml index 479a15949..9f93a2f02 100644 --- a/deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml +++ b/deploy/kubernetes/charts/orders/templates/postgresql-statefulset.yaml @@ -25,32 +25,30 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} containers: - - name: mysql + - name: postgresql image: "{{ .Values.postgresql.image.repository }}:{{ .Values.postgresql.image.tag }}" imagePullPolicy: {{ .Values.postgresql.image.pullPolicy }} env: - - name: MYSQL_ROOT_PASSWORD - value: my-secret-pw - - name: MYSQL_DATABASE + - name: POSTGRES_DB value: {{ .Values.postgresql.database }} - - name: MYSQL_USER + - name: POSTGRES_USER valueFrom: secretKeyRef: name: {{ .Values.postgresql.secret.name }} key: username - - name: MYSQL_PASSWORD + - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.postgresql.secret.name }} key: password - args: - - "--ignore-db-dir=lost+found" + - name: PGDATA + value: /data/pgdata volumeMounts: - name: data - mountPath: /var/lib/mysql + mountPath: /data ports: - - name: mysql - containerPort: 3306 + - name: postgresql + containerPort: 5432 protocol: TCP {{- with .Values.postgresql.nodeSelector }} nodeSelector: From 90dc078b0faa5d96bad174400a8d3dccac3d8687 Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 23:10:31 +0000 Subject: [PATCH 6/8] Use correct instance type for postgres --- deploy/terraform/lib/dependencies/orders_rds.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/terraform/lib/dependencies/orders_rds.tf b/deploy/terraform/lib/dependencies/orders_rds.tf index 7f3a4d993..95385a76a 100644 --- a/deploy/terraform/lib/dependencies/orders_rds.tf +++ b/deploy/terraform/lib/dependencies/orders_rds.tf @@ -5,7 +5,7 @@ module "orders_rds" { name = "${var.environment_name}-orders" engine = "aurora-postgresql" engine_version = "15.5" - instance_class = "db.t3.small" + instance_class = "db.t3.medium" instances = { one = {} From 501efdb35d91e8f9854b4bf193c5f783b49a6b77 Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 23:10:52 +0000 Subject: [PATCH 7/8] Correctly build postgres endpoint --- deploy/kubernetes/charts/orders/templates/_helpers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kubernetes/charts/orders/templates/_helpers.tpl b/deploy/kubernetes/charts/orders/templates/_helpers.tpl index 4089356f7..1c5c6c6ae 100644 --- a/deploy/kubernetes/charts/orders/templates/_helpers.tpl +++ b/deploy/kubernetes/charts/orders/templates/_helpers.tpl @@ -133,7 +133,7 @@ app.kubernetes.io/component: postgresql {{- define "orders.postgresql.endpoint" -}} {{- if not (empty .Values.postgresql.endpoint.host) -}} -jdbc:postgresql://{{- .Values.postgresql.endpoint.host -}}:{{- .Values.postgresql.port -}}/{{ .Values.postgresql.database }} +jdbc:postgresql://{{- .Values.postgresql.endpoint.host -}}:{{- .Values.postgresql.endpoint.port -}}/{{ .Values.postgresql.database }} {{- else -}} jdbc:postgresql://{{ include "orders.postgresql.fullname" . }}:{{ .Values.postgresql.service.port }}/{{ .Values.postgresql.database }} {{- end -}} From 20f634ef55076df0f3960ded6f740d047969910d Mon Sep 17 00:00:00 2001 From: Niall Thomson Date: Wed, 3 Jan 2024 23:11:25 +0000 Subject: [PATCH 8/8] Update postgres entry in orders yaml --- deploy/terraform/eks/default/values/orders.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/terraform/eks/default/values/orders.yaml b/deploy/terraform/eks/default/values/orders.yaml index 7882ac714..185c4d93a 100644 --- a/deploy/terraform/eks/default/values/orders.yaml +++ b/deploy/terraform/eks/default/values/orders.yaml @@ -1,4 +1,4 @@ -mysql: +postgresql: create: false database: ${database_name}