Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 19 additions & 10 deletions google-cloud-sql/docs/cloud-sql-auth-proxy.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
# Cloud SQL Auth Proxy

### Set instance connection name
### Set environment variables

```bash
export CREDENTIALS_PATH="/Users/admin/.config/gcloud/sa-private-key.json"
export GOOGLE_APPLICATION_CREDENTIALS="$CREDENTIALS_PATH"
export TF_VAR_project_id=$(gcloud config get-value project)
export TF_VAR_region="us-central1"
export TF_VAR_authorized_cidr="$(curl -4 -s ifconfig.me)/32"
export TF_VAR_db_user=username
export TF_VAR_db_password=password
export TF_VAR_db_name=jm_demo_db
export INSTANCE_CONNECTION_NAME="lofty-root-378503:us-central1:jm-pg-demo"
export PGPASSWORD="$TF_VAR_db_password"
```

### Provision DB infrastructure

Use Terraform or `gcloud` to create the Cloud SQL Postgres instance.

### Run the CloudSQL auth proxy

Remove any old proxy container:
Expand All @@ -28,8 +41,6 @@ PROXY_PID=$!
#### Docker

```bash

export CREDENTIALS_PATH="/Users/admin/.config/gcloud/sa-private-key.json"
docker run -d --name csql-proxy -p 127.0.0.1:5400:5432 \
-v "$GOOGLE_APPLICATION_CREDENTIALS:/creds/key.json:ro" \
gcr.io/cloud-sql-connectors/cloud-sql-proxy:2 \
Expand Down Expand Up @@ -89,12 +100,6 @@ for p in {5400..5450}; do (lsof -iTCP:$p -sTCP:LISTEN >/dev/null) || { echo "Fir

### Test connection

```bash
export TF_VAR_db_user=username
export TF_VAR_db_password=password
export TF_VAR_db_name=jm_demo_db
```

Have the proxy running and healthy to `127.0.0.1:5432`.
Make sure nothing else is using port `5432`.
If it is, pick another port (e.g., 5433) and update your `psql` command.
Expand All @@ -110,7 +115,7 @@ PGPASSWORD="$TF_VAR_db_password" psql \

### If the test is successful

Assuming the file `/src/test/resources/init.sql` exists:
Create the table from file contents. For example, assuming `/src/test/resources/init.sql` exists:

```bash
PGPASSWORD="$TF_VAR_db_password" psql \
Expand Down Expand Up @@ -205,6 +210,10 @@ DROP TABLE widgets;

### Exit `psql`

```psql
\q
```

```psql
exit
```
Expand Down
31 changes: 31 additions & 0 deletions google-cloud-sql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
Expand All @@ -78,6 +86,18 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

<!-- Hibernate -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand All @@ -91,6 +111,11 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand All @@ -110,6 +135,12 @@

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>spring-cloud-gcp-dependencies</artifactId>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.squidmin.java.spring.maven.cloudsql.controller;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.squidmin.java.spring.maven.cloudsql.service.WidgetService;
import org.squidmin.java.spring.maven.cloudsql.domain.Widget;

import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/cloud-sql/api/widgets")
public class WidgetController {

private final WidgetService widgetService;

public WidgetController(WidgetService widgetService) {
this.widgetService = widgetService;
}

@GetMapping
public List<Widget> list(@RequestParam(name = "q", required = false) String q) {
return widgetService.searchByName(q);
}

/**
* Get a widget by ID
* @param id the widget ID
* @return the widget if found, or 404 if not found
*/
@GetMapping("{id}")
public ResponseEntity<Widget> get(@PathVariable UUID id) {
return widgetService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@PostMapping(
value = "/insert",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Widget> insert(@RequestBody Widget widget) {
// Generate ID server-side if not provided
if (widget.getId() == null) {
widget.setId(UUID.randomUUID());
}

Widget saved = widgetService.save(widget);
return ResponseEntity.ok(saved);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.squidmin.java.spring.maven.cloudsql.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

import java.time.Instant;
import java.util.Map;
import java.util.UUID;

@Entity
@Table(name = "widgets")
public class Widget {

@Id
@Column(name = "id", nullable = false, columnDefinition = "uuid")
private UUID id;

@Column(name = "name", nullable = false)
private String name;

// DB sets default now(); mark read-only so we don’t overwrite it
@Column(name = "created_at", columnDefinition = "timestamptz", updatable = false, insertable = false)
private Instant createdAt;

@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "meta", columnDefinition = "jsonb")
private Map<String, Object> meta;

public Widget() {}

public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public Instant getCreatedAt() { return createdAt; }

public Map<String, Object> getMeta() { return meta; }
public void setMeta(Map<String, Object> meta) { this.meta = meta; }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.squidmin.java.spring.maven.cloudsql.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.squidmin.java.spring.maven.cloudsql.domain.Widget;

import java.util.List;
import java.util.UUID;

public interface WidgetRepository extends JpaRepository<Widget, UUID> {
List<Widget> findByNameContainingIgnoreCase(String q);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.squidmin.java.spring.maven.cloudsql.service;

import org.springframework.stereotype.Service;
import org.squidmin.java.spring.maven.cloudsql.domain.Widget;
import org.squidmin.java.spring.maven.cloudsql.repository.WidgetRepository;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
public class WidgetService {

private final WidgetRepository repo;

public WidgetService(WidgetRepository repo) {
this.repo = repo;
}

public List<Widget> findAll() {
return repo.findAll();
}

public Optional<Widget> findById(UUID id) {
return repo.findById(id);
}

public List<Widget> searchByName(String q) {
return (q == null || q.isBlank()) ? repo.findAll() : repo.findByNameContainingIgnoreCase(q);
}

public Widget save(Widget widget) {
return repo.save(widget);
}

}
15 changes: 15 additions & 0 deletions google-cloud-sql/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
spring:
application:
name: google-cloud-sql
datasource:
url: jdbc:postgresql://127.0.0.1:5400/jm_demo_db
username: username
password: ${DB_PASSWORD:password}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 2

jpa:
open-in-view: false
hibernate:
ddl-auto: none
properties:
hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.squidmin.java.spring.maven.cloudsql.actuator;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"management.endpoints.web.exposure.include=health,info",
"management.endpoint.health.show-details=always",
"spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1",
"spring.datasource.driver-class-name=org.h2.Driver",
"spring.jpa.hibernate.ddl-auto=none"
}
)
public class ActuatorTest {

@LocalServerPort
int port;

@Autowired
TestRestTemplate rest;

@Test
void health_isUp() {
ResponseEntity<String> res =
rest.getForEntity("http://localhost:" + port + "/actuator/health", String.class);
assertThat(res.getStatusCode().is2xxSuccessful()).isTrue();
assertThat(res.getBody()).contains("\"status\":\"UP\"");
}

@Test
void info_isReachable() {
ResponseEntity<String> res =
rest.getForEntity("http://localhost:" + port + "/actuator/info", String.class);
assertThat(res.getStatusCode().is2xxSuccessful()).isTrue();
}

}
15 changes: 15 additions & 0 deletions google-cloud-sql/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
spring:
application:
name: google-cloud-sql
datasource:
url: jdbc:postgresql://127.0.0.1:5400/jm_demo_db
username: username
password: ${DB_PASSWORD:password}
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 2

jpa:
open-in-view: false
hibernate:
ddl-auto: none
properties:
hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
11 changes: 6 additions & 5 deletions google-cloud-sql/src/test/resources/init.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
CREATE TABLE IF NOT EXISTS widgets (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
meta JSONB
CREATE TABLE IF NOT EXISTS widgets
(
id UUID PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
meta JSONB
);
Loading
Loading