Skip to content
Open
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ STACKS_ENABLED=true
STACKS_MAX_SCOPE=team
STACKS_MAX_PER=3
STACKS_PROVISIONER_BASE_URL=http://localhost:8081
STACKS_PROVISIONER_USE_GRPC=false
STACKS_PROVISIONER_GRPC_ADDR=localhost:9090
STACKS_PROVISIONER_API_KEY=change-me
STACKS_PROVISIONER_TIMEOUT=5s
STACKS_CREATE_WINDOW=1m
Expand Down
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
SHELL := /bin/bash

GO ?= go
BUF ?= buf
BUF_VERSION ?= v1.66.1
BUF_MODULE ?= buf.build/smctf/container-provisioner

.PHONY: all fmt vet lint buf-install buf-lint buf-generate test build

all: buf-lint buf-generate test build

fmt:
$(GO) fmt ./...

vet:
$(GO) vet ./...

lint: buf-lint vet

buf-install:
$(GO) install github.com/bufbuild/buf/cmd/buf@$(BUF_VERSION)

buf-lint:
$(BUF) lint $(BUF_MODULE)

buf-generate:
$(BUF) generate $(BUF_MODULE) --template buf.gen.yaml

test:
$(GO) test ./...

build:
$(GO) build ./cmd/server
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ STACKS_ENABLED=true
STACKS_MAX_SCOPE=team
STACKS_MAX_PER=3
STACKS_PROVISIONER_BASE_URL=http://localhost:8081
STACKS_PROVISIONER_USE_GRPC=false
STACKS_PROVISIONER_GRPC_ADDR=localhost:9090
STACKS_PROVISIONER_API_KEY=change-me
STACKS_PROVISIONER_TIMEOUT=5s
STACKS_CREATE_WINDOW=1m
Expand Down Expand Up @@ -203,6 +205,29 @@ S3_PRESIGN_TTL=15m

</details>

## Buf / BSR (container-provisioner proto)

This repo consumes the container-provisioner proto via Buf Schema Registry (BSR).

Setup:

```bash
make buf-install
buf registry login
```

Generate code:

```bash
make buf-generate
```

Module reference is set via `BUF_MODULE` (Makefile). You can override via:

```bash
make buf-generate BUF_MODULE=buf.build/<org>/container-provisioner
```

> [!IMPORTANT]
>
> Make sure to change `JWT_SECRET` to a secure random string in production!
Expand Down
8 changes: 8 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: v1
plugins:
- plugin: buf.build/protocolbuffers/go
out: internal/gen
opt: paths=source_relative
- plugin: buf.build/grpc/go
out: internal/gen
opt: paths=source_relative
2 changes: 2 additions & 0 deletions buf.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Generated by buf. DO NOT EDIT.
version: v2
24 changes: 23 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,29 @@ func main() {
teamSvc := service.NewTeamService(teamRepo, divisionRepo)
ctfSvc := service.NewCTFService(cfg, challengeRepo, submissionRepo, redisClient, fileStore)
appConfigSvc := service.NewAppConfigService(appConfigRepo, redisClient, cfg.Cache.AppConfigTTL)
stackClient := stack.NewClient(cfg.Stack.ProvisionerBaseURL, cfg.Stack.ProvisionerAPIKey, cfg.Stack.ProvisionerTimeout)

var stackClient stack.API
var stackClientCloser func() error
if cfg.Stack.ProvisionerUseGRPC {
client, err := stack.NewGRPCClient(cfg.Stack.ProvisionerGRPCAddr, cfg.Stack.ProvisionerAPIKey, cfg.Stack.ProvisionerTimeout)
if err != nil {
logger.Error("grpc stack client init error", slog.Any("error", err))
os.Exit(1)
}

stackClient = client
stackClientCloser = client.Close
} else {
stackClient = stack.NewClient(cfg.Stack.ProvisionerBaseURL, cfg.Stack.ProvisionerAPIKey, cfg.Stack.ProvisionerTimeout)
}
if stackClientCloser != nil {
defer func() {
if err := stackClientCloser(); err != nil {
logger.Warn("stack client close error", slog.Any("error", err))
}
}()
}

stackSvc := service.NewStackService(cfg.Stack, stackRepo, challengeRepo, submissionRepo, stackClient, redisClient)

bootstrap.BootstrapAdmin(ctx, cfg, database, userRepo, teamRepo, divisionRepo, logger)
Expand Down
3 changes: 2 additions & 1 deletion codecov.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ ignore:
- "migrations/**"
- "**/*.sql"
- "internal/storage/s3.go" # S3 storage is production only code. instead, we test mock storage.
- "internal/stack/client.go" # Container Provisioner client is production only code. instead, we test mock client.
- "internal/stack/client.go" # Container Provisioner HTTP client is production only code. instead, we test mock client.
- "internal/stack/grpc_client.go" # Container Provisioner gRPC client is production only code. instead, we test mock client.
- "internal/http/handlers/types.go" # only type definitions and constructors.
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ require (
github.com/uptrace/bun/driver/pgdriver v1.2.16
github.com/uptrace/bun/extra/bundebug v1.2.16
golang.org/x/crypto v0.47.0
google.golang.org/grpc v1.75.1
google.golang.org/protobuf v1.36.10
)

require (
Expand Down Expand Up @@ -119,15 +121,15 @@ require (
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/arch v0.20.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mellium.im/sasl v0.3.2 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
Expand Down Expand Up @@ -265,6 +267,8 @@ go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
Expand Down Expand Up @@ -297,6 +301,8 @@ golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
Expand Down
69 changes: 43 additions & 26 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,16 @@ type S3Config struct {
}

type StackConfig struct {
Enabled bool
MaxScope string
MaxPer int
ProvisionerBaseURL string
ProvisionerAPIKey string
ProvisionerTimeout time.Duration
CreateWindow time.Duration
CreateMax int
Enabled bool
MaxScope string
MaxPer int
ProvisionerBaseURL string
ProvisionerGRPCAddr string
ProvisionerUseGRPC bool
ProvisionerAPIKey string
ProvisionerTimeout time.Duration
CreateWindow time.Duration
CreateMax int
}

type BootstrapConfig struct {
Expand Down Expand Up @@ -240,6 +242,13 @@ func Load() (Config, error) {
errs = append(errs, err)
}

stackUseGRPC, err := getEnvBool("STACKS_PROVISIONER_USE_GRPC", false)
if err != nil {
errs = append(errs, err)
}

stackGRPCAddr := getEnv("STACKS_PROVISIONER_GRPC_ADDR", "localhost:9090")

stackCreateWindow, err := getDuration("STACKS_CREATE_WINDOW", time.Minute)
if err != nil {
errs = append(errs, err)
Expand Down Expand Up @@ -317,14 +326,16 @@ func Load() (Config, error) {
PresignTTL: s3PresignTTL,
},
Stack: StackConfig{
Enabled: stackEnabled,
MaxScope: stackMaxScope,
MaxPer: stackMaxPer,
ProvisionerBaseURL: getEnv("STACKS_PROVISIONER_BASE_URL", "http://localhost:8081"),
ProvisionerAPIKey: getEnv("STACKS_PROVISIONER_API_KEY", ""),
ProvisionerTimeout: stackTimeout,
CreateWindow: stackCreateWindow,
CreateMax: stackCreateMax,
Enabled: stackEnabled,
MaxScope: stackMaxScope,
MaxPer: stackMaxPer,
ProvisionerBaseURL: getEnv("STACKS_PROVISIONER_BASE_URL", "http://localhost:8081"),
ProvisionerGRPCAddr: stackGRPCAddr,
ProvisionerUseGRPC: stackUseGRPC,
ProvisionerAPIKey: getEnv("STACKS_PROVISIONER_API_KEY", ""),
ProvisionerTimeout: stackTimeout,
CreateWindow: stackCreateWindow,
CreateMax: stackCreateMax,
},
Bootstrap: BootstrapConfig{
AdminTeamEnabled: bootstrapAdminTeamEnabled,
Expand Down Expand Up @@ -486,8 +497,12 @@ func validateConfig(cfg Config) error {
if cfg.Stack.MaxScope != "user" && cfg.Stack.MaxScope != "team" {
errs = append(errs, errors.New("STACKS_MAX_SCOPE must be user or team"))
}
if cfg.Stack.ProvisionerBaseURL == "" {
errs = append(errs, errors.New("STACKS_PROVISIONER_BASE_URL must not be empty"))
if cfg.Stack.ProvisionerUseGRPC {
if cfg.Stack.ProvisionerGRPCAddr == "" {
errs = append(errs, errors.New("STACKS_PROVISIONER_GRPC_ADDR must not be empty when STACKS_PROVISIONER_USE_GRPC=true"))
}
} else if cfg.Stack.ProvisionerBaseURL == "" {
errs = append(errs, errors.New("STACKS_PROVISIONER_BASE_URL must not be empty when STACKS_PROVISIONER_USE_GRPC=false"))
}
if cfg.Stack.ProvisionerTimeout <= 0 {
errs = append(errs, errors.New("STACKS_PROVISIONER_TIMEOUT must be positive"))
Expand Down Expand Up @@ -614,14 +629,16 @@ func FormatForLog(cfg Config) map[string]any {
"presign_ttl": seconds(cfg.S3.PresignTTL),
},
"stack": map[string]any{
"enabled": cfg.Stack.Enabled,
"max_scope": cfg.Stack.MaxScope,
"max_per": cfg.Stack.MaxPer,
"provisioner_base_url": cfg.Stack.ProvisionerBaseURL,
"provisioner_api_key": cfg.Stack.ProvisionerAPIKey,
"provisioner_timeout": seconds(cfg.Stack.ProvisionerTimeout),
"create_window": seconds(cfg.Stack.CreateWindow),
"create_max": cfg.Stack.CreateMax,
"enabled": cfg.Stack.Enabled,
"max_scope": cfg.Stack.MaxScope,
"max_per": cfg.Stack.MaxPer,
"provisioner_base_url": cfg.Stack.ProvisionerBaseURL,
"provisioner_grpc_addr": cfg.Stack.ProvisionerGRPCAddr,
"provisioner_use_grpc": cfg.Stack.ProvisionerUseGRPC,
"provisioner_api_key": cfg.Stack.ProvisionerAPIKey,
"provisioner_timeout": seconds(cfg.Stack.ProvisionerTimeout),
"create_window": seconds(cfg.Stack.CreateWindow),
"create_max": cfg.Stack.CreateMax,
},
"bootstrap": map[string]any{
"admin_team_enabled": cfg.Bootstrap.AdminTeamEnabled,
Expand Down
Loading
Loading