Skip to content

Commit

Permalink
Add permissions (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
rizzza authored Jul 10, 2023
1 parent e0ade14 commit eb90bf6
Show file tree
Hide file tree
Showing 51 changed files with 879 additions and 82 deletions.
10 changes: 4 additions & 6 deletions .devcontainer/.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ COCKROACH_HOST=crdb:26257
COCKROACH_URL="postgresql://root@crdb:26257/location_api_dev?sslmode=disable"

# location-api config
LOCATIONAPI_CRDB_URI="postgresql://root@crdb:26257/location_api_dev?sslmode=disable"
ATLAS_DB_URI="postgresql://root@crdb:26257/atlas_migrations?sslmode=disable"
LOCATIONAPI_CRDB_URI="postgresql://root@crdb:26257/location_api_dev?sslmode=disable"
LOCATIONAPI_EVENTS_PUBLISHER_NATS_CREDSFILE="/workspaces/location-api/.devcontainer/nsc/nkeys/creds/LOCAL/LOC/USER.creds"

NATS_URL="nats://nats:4222"
NATS_CREDS="/tmp/user.creds"

NKEYS_PATH="/workspace/.devcontainer/nsc/nkeys"
NSC_HOME="/workspace/.devcontainer/nsc/nats"
NKEYS_PATH="/workspaces/location-api/.devcontainer/nsc/nkeys"
NSC_HOME="/workspaces/location-api/.devcontainer/nsc/nats"

# postgresql client config
PGHOST=crdb
Expand Down
11 changes: 9 additions & 2 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ RUN export DEBIAN_FRONTEND=noninteractive \
uuid-runtime \
postgresql-client

# Install NATS Tooling
RUN curl -o /tmp/install.sh https://raw.githubusercontent.com/nats-io/nsc/main/install.sh \
&& chmod +x /tmp/install.sh \
&& /tmp/install.sh -d /usr/local/bin -s - \
&& rm -f /tmp/install.sh

USER vscode

RUN go install ariga.io/atlas/cmd/atlas@latest
ENV NATS_CLI_VERSION=0.0.35
RUN go install -v github.com/cweill/gotests/[email protected] \
&& go install github.com/nats-io/natscli/nats@v${NATS_CLI_VERSION} \
&& go install github.com/nats-io/nkeys/nk@latest

USER root
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"editor.defaultFormatter": "golang.go"
},
"go.buildTags": "testtools",
"go.formatTool": "goimports",
"go.lintTool": "golangci-lint",
"gopls": {
"formatting.gofumpt": true,
Expand Down
32 changes: 32 additions & 0 deletions .devcontainer/nats/nats-server.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
server_name: nats

# Client port of 4222 on all interfaces
port: 4222

# HTTP monitoring port
monitor_port: 8222

# # This is for clustering multiple servers together.
# cluster {
# name: "cluster1"
# listen: 0.0.0.0:6222
# routes = [nats://127.0.0.1:6222]
# cluster_advertise: nats-server:6222
# connect_retries: 0
# }

jetstream: enabled
jetstream {
store_dir: /data/jetstream
max_mem: 10M
max_file: 1G
}

debug: true
logtime: true

max_payload: 4MB
lame_duck_grace_period: 10s
lame_duck_duration: 30s

include "resolver.conf"
7 changes: 7 additions & 0 deletions .devcontainer/scripts/app-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

set -e

.devcontainer/scripts/nats_account.sh

sleep infinity
35 changes: 35 additions & 0 deletions .devcontainer/scripts/nats_init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/sh

set -e

# script to bootstrap a nats operator environment

if nsc describe operator; then
echo "operator exists, not overwriting config"
exit 0
fi

echo "Cleaning up NATS environment"
rm -rf /nsc/*

echo "Creating NATS operator"
nsc add operator --generate-signing-key --sys --name LOCAL
nsc edit operator -u 'nats://nats:4222'
nsc list operators
nsc describe operator

export OPERATOR_SIGNING_KEY_ID=`nsc describe operator -J | jq -r '.nats.signing_keys | first'`

echo "Creating NATS account for location-api"
nsc add account -n LOC -K ${OPERATOR_SIGNING_KEY_ID}
nsc edit account LOC --sk generate --js-mem-storage -1 --js-disk-storage -1 --js-streams -1 --js-consumer -1
nsc describe account LOC

export ACCOUNTS_SIGNING_KEY_ID=`nsc describe account LOC -J | jq -r '.nats.signing_keys | first'`

echo "Creating NATS user for location-api"
nsc add user -n USER -K ${ACCOUNTS_SIGNING_KEY_ID}
nsc describe user USER

echo "Generating NATS resolver.conf"
nsc generate config --mem-resolver --sys-account SYS --config-file /nats/resolver.conf --force
2 changes: 1 addition & 1 deletion .github/workflows/test-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ jobs:
run: go install ariga.io/atlas/cmd/atlas@latest

- name: Run go tests for ${{ matrix.ci-database }}
run: METADATAAPI_TESTDB_URI="${{ matrix.env-database-uri }}" go test -race -coverprofile=coverage.txt -covermode=atomic -tags testtools ./...
run: LOCATIONAPI_TESTDB_URI="${{ matrix.env-database-uri }}" go test -race -coverprofile=coverage.txt -covermode=atomic -tags testtools ./...
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

# Go workspace file
go.work

location-api
# binary
location-api

# devcontainer artifacts
.devcontainer/nats/resolver.conf
.devcontainer/nsc
92 changes: 92 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
all: lint tests binary ## Runs lint, tests, and builds the binary

PHONY: help all test coverage lint golint clean vendor docker-up docker-down unit-test
GOOS=linux
DB=location_api
DEV_DB=${DB}_dev
TEST_DB=${DB}_test
DEV_URI="postgresql://root@crdb:26257/${DEV_DB}?sslmode=disable"
TEST_URI="postgresql://root@crdb:26257/${TEST_DB}?sslmode=disable"

APP_NAME=location-api
PID_FILE=/tmp/loc.pid

help: Makefile ## Print help
@grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -e 's/:.*##/#/' | column -c 2 -t -s#

tests: | unit-tests

unit-tests: ## Runs unit tests
@echo --- Running unit tests...
@date --rfc-3339=seconds
@go test -race -cover -failfast -tags testtools -p 1 -v ./...

coverage: ## Generates coverage report
@echo --- Generating coverage report...
@date --rfc-3339=seconds
@go test -race -coverprofile=coverage.out -covermode=atomic -tags testtools -p 1 ./...
@go tool cover -func=coverage.out
@go tool cover -html=coverage.out

lint: golint ## Runs linting

golint:
@echo --- Running golint...
@date --rfc-3339=seconds
@golangci-lint run

clean: ## Clean up all the things
@echo --- Cleaning...
@date --rfc-3339=seconds
@rm -rf ./bin/
@rm -rf coverage.out
@go clean -testcache

binary: | vendor generate ## Builds the binary
@echo --- Building binary...
@date --rfc-3339=seconds
@go build -o bin/${APP_NAME} main.go

vendor: ## Vendors dependencies
@echo --- Downloading dependencies...
@date --rfc-3339=seconds
@go mod tidy
@go mod download

testclient:| background-run .testclient kill-running ## Regenerates the test client in graphclient

.testclient:
@echo --- Generating test graph client...
@date --rfc-3339=seconds
@go generate ./internal/graphclient

dev-nats: ## Initializes nats
@echo --- Initializing nats
@date --rfc-3339=seconds
@.devcontainer/scripts/nats_account.sh

generate: background-run .generate kill-running ## Generates code

.generate:
@echo --- Generating code...
@date --rfc-3339=seconds
@go generate ./...

go-run: ## Runs the app
@echo --- Running binary...
@date --rfc-3339=seconds
@go run main.go serve --dev

background-run: ## Runs in the app in the background
@date --rfc-3339=seconds
@if [ ! -f "${PID_FILE}" ]; then \
echo --- Running binary in the background...; \
go run main.go serve --dev --oidc=false --pid-file=${PID_FILE} & \
else \
echo --- Binary already running in the background...; \
fi

kill-running: ## Kills the running binary from pid file
@echo --- Killing background binary...
@date --rfc-3339=seconds
@kill $$(cat ${PID_FILE})
72 changes: 64 additions & 8 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ package cmd

import (
"context"
"os"
"strconv"
"syscall"

"entgo.io/ent/dialect"
entsql "entgo.io/ent/dialect/sql"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.infratographer.com/permissions-api/pkg/permissions"
"go.infratographer.com/x/crdbx"
"go.infratographer.com/x/echojwtx"
"go.infratographer.com/x/echox"
Expand All @@ -22,18 +27,28 @@ import (
"go.infratographer.com/location-api/internal/graphapi"
)

var defaultListenAddr = ":7906"
var defaultListenAddr = ":7909"

var (
enablePlayground bool
serveDevMode bool
pidFileName = ""
)

var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the location API",
Run: func(cmd *cobra.Command, args []string) {
serve(cmd.Context())
RunE: func(cmd *cobra.Command, args []string) error {
if pidFileName != "" {
if err := writePidFile(pidFileName); err != nil {
logger.Error("failed to write pid file", zap.Error(err))
return err
}

defer os.Remove(pidFileName)
}

return serve(cmd.Context())
},
}

Expand All @@ -43,13 +58,15 @@ func init() {
echox.MustViperFlags(viper.GetViper(), serveCmd.Flags(), defaultListenAddr)
echojwtx.MustViperFlags(viper.GetViper(), serveCmd.Flags())
events.MustViperFlagsForPublisher(viper.GetViper(), serveCmd.Flags(), appName)
permissions.MustViperFlags(viper.GetViper(), serveCmd.Flags())

// only available as a CLI arg because it shouldn't be something that could accidentially end up in a config file or env var
serveCmd.Flags().BoolVar(&serveDevMode, "dev", false, "dev mode: enables playground, disables all auth checks, sets CORS to allow all, pretty logging, etc.")
serveCmd.Flags().BoolVar(&enablePlayground, "playground", false, "enable the graph playground")
serveCmd.Flags().StringVar(&pidFileName, "pid-file", "", "path to the pid file")
}

func serve(ctx context.Context) {
func serve(ctx context.Context) error {
if serveDevMode {
enablePlayground = true
config.AppConfig.Logging.Debug = true
Expand Down Expand Up @@ -90,31 +107,70 @@ func serve(ctx context.Context) {
}

client := ent.NewClient(cOpts...)
defer client.Close()

var middleware []echo.MiddlewareFunc

// jwt auth middleware
if viper.GetBool("oidc.enabled") {
auth, err := echojwtx.NewAuth(ctx, config.AppConfig.OIDC)
if err != nil {
logger.Fatal("failed to initialize jwt authentication", zap.Error(err))
}

auth.JWTConfig.Skipper = echox.SkipDefaultEndpoints
config.AppConfig.Server = config.AppConfig.Server.WithMiddleware(auth.Middleware())
middleware = append(middleware, auth.Middleware())
}

srv, err := echox.NewServer(logger.Desugar(), config.AppConfig.Server, versionx.BuildDetails())
if err != nil {
logger.Fatal("failed to initialize new server", zap.Error(err))
}

perms, err := permissions.New(config.AppConfig.Permissions,
permissions.WithLogger(logger),
permissions.WithDefaultChecker(permissions.DefaultAllowChecker),
)
if err != nil {
logger.Fatal("failed to initialize permissions", zap.Error(err))
}

middleware = append(middleware, perms.Middleware())

r := graphapi.NewResolver(client, logger.Named("resolvers"))
handler := r.Handler(enablePlayground, nil)
handler := r.Handler(enablePlayground, middleware...)

srv.AddHandler(handler)

// TODO: we should have a database check
// srv.AddReadinessCheck("database", r.DatabaseCheck)

if err := srv.Run(); err != nil {
if err = srv.RunWithContext(ctx); err != nil {
logger.Fatal("failed to run server", zap.Error(err))
}

return err
}

// Write a pid file, but first make sure it doesn't exist with a running pid.
func writePidFile(pidFile string) error {
// Read in the pid file as a slice of bytes.
if piddata, err := os.ReadFile(pidFile); err == nil {
// Convert the file contents to an integer.
if pid, err := strconv.Atoi(string(piddata)); err == nil {
// Look for the pid in the process list.
if process, err := os.FindProcess(pid); err == nil {
// Send the process a signal zero kill.
if err := process.Signal(syscall.Signal(0)); err == nil {
// We only get an error if the pid isn't running, or it's not ours.
return err
}
}
}
}

logger.Debugw("writing pid file", "pid-file", pidFile)

// If we get here, then the pidfile didn't exist,
// or the pid in it doesn't belong to the user running this app.
return os.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0o664) // nolint: gomnd
}
Loading

0 comments on commit eb90bf6

Please sign in to comment.