Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
spilin committed Dec 17, 2024
0 parents commit 74bbb63
Show file tree
Hide file tree
Showing 19 changed files with 1,309 additions and 0 deletions.
74 changes: 74 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Docker Image

on:
workflow_dispatch:
inputs:
tag:
description: 'Tag'
required: true
release:
types: [created]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
VERSION: ${{ github.event.inputs.tag || github.event.release.tag_name || '' }}

jobs:
build-and-push:
runs-on: k8s-infrastructure-dind

permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Check if VERSION follows the x.x.x format
run: |
if [[ "${{ env.VERSION }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "LATEST_TAG_ENABLED=true" >> $GITHUB_ENV
else
echo "LATEST_TAG_ENABLED=false" >> $GITHUB_ENV
fi
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha
type=semver,pattern={{version}},value=${{ env.VERSION }}
type=raw,value=latest,enable=${{ env.LATEST_TAG_ENABLED == 'true' }}
type=raw,value=testnet
flavor: |
latest=false
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ env.VERSION }}
secrets: |
GIT_AUTH_TOKEN=${{ secrets.GH_PAT }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
config/local.yaml
config/docker-compose.yaml
.DS_Store
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Build stage
FROM golang:1.23-alpine AS builder

# Install git and build dependencies
RUN apk add --no-cache git build-base

WORKDIR /app

# Copy go mod and sum files
COPY go.mod go.sum ./

# Copy the source code
COPY . .

# Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -o app .

# Final stage
FROM alpine:3.17

WORKDIR /app

# Install runtime dependencies
RUN apk add --no-cache ca-certificates curl

# Copy the binary and goose from the builder stage
COPY --from=builder /app/app /app/app

# Ensure the binary is executable
RUN chmod +x /app/app
147 changes: 147 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Blockscout VC Sidecar

## Overview

The **Blockscout VC Sidecar** service monitors database changes in Supabase and automatically updates Docker container configurations and restarts services when necessary. This ensures that your Blockscout services are always running with the latest configuration. Intention is to make sure that changes made in Cloud Console are reflected in the running containers.

## Configuration File

To configure the service, use a YAML file with the following structure:

```yaml
supabaseRealtimeUrl: "wss://your-project.supabase.co/realtime/v1/websocket"
supabaseAnonKey: "your-anon-key"
pathToDockerCompose: "./config/docker-compose.yaml"
frontendServiceName: "frontend"
backendServiceName: "backend"
statsServiceName: "stats"
table: "silos"
chainId: 10
```
## Running the Service with Docker Compose
The service can be deployed using Docker Compose. Below is an example configuration:
```yaml
services:
blockscout-vc-sidecar:
image: ghcr.io/aurora-is-near/blockscout-vc:latest
container_name: blockscout-vc
pull_policy: always
command: ["--config", "/app/config/local.yaml"]
volumes:
- ./config:/app/config
- /var/run/docker.sock:/var/run/docker.sock
- ./docker-compose.yaml:/app/config/docker-compose.yaml:ro
restart: unless-stopped
```
### Important Notes
- Configuration files should be mounted in the `/app/config` directory

### Basic Commands

Start the service:
```bash
docker compose up -d
```

Restart the service:
```bash
docker compose up -d --force-recreate
```

Stop the service:
```bash
docker compose down
```

View logs:
```bash
docker logs -f blockscout-vc-sidecar
```

## Features

- Monitors Supabase database changes in real-time
- Automatically updates Docker Compose environment variables
- Restarts affected services when configuration changes
- Handles multiple service updates efficiently
- Prevents duplicate container restarts
- Validates configuration changes before applying

## Development

### Prerequisites

- Go 1.21 or later
- Docker
- Docker Compose

### Building from Source

1. Clone the repository:
```bash
git clone https://github.com/blockscout/blockscout-vc-sidecar.git
```

2. Build the binary:
```bash
go build -o blockscout-vc-sidecar
```

3. Run with configuration:
```bash
./blockscout-vc-sidecar --config config/local.yaml
```

### Project Structure

```
blockscout-vc/
├── cmd/
│ └── root.go
│ └── sidecar.go
├── internal/
│ ├── client/ # WebSocket client implementation
│ ├── config/ # Configuration handling
│ ├── docker/ # Docker operations
│ ├── handlers/ # Event handlers
│ ├── heartbeat/ # Heartbeat logic
│ └── subscription/ # Supabase subscription logic
│ └── worker/ # Worker implementation
├── config/
│ └── local.yaml # Configuration file
└── main.go
```
## Configuration Options
| Parameter | Description | Required |
|-----------|-------------|----------|
| `supabaseRealtimeUrl` | Supabase Realtime WebSocket URL | Yes |
| `supabaseAnonKey` | Supabase Anonymous Key | Yes |
| `pathToDockerCompose` | Path to the Docker Compose file | Yes |
| `frontendServiceName` | Name of the frontend service | Yes |
| `backendServiceName` | Name of the backend service | Yes |
| `statsServiceName` | Name of the stats service | Yes |
| `table` | Name of the table to listen to | Yes |
| `chainId` | Chain ID to listen to | Yes |
## Debugging
Enable debug logging by setting the environment variable:
```bash
export LOG_LEVEL=debug
```

## Deployment Guide

### Release and Versioning

Releases are managed via GitHub with canonical versioning (e.g., `0.1.2`). Ensure the versioning follows semantic versioning guidelines.

To release a new version:
1. Create a release on GitHub, specifying the appropriate tag (following semantic versioning guidelines).
2. This will trigger the build and push workflows to create a new Docker image and store it in the GitHub registry.
26 changes: 26 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"
)

func RootCmd() *cobra.Command {
rootCmd := &cobra.Command{
Use: "blockscout-vc",
Short: "Blockscout Virtual Chain toolset",
Long: `Blockscout Virtual Chain toolset`,
// Default behavior is to show help
Run: func(cmd *cobra.Command, args []string) {
err := cmd.Help()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
},
}

return rootCmd
}
69 changes: 69 additions & 0 deletions cmd/sidecar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cmd

import (
"blockscout-vc/internal/client"
"blockscout-vc/internal/config"
"blockscout-vc/internal/heartbeat"
"blockscout-vc/internal/subscription"
"blockscout-vc/internal/worker"
"context"
"fmt"
"os"
"os/signal"
"time"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// StartSidecarCmd creates and returns the sidecar command
func StartSidecarCmd() *cobra.Command {
startServer := &cobra.Command{
Use: "sidecar",
Short: "Start sidecar",
Long: `Starts sidecar to listen for changes in the database and recreate the containers`,
// Initialize configuration before running
PreRun: func(cmd *cobra.Command, args []string) {
config.InitConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
// Create a cancellable context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Initialize WebSocket client
supabaseRealtimeUrl := viper.GetString("supabaseRealtimeUrl")
supabaseAnonKey := viper.GetString("supabaseAnonKey")
client := client.New(supabaseRealtimeUrl, supabaseAnonKey)
if err := client.Connect(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer client.Close()

// Initialize and start the worker
worker := worker.New()
worker.Start(ctx)

// Initialize and start heartbeat service
hb := heartbeat.New(client, 30*time.Second)
hb.Start()
defer hb.Stop()

// Initialize and start subscription service
sub := subscription.New(client)
sub.Subscribe(worker)
defer sub.Stop()

// Wait for interrupt signal
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)

<-interrupt
fmt.Println("\nReceived interrupt signal, shutting down.")
return nil
},
}

return startServer
}
9 changes: 9 additions & 0 deletions config/example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
supabaseUrl: "postgresql://postgres:postgres@localhost:5432/postgres"
supabaseRealtimeUrl: "wss://localhost:5432/realtime/v1/websocket"
supabaseAnonKey: "replace-with-actual-anon-key"
pathToDockerCompose: "./config/docker-compose.yaml"
frontendServiceName: "frontend"
backendServiceName: "backend"
statsServiceName: "stats"
table: "silos"
chainId: "replace-with-actual-chain-id"
Loading

0 comments on commit 74bbb63

Please sign in to comment.