Skip to content

Commit

Permalink
[Release] Liquibase Migration Tooling Added
Browse files Browse the repository at this point in the history
* Liquibase migration tooling and scripts added.
* General code base maintenance.
* Redis cluster Docker configuration simplified.
* Redis cluster nodes canonical node names added.
  • Loading branch information
surahman authored Feb 21, 2023
2 parents 3749619 + 510b26b commit 502778f
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 42 deletions.
28 changes: 11 additions & 17 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ services:

redis-node-0:
container_name: redis-node-0
image: docker.io/bitnami/redis-cluster:latest
image: docker.io/bitnami/redis-cluster:7.0
restart: always
ports:
- '6379:6379'
Expand All @@ -33,7 +33,6 @@ services:
environment:
- 'REDIS_PASSWORD=root'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
command: ["redis-server", "--bind", "redis", "--port", "6379"]
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 15s
Expand All @@ -42,16 +41,15 @@ services:

redis-node-1:
container_name: redis-node-1
image: docker.io/bitnami/redis-cluster:latest
image: docker.io/bitnami/redis-cluster:7.0
restart: always
ports:
- '6380:6380'
- '6380:6379'
volumes:
- redis-cluster_data-1:/bitnami/redis/data
environment:
- 'REDIS_PASSWORD=root'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
command: ["redis-server", "--bind", "redis", "--port", "6380"]
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 15s
Expand All @@ -60,16 +58,15 @@ services:

redis-node-2:
container_name: redis-node-2
image: docker.io/bitnami/redis-cluster:latest
image: docker.io/bitnami/redis-cluster:7.0
restart: always
ports:
- '6381:6381'
- '6381:6379'
volumes:
- redis-cluster_data-2:/bitnami/redis/data
environment:
- 'REDIS_PASSWORD=root'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
command: ["redis-server", "--bind", "redis", "--port", "6381"]
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 15s
Expand All @@ -78,16 +75,15 @@ services:

redis-node-3:
container_name: redis-node-3
image: docker.io/bitnami/redis-cluster:latest
image: docker.io/bitnami/redis-cluster:7.0
restart: always
ports:
- '6382:6382'
- '6382:6379'
volumes:
- redis-cluster_data-3:/bitnami/redis/data
environment:
- 'REDIS_PASSWORD=root'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
command: ["redis-server", "--bind", "redis", "--port", "6382"]
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 15s
Expand All @@ -96,16 +92,15 @@ services:

redis-node-4:
container_name: redis-node-4
image: docker.io/bitnami/redis-cluster:latest
image: docker.io/bitnami/redis-cluster:7.0
restart: always
ports:
- '6383:6383'
- '6383:6379'
volumes:
- redis-cluster_data-4:/bitnami/redis/data
environment:
- 'REDIS_PASSWORD=root'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
command: ["redis-server", "--bind", "redis", "--port", "6383"]
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 15s
Expand All @@ -114,10 +109,10 @@ services:

redis-node-5:
container_name: redis-node-5
image: docker.io/bitnami/redis-cluster:latest
image: docker.io/bitnami/redis-cluster:7.0
restart: always
ports:
- '6384:6384'
- '6384:6379'
volumes:
- redis-cluster_data-5:/bitnami/redis/data
depends_on:
Expand All @@ -132,7 +127,6 @@ services:
- 'REDIS_CLUSTER_REPLICAS=1'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
- 'REDIS_CLUSTER_CREATOR=yes'
command: ["redis-server", "--bind", "redis", "--port", "6384"]
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 15s
Expand Down
2 changes: 1 addition & 1 deletion docs/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"name": "GPL-3.0",
"url": "https://opensource.org/licenses/GPL-3.0"
},
"version": "1.6.2"
"version": "1.7.0"
},
"host": "localhost:44243",
"basePath": "/api/rest/v1",
Expand Down
2 changes: 1 addition & 1 deletion docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ info:
name: GPL-3.0
url: https://opensource.org/licenses/GPL-3.0
title: Multiple Choice Question Platform.
version: 1.6.2
version: 1.7.0
paths:
/health:
get:
Expand Down
3 changes: 1 addition & 2 deletions pkg/cassandra/cassandra_queries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cassandra

import (
"fmt"
"math"
"net/http"
"reflect"
"testing"
Expand Down Expand Up @@ -554,7 +553,7 @@ func TestReadResponseStatisticsPageQuery(t *testing.T) {
}

// Iterate over all pages and tally up the total records retrieved.
expectedPageCount := int(math.Ceil(float64(testCase.expectedRecordCount / testCase.request.PageSize)))
expectedPageCount := testCase.expectedRecordCount / testCase.request.PageSize
pageCount := 0
recordCount := 0
request := &model_cassandra.StatsRequest{
Expand Down
2 changes: 1 addition & 1 deletion pkg/http/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (s *Server) Run() {
}()

// Wait for interrupt signal to gracefully shut down the server.
quit := make(chan os.Signal)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

// Wait for interrupt.
Expand Down
4 changes: 3 additions & 1 deletion pkg/http/graph/resolvers/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"github.com/surahman/mcq-platform/pkg/logger"
)

type GinContextKey struct{}

// GinContextFromContext will extract the Gin context from the context passed in.
func GinContextFromContext(ctx context.Context, logger *logger.Logger) (*gin.Context, error) {
ctxValue := ctx.Value("GinContextKey")
ctxValue := ctx.Value(GinContextKey{})
if ctxValue == nil {
logger.Error("could not retrieve gin.Context")
return nil, errors.New("malformed request: authorization information not found")
Expand Down
12 changes: 6 additions & 6 deletions pkg/http/graph/resolvers/authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ func TestGinContextFromContext(t *testing.T) {
name: "incorrect context",
expectedMsg: "information malformed",
expectErr: require.Error,
ctx: context.WithValue(context.TODO(), "GinContextKey", context.TODO()),
ctx: context.WithValue(context.TODO(), GinContextKey{}, context.TODO()),
}, {
name: "success",
expectErr: require.NoError,
ctx: context.WithValue(context.TODO(), "GinContextKey", &gin.Context{}),
ctx: context.WithValue(context.TODO(), GinContextKey{}, &gin.Context{}),
},
// ----- test cases end ----- //
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func TestAuthorizationCheck(t *testing.T) {
name: "incorrect context",
expectedMsg: "information malformed",
expectErr: require.Error,
ctx: context.WithValue(context.TODO(), "GinContextKey", context.TODO()),
ctx: context.WithValue(context.TODO(), GinContextKey{}, context.TODO()),
authValidateJWTData: &http_common.MockAuthData{
OutputParam1: "",
OutputParam2: int64(-1),
Expand All @@ -93,7 +93,7 @@ func TestAuthorizationCheck(t *testing.T) {
name: "no token",
expectedMsg: "does not contain",
expectErr: require.Error,
ctx: context.WithValue(context.TODO(), "GinContextKey", ginCtxNoAuth),
ctx: context.WithValue(context.TODO(), GinContextKey{}, ginCtxNoAuth),
authValidateJWTData: &http_common.MockAuthData{
OutputParam1: "",
OutputParam2: int64(-1),
Expand All @@ -104,7 +104,7 @@ func TestAuthorizationCheck(t *testing.T) {
name: "no token",
expectedMsg: "failed to authenticate token",
expectErr: require.Error,
ctx: context.WithValue(context.TODO(), "GinContextKey", ginCtxAuth),
ctx: context.WithValue(context.TODO(), GinContextKey{}, ginCtxAuth),
authValidateJWTData: &http_common.MockAuthData{
OutputParam1: "",
OutputParam2: int64(-1),
Expand All @@ -114,7 +114,7 @@ func TestAuthorizationCheck(t *testing.T) {
}, {
name: "success",
expectErr: require.NoError,
ctx: context.WithValue(context.TODO(), "GinContextKey", ginCtxAuth),
ctx: context.WithValue(context.TODO(), GinContextKey{}, ginCtxAuth),
authValidateJWTData: &http_common.MockAuthData{
OutputParam1: "successful token refresh",
OutputParam2: int64(999),
Expand Down
2 changes: 1 addition & 1 deletion pkg/http/graph/resolvers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func PlaygroundHandler(baseURL, queryURL string) gin.HandlerFunc {
// GinContextToContextMiddleware is middleware that will place the Gin context into a context for the GraphQL resolvers.
func GinContextToContextMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
ctx := context.WithValue(c.Request.Context(), GinContextKey{}, c)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/http/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (s *Server) Run() {
}()

// Wait for interrupt signal to gracefully shut down the server.
quit := make(chan os.Signal)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

// Wait for interrupt.
Expand All @@ -105,7 +105,7 @@ func (s *Server) initialize() {
s.router = gin.Default()

// @title Multiple Choice Question Platform.
// @version 1.6.2
// @version 1.7.0
// @description Multiple Choice Question Platform API.
// @description This application supports the creation, managing, marking, viewing, retrieving stats, and scores of quizzes.
//
Expand Down
4 changes: 0 additions & 4 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ func (l *Logger) Init(fs *afero.Fs) (err error) {
switch strings.ToLower(userConfig.BuiltinConfig) {
case "development":
baseConfig = zap.NewDevelopmentConfig()
break
case "production":
baseConfig = zap.NewProductionConfig()
break
default:
msg := "could not select the base configuration type"
log.Println(msg)
Expand All @@ -53,10 +51,8 @@ func (l *Logger) Init(fs *afero.Fs) (err error) {
switch strings.ToLower(userConfig.BuiltinEncoderConfig) {
case "development":
encConfig = zap.NewDevelopmentEncoderConfig()
break
case "production":
encConfig = zap.NewProductionEncoderConfig()
break
default:
msg := "could not select the base encoder configuration type"
log.Println(msg)
Expand Down
49 changes: 49 additions & 0 deletions pkg/model/cassandra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Responses](#responses)
- [CQL Query](#cql-query)
- [Schema Migration and Setup](#schema-migration-and-setup)
- [Liquibase Migration](#liquibase-migration)

<br/>

Expand Down Expand Up @@ -160,3 +161,51 @@ to be deployed in the following order:
1. [user.cql](user.cql)
2. [quiz.cql](quiz.cql)
3. [responses.cql](responses.cql)

This can be achieved manually or through tooling such as [Liquibase](https://www.liquibase.com/) which is the industry
standard for database migrations.

### Liquibase Migration
For convenience, a Liquibase database schema migration change set has been provided with stages for rollbacks.

Please ensure you have the following installed in the `lib` directory of your Liquibase installation:
* [Liquibase Cassandra Plugin](https://github.com/liquibase/liquibase-cassandra)
* [Datastax JDBC Driver for Apache Cassandra](https://downloads.datastax.com/#odbc-jdbc-drivers)

The [properties file](liquibase.properties) with the login credentials for the Cassandra cluster will need to be
updated appropriately.

```text
changeLogFile: cassandra_migration.sql
url: jdbc:cassandra://localhost:9042/mcq_platform;DefaultKeyspace=mcq_platform;AuthMech=1
username: admin
password: root
driver: com.simba.cassandra.jdbc42.Driver
defaultSchemaName: mcq_platform
```

The following key space will need to be manually created in your Cassandra cluster before the migration can be executed:

```cql
CREATE KEYSPACE IF NOT EXISTS mcq_platform WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 3};
```

If you are running a single-node cluster for testing you will need to set the `replication_factor` to `1`.

At this point a test should be run to ensure Liquibase is able to connect to the database:

```bash
liquibase status
```

If a connection could be established, you should be ready to execute a migration:

```bash
liquibase update
```

Verification of the changes can be achieved through:

```bash
liquibase history
```
Loading

0 comments on commit 502778f

Please sign in to comment.