Kube Cloud Pt3 | Service Interaction via Kubernetes
full course- Kube Cloud Pt3 | Synchronous Service Interaction
- Kube Cloud Pt3 | REST Interaction
- Kube Cloud Pt3 | Health Indicators
In this course I’m going to show you how to make another spring boot microservice (message-generator
), deploy it with our first service (cloud-application
). I’ll show how cloud-application
service can discover message-generator
via kubernetes services, call an endpoint in message-generato
r with a feign based REST client as well as how to enhance our component tests to cover that new interaction. Let’s get started.
Start a New Spring Boot Microservice
You’ll want to repeat the steps in the first part of this course to setup a new microservice. I’m not going to go over them here, but here’s the outline of what is needed:
- Create a new github repository (message-generator)
- Create a new spring boot application
- Make sure to include
Spring Web
starter Spring Boot DevTools
Lombok
- Make sure to include
- Create the repository at canister.io
- Create the github action workflows
- Feature branch
- Main branch with deploy
- Dependabot
- Add the secrets to build and push the image as well as the helm chart
- CANISTER_USERNAME
- CANISTER_PASSWORD
- CHART_TOKEN
- KUBECONFIG
Once I got to a place where everything built and deployed from the base spring boot image, I tagged it.
Add a Controller
The goal here is to generate a message to provide to the cloud-application if the user doesn’t supply a message. It’s a bit contrived and not ideally the way I would evolve a service endpoint for cloud-application, but it will do for our demo.
We’re going to need a controller endpoint and tests. Lets write that code.
Update pom.xml to add some dependencies:
<properties>
<java.version>11</java.version>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
<javafaker.version>1.0.2</javafaker.version>
</properties>
...
<!-- mapstruct -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>${javafaker.version}</version>
</dependency>
First create a service
package in src/main/java/com/bullyrooks/messagegenerator/
and add a MessageSerivce.java
class with this code:
package com.bullyrooks.messagegenerator.service;
import com.github.javafaker.Faker;
import org.springframework.stereotype.Service;
@Service
public class MessageService {
Faker faker = new Faker();
public String getMessage(){
return faker.gameOfThrones().quote();
}
}
I’m using the faker library to generate some random text, because that’s what its good for.
Now create a controller
package in src/main/java/com/bullyrooks/messagegenerator/
and add a MessageController.java
class with this code:
package com.bullyrooks.messagegenerator.controller;
import com.bullyrooks.messagegenerator.controller.dto.MessageResponseDTO;
import com.bullyrooks.messagegenerator.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@Autowired
MessageService messageService;
@GetMapping("/message")
public MessageResponseDTO getMessage(){
return MessageResponseDTO.builder()
.message(messageService.getMessage())
.build();
}
}
We’ll need a dto as well. Create a dto
package under controller
. Create a class called MessageResponseDTO
with this content
package com.bullyrooks.messagegenerator.controller.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MessageResponseDTO {
private String message;
}
Write a Test
Create a controller
package in src/test/java/com/bullyrooks/messagegenerator/
. Create a class called MessageControllerTest.java
with this code
package com.bullyrooks.messagegenerator.controller;
import com.bullyrooks.messagegenerator.controller.dto.MessageResponseDTO;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.net.URISyntaxException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
@AutoConfigureMockMvc
public class MessageControllerTest {
@LocalServerPort
int randomServerPort;
@Test
void testAddCustomerSuccess() throws URISyntaxException, JsonProcessingException {
RestTemplate restTemplate = new RestTemplate();
String baseUrl = "http://localhost:" + randomServerPort + "/message";
URI uri = new URI(baseUrl);
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<MessageResponseDTO> result = restTemplate.getForEntity(uri, MessageResponseDTO.class);
//Verify request succeed
assertEquals(200, result.getStatusCodeValue());
MessageResponseDTO response = result.getBody();
log.info("Test message returned: {}",response.getMessage());
assertTrue(StringUtils.isNotBlank(response.getMessage()));
}
}
Now run the test, you should see it pass with some output like this:
2022-01-23 14:44:15.493 INFO 24748 --- [o-auto-1-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-01-23 14:44:15.493 INFO 24748 --- [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-01-23 14:44:15.494 INFO 24748 --- [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2022-01-23 14:44:15.657 INFO 24748 --- [ main] c.b.m.controller.MessageControllerTest : Test message returned: Do the dead frighten you?
Kubernetes Config
Although this will deploy and startup, it will conflict with the port that cloud-application is already running on. We can fix that quickly by updating the values.yaml
like this:
service:
type: NodePort
port: 8081
targetPort: 8080
port:
containerPort: 8081
This is very similar to what would happen if we ran both cloud-application and message-generator locally with the default config (listening on port 8080). You can fix that by modifying the application.yml of one of them by adding this config: If you do that, make sure that the server.port
config matches your targetPort
in the values.yaml
server:
port : 8081
Lets go ahead and commit onto main
.
$ git add .
warning: LF will be replaced by CRLF in pom.xml.
The file will have its original line endings in your working directory
$ git commit -m "message generating endpoint"
[main 1057d9c] message generating endpoint
5 files changed, 116 insertions(+)
create mode 100644 src/main/java/com/bullyrooks/messagegenerator/controller/MessageController.java
Compressing objects: 100% (14/14), done.
Writing objects: 100% (22/22), 2.82 KiB | 963.00 KiB/s, done.
Total 22 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com-bullyrook:bullyrooks/message-generator.git
6e91180..1057d9c main -> main
$ git push
And we should see it build and deploy to Okteto
The build should pass

We should see it deploy from the pipeline
Setting up kubectl configuration
Preparing helm execution
Executing helm
"***" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "***" chart repository
Update Complete. ⎈Happy Helming!⎈
Release "message-generator" has been upgraded. Happy Helming!
NAME: message-generator
LAST DEPLOYED: Sun Jan 23 21:48:24 2022
NAMESPACE: ***
STATUS: deployed
REVISION: 3
NOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace *** -o jsonpath="{.spec.ports[0].nodePort}" services message-generator)
export NODE_IP=$(kubectl get nodes --namespace *** -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
And see it start up in Okteto

And confirm we can hit the endpoint via postman

0 comments on “Kube Cloud Pt3 | Synchronous Service Interaction”Add yours →