Skip to content

Commit b5e9272

Browse files
committed
Initial commit.
0 parents  commit b5e9272

11 files changed

Lines changed: 966 additions & 0 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Create release
2+
on:
3+
pull_request_target:
4+
branches:
5+
- main
6+
types:
7+
- closed
8+
jobs:
9+
release:
10+
if: github.event.pull_request.merged == true && !contains(github.event.pull_request.title, 'skip-release')
11+
uses: libops/actions/.github/workflows/bump-release.yaml@main
12+
with:
13+
workflow_file: goreleaser.yml
14+
permissions:
15+
contents: write
16+
actions: write
17+
secrets: inherit

.github/workflows/goreleaser.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: goreleaser
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "*"
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
goreleaser:
14+
runs-on: ubuntu-24.04
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Set up Go
22+
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
23+
with:
24+
go-version: ">=1.25.0"
25+
26+
- name: Run GoReleaser
27+
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6
28+
with:
29+
distribution: goreleaser
30+
version: latest
31+
args: release --clean
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: lint-test-build-push
2+
on:
3+
push:
4+
paths-ignore:
5+
- "**/*.md"
6+
- "renovate.json5"
7+
branches:
8+
- "**"
9+
tags:
10+
- "*"
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
permissions:
17+
contents: read
18+
jobs:
19+
lint-test:
20+
runs-on: ubuntu-24.04
21+
steps:
22+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
23+
24+
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
25+
with:
26+
go-version: ">=1.25.0"
27+
28+
- name: golangci-lint
29+
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8
30+
with:
31+
version: latest
32+
33+
- name: Test with the Go CLI
34+
run: go test -v -race ./...
35+
36+
build-push:
37+
needs: [lint-test]
38+
uses: libops/actions/.github/workflows/build-push.yml@main
39+
permissions:
40+
contents: read
41+
packages: write
42+
id-token: write
43+
secrets: inherit

.golangci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
version: "2"
2+
linters:
3+
exclusions:
4+
generated: lax
5+
presets:
6+
- comments
7+
- common-false-positives
8+
- legacy
9+
- std-error-handling
10+
paths:
11+
- ../../go
12+
- ../../../../opt
13+
- ./vendor
14+
- third_party$
15+
- builtin$
16+
- examples$
17+
formatters:
18+
enable:
19+
- gofmt
20+
exclusions:
21+
generated: lax
22+
paths:
23+
- ../../go
24+
- ../../../../opt
25+
- ./vendor
26+
- third_party$
27+
- builtin$
28+
- examples$

.goreleaser.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
before:
2+
hooks:
3+
- go mod tidy
4+
builds:
5+
- binary: lightsout
6+
env:
7+
- CGO_ENABLED=0
8+
goos:
9+
- linux
10+
- windows
11+
- darwin
12+
13+
archives:
14+
- format: tar.gz
15+
# this name template makes the OS and Arch compatible with the results of uname.
16+
name_template: >-
17+
{{ .ProjectName }}_
18+
{{- title .Os }}_
19+
{{- if eq .Arch "amd64" }}x86_64
20+
{{- else if eq .Arch "386" }}i386
21+
{{- else }}{{ .Arch }}{{ end }}
22+
{{- if .Arm }}v{{ .Arm }}{{ end }}
23+
# use zip for windows archives
24+
format_overrides:
25+
- goos: windows
26+
format: zip
27+
checksum:
28+
name_template: "checksums.txt"
29+
snapshot:
30+
name_template: "{{ incpatch .Version }}-next"
31+
changelog:
32+
sort: asc
33+
filters:
34+
exclude:
35+
- "^docs:"
36+
- "^test:"

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM golang:1.25-alpine3.22 AS builder
2+
3+
WORKDIR /app
4+
COPY go.* ./
5+
RUN go mod download
6+
7+
COPY . .
8+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o lightsout .
9+
10+
FROM alpine:3.22
11+
RUN apk --no-cache add ca-certificates curl docker-cli
12+
WORKDIR /root/
13+
14+
COPY --from=builder /app/lightsout .
15+
16+
# Set default environment variables
17+
ENV PORT=8808
18+
ENV INACTIVITY_TIMEOUT=90
19+
ENV LOG_LEVEL=INFO
20+
ENV GOOGLE_PROJECT_ID=""
21+
ENV GCE_ZONE=""
22+
ENV GCE_INSTANCE=""
23+
ENV LIBOPS_KEEP_ONLINE=""
24+
25+
EXPOSE 8808
26+
27+
CMD ["/app/lightsout"]

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Lights Out
2+
3+
A lightweight Go service that combines ping health checks with power management for GCE instances. Acts as both a health endpoint and an activity monitor that can automatically shut down instances after periods of inactivity.
4+
5+
## Features
6+
7+
- **HTTP Health Endpoints**: `/ping` (monitored) and `/health` (healthcheck)
8+
- **Activity Monitoring**: Tracks requests to `/ping` endpoint
9+
- **Configurable Timeouts**: Environment-controlled inactivity periods
10+
- **GCP Integration**: Automatic instance suspension via GCP API service
11+
12+
## Usage
13+
14+
### As Docker Container
15+
16+
```bash
17+
docker run -p 8808:8808 -e INACTIVITY_TIMEOUT=90 lightswitch:latest
18+
```
19+
20+
### Environment Variables
21+
22+
| Variable | Default | Description |
23+
| -------------------- | ------- | ---------------------------------------- |
24+
| `PORT` | `8808` | HTTP server port |
25+
| `INACTIVITY_TIMEOUT` | `90` | Seconds of inactivity before shutdown |
26+
| `CHECK_INTERVAL` | `30` | Seconds between activity checks |
27+
| `LIBOPS_PROXY_URL` | - | GCP proxy URL for suspension |
28+
| `LIBOPS_KEEP_ONLINE` | - | Set to "yes" to disable auto-shutdown |
29+
| `LOG_LEVEL` | `INFO` | Logging level (DEBUG, INFO, WARN, ERROR) |
30+
31+
### Endpoints
32+
33+
- `GET /ping` - Returns "pong", activity is logged and monitored
34+
- `GET /health` - Returns "healthy", used for container healthchecks
35+
36+
## Integration
37+
38+
This service is designed to work in tandem with [ppb (Proxy Power Button)](https://github.com/libops/ppb) to create a complete on-demand infrastructure solution:
39+
40+
### How ppb and lightsout work together:
41+
42+
1. **ppb (Boot)**: Acts as an ingress proxy that automatically starts GCE instances when requests arrive
43+
- Receives incoming requests for dormant instances
44+
- Starts the GCE instance via GCP API
45+
- Proxies requests to the now-running backend
46+
47+
2. **lightsout (Shutdown)**: Monitors activity and automatically shuts down instances during idle periods
48+
- Tracks activity via `/ping` endpoint calls
49+
- Monitors for configurable inactivity timeouts
50+
- Automatically suspends the GCE instance to save costs
51+
52+
### Deployment Architecture:
53+
54+
```
55+
Internet → ppb (ingress) → GCE Instance (running lightsout + your app)
56+
```
57+
58+
- Deploy ppb as your public-facing service
59+
- Deploy lightsout alongside your application on GCE instances
60+
- Configure your application to periodically call `/ping` to signal activity
61+
- ppb handles instance startup, lightsout handles instance shutdown
62+
63+
### Required IAM Permissions
64+
65+
The Google Service Account (GSA) used by this service requires the following IAM permissions:
66+
67+
- `compute.instances.suspend` - To suspend/stop the GCE instance
68+
- `compute.instances.get` - To check the current status of the instance
69+
70+
These can be granted via the predefined `Compute Instance Admin (v1)` role, or by creating a custom role with only the specific permissions needed:
71+
72+
```bash
73+
# Create custom role with minimal permissions
74+
gcloud iam roles create lightsout.instanceManager \
75+
--project=YOUR_PROJECT_ID \
76+
--title="Lightsout Instance Manager" \
77+
--description="Minimal permissions for lightsout service" \
78+
--permissions=compute.instances.suspend,compute.instances.get
79+
80+
# Assign to service account
81+
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
82+
--member="serviceAccount:YOUR_SERVICE_ACCOUNT@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
83+
--role="projects/YOUR_PROJECT_ID/roles/lightsout.instanceManager"
84+
```

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/libops/lightsout
2+
3+
go 1.25.0

0 commit comments

Comments
 (0)