Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
martin31821 committed Nov 11, 2023
0 parents commit fdd8a4a
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/docker-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Build Docker Images

on:
push:
tags:
- '**'
branches:
- 'main'
pull_request:
types:
- "opened"
- "reopened"
- "synchronize"
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
strategy:
matrix:
image: ["signal-http-bridge"]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v2

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

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}
tags: |
type=semver,pattern={{version}}
type=edge
type=ref,event=pr
- name: Build and push Docker Images
uses: docker/build-push-action@v3
with:
context: .
file: Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
build-args: "TARGET=${{ matrix.image }}"
35 changes: 35 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# syntax=docker/dockerfile:1

##
## Build stage.
##
FROM golang:1.21.3-alpine AS build
ENV GO111MODULE=on

WORKDIR /app

# Download dependencies
COPY go.mod ./
COPY go.sum ./
RUN go mod download

# Copy app
COPY . .

# build the actual binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o entry

##
## Deploy stage
##
FROM alpine:3.18

# install common deps
RUN apk add curl wget bash

# copy the prebuilt file
WORKDIR /
COPY --from=build /app/entry /usr/bin/entry

# set app as startup app
ENTRYPOINT ["/usr/bin/entry"]
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"signals": {
"USR1": "echo 'hallo'"
}
}
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/deinstapel/signal-cmd-executor

go 1.21.3

require (
github.com/abiosoft/lineprefix v0.1.4 // indirect
github.com/fatih/color v1.12.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
)
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
github.com/abiosoft/lineprefix v0.1.4 h1:fXu3jc+B2EaS98mTpEL5OH9EKv3scHRb7/gsvlqAD1A=
github.com/abiosoft/lineprefix v0.1.4/go.mod h1:Myq9hfXs8e2OmHFvajp3pHxxThZL645XK+BrEQNvNSs=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
96 changes: 96 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"syscall"

"github.com/abiosoft/lineprefix"
)

type Config struct {
Signals map[string]string `json:"signals"`
}

var signalMap = map[string]syscall.Signal{
"USR1": syscall.SIGUSR1,
"USR2": syscall.SIGUSR2,
"INT": syscall.SIGINT,
"ABRT": syscall.SIGABRT,
"ALRM": syscall.SIGALRM,
"BUS": syscall.SIGBUS,
"CHLD": syscall.SIGCHLD,
"CLD": syscall.SIGCLD,
"CONT": syscall.SIGCONT,
"FPE": syscall.SIGFPE,
"HUP": syscall.SIGHUP,
"IO": syscall.SIGIO,
"IOT": syscall.SIGIOT,
}

// handles a single signal
func handleSignal(ctx context.Context, signalName string, command string) {
c := make(chan os.Signal, 3)
signalNumber, ok := signalMap[signalName]
if !ok {
log.Fatalf("failed to find syscall for signal %v\n", signalName)
}

signal.Notify(c, signalNumber)

prefix := lineprefix.Prefix(fmt.Sprintf("[CMD SIG%v]", signalName))
stdoutWrapper := lineprefix.New(prefix, lineprefix.Writer(os.Stdout))
stderrWrapper := lineprefix.New(prefix, lineprefix.Writer(os.Stderr))

outer:
for {
select {
case <-c:
cmd := exec.Command("/bin/bash", "-c", command)
cmd.Stderr = stderrWrapper
cmd.Stdout = stdoutWrapper
if err := cmd.Run(); err != nil {
log.Printf("WARNING: program terminated with error: %v\n", err)
}
case <-ctx.Done():
break outer
}
}

signal.Stop(c)
close(c)
}

// main entry point for the program, loads config file etc.
func main() {
configFile, ok := os.LookupEnv("CONFIG_FILE")
if !ok {
configFile = "/etc/config.json"
}

configBytes, err := os.ReadFile(configFile)
if err != nil {
log.Fatalf("failed to read config file: %v\n", err)
}

config := Config{}
if err := json.Unmarshal(configBytes, &config); err != nil {
log.Fatalf("failed to deserialize config file: %v\n", err)
}

ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM)
defer cancel()

for signal, command := range config.Signals {
go handleSignal(ctx, signal, command)
}

<-ctx.Done()

log.Printf("exiting")
}

0 comments on commit fdd8a4a

Please sign in to comment.