diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..b7537cea --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,74 @@ +--- +name: Lint & Test + +# Run for all pushes to main and pull requests when Go or YAML files change +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: '1.20' + + - name: Run golangci-lint + run: make lint + + test: + name: test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: '1.20' + + - name: Run go tests + run: make test + + # TODO(jaosorior): Add this back once we have a Dockerfile + # Related bug: # 26 + # build-image: + # name: build-image + # runs-on: ubuntu-latest + # steps: + # - name: Checkout + # uses: actions/checkout@v3 + # + # - name: docker-metadata + # id: docker-metadata + # uses: docker/metadata-action@v4 + # with: + # images: ghcr.io/${{ github.repository }}/krakend-endpoints-tool + # tags: | + # type=sha + + # - name: Build + # uses: docker/build-push-action@v4 + # with: + # context: . + # file: ./Dockerfile + # push: false + # load: true + # tags: ${{ steps.docker-metadata.outputs.tags }} + + # - name: Run Trivy vulnerability scanner + # uses: aquasecurity/trivy-action@master + # with: + # image-ref: ${{ steps.docker-metadata.outputs.tags }} + # security-checks: 'vuln,config,secret' + # ignore-unfixed: true + # severity: 'HIGH,CRITICAL' + # format: 'table' diff --git a/Makefile b/Makefile index 05df704f..6fa2c05f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,92 @@ -# go install github.com/volatiletech/sqlboiler/v4@latest -# go install github.com/glerchundi/sqlboiler-crdb/v4 -# for now install this one though, once PR #44 is merged go back https://github.com/glerchundi/sqlboiler-crdb/pull/44 -# go install github.com/adammohammed/sqlboiler-crdb@support-for-v21.2-and-newer +BIN?=permissions-api + +# Utility settings +TOOLS_DIR := .tools +GOLANGCI_LINT_VERSION = v1.50.1 + +# Container build settings +CONTAINER_BUILD_CMD?=docker build + +# Container settings +CONTAINER_REPO?=ghcr.io/infratographer +PERMISSIONS_API_CONTAINER_IMAGE_NAME = $(CONTAINER_REPO)/permissions-api +CONTAINER_TAG?=latest + +# go files to be checked +GO_FILES=$(shell git ls-files '*.go') + +## Targets + +.PHONY: help +help: Makefile ## Print help + @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -e 's/:.*##/#/' | column -c 2 -t -s# + +.PHONY: build +build: ## Builds permissions-api binary. + go build -o $(BIN) ./main.go + +.PHONY: test +test: ## Runs unit tests. + @echo Running unit tests... + @go test -timeout 30s -cover -short -tags testtools ./... + +.PHONY: coverage +coverage: ## Generates a test coverage report. + @echo Generating coverage report... + @go test -timeout 30s -tags testtools ./... -coverprofile=coverage.out -covermode=atomic + @go tool cover -func=coverage.out + @go tool cover -html=coverage.out + +lint: golint ## Runs all lint checks. + +golint: | vendor $(TOOLS_DIR)/golangci-lint ## Runs Go lint checks. + @echo Linting Go files... + @$(TOOLS_DIR)/golangci-lint run + +clean: ## Cleans generated files. + @echo Cleaning... + @rm -f coverage.out + @go clean -testcache + @rm -rf $(TOOLS_DIR) + +vendor: ## Downloads and tidies go modules. + @go mod download + @go mod tidy + +.PHONY: gci-diff gci-write gci +gci-diff: $(GO_FILES) | gci-tool ## Outputs improper go import ordering. + @gci diff -s 'standard,default,prefix(github.com/infratographer)' $^ + +gci-write: $(GO_FILES) | gci-tool ## Checks and updates all go files for proper import ordering. + @gci write -s 'standard,default,prefix(github.com/infratographer)' $^ + +gci: | gci-diff gci-write ## Outputs and corrects all improper go import ordering. + +image: ## Builds all docker images. + $(CONTAINER_BUILD_CMD) -f Dockerfile . -t $(PERMISSIONS_API_CONTAINER_IMAGE_NAME):$(CONTAINER_TAG) + +# Tools setup +$(TOOLS_DIR): + mkdir -p $(TOOLS_DIR) + +$(TOOLS_DIR)/golangci-lint: $(TOOLS_DIR) + export \ + VERSION=$(GOLANGCI_LINT_VERSION) \ + URL=https://raw.githubusercontent.com/golangci/golangci-lint \ + BINDIR=$(TOOLS_DIR) && \ + curl -sfL $$URL/$$VERSION/install.sh | sh -s $$VERSION + $(TOOLS_DIR)/golangci-lint version + $(TOOLS_DIR)/golangci-lint linters + +.PHONY: gci-tool +gci-tool: + @which gci &>/dev/null \ + || echo Installing gci tool \ + && go install github.com/daixiang0/gci@latest + +.PHONY: nk-tool +nk-tool: + @which nk &>/dev/null || \ + echo Installing "nk" tool && \ + go install github.com/nats-io/nkeys/nk@latest && \ + export PATH=$$PATH:$(shell go env GOPATH) diff --git a/cmd/root.go b/cmd/root.go index 098bf44d..bab62960 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -32,9 +32,10 @@ import ( ) var ( - appName = "permissionapi" - cfgFile string - logger *zap.SugaredLogger + appName = "permissionapi" + cfgFile string + logger *zap.SugaredLogger + globalCfg *config.AppConfig ) // rootCmd represents the base command when called without any subcommands @@ -55,7 +56,7 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is /etc/infratographer/permission-api.yaml)") - loggingx.MustViperFlags(rootCmd.PersistentFlags()) + loggingx.MustViperFlags(viper.GetViper(), rootCmd.PersistentFlags()) // Add version command versionx.RegisterCobraCommand(rootCmd, func() { versionx.PrintVersion(logger) }) @@ -90,7 +91,7 @@ func initConfig() { // If a config file is found, read it in. err := viper.ReadInConfig() - var settings config.Settings + var settings config.AppConfig if err := viper.Unmarshal(&settings); err != nil { log.Fatalf("unable to process app config, error: %s", err.Error()) @@ -105,5 +106,5 @@ func initConfig() { ) } - config.AppConfig = settings + globalCfg = &settings } diff --git a/cmd/schema.go b/cmd/schema.go index e2f5a78a..66cd724f 100644 --- a/cmd/schema.go +++ b/cmd/schema.go @@ -30,7 +30,7 @@ var schemaCmd = &cobra.Command{ Use: "schema", Short: "write the schema into SpiceDB", Run: func(cmd *cobra.Command, args []string) { - writeSchema(cmd.Context()) + writeSchema(cmd.Context(), globalCfg) }, } @@ -38,18 +38,18 @@ func init() { rootCmd.AddCommand(schemaCmd) } -func writeSchema(ctx context.Context) { - err := otelx.InitTracer(config.AppConfig.Tracing, appName, logger) +func writeSchema(ctx context.Context, cfg *config.AppConfig) { + err := otelx.InitTracer(cfg.Tracing, appName, logger) if err != nil { logger.Fatalw("unable to initialize tracing system", "error", err) } - client, err := spicedbx.NewClient(config.AppConfig.SpiceDB, config.AppConfig.Tracing.Enabled) + client, err := spicedbx.NewClient(cfg.SpiceDB, cfg.Tracing.Enabled) if err != nil { logger.Fatalw("unable to initialize spicedb client", "error", err) } - schema := spicedbx.GeneratedSchema(config.AppConfig.SpiceDB.Prefix) + schema := spicedbx.GeneratedSchema(cfg.SpiceDB.Prefix) logger.Debugw("Writing schema to DB", "schema", schema) diff --git a/cmd/server.go b/cmd/server.go index 38ee2be4..b7fe1f39 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -38,7 +38,7 @@ var serverCmd = &cobra.Command{ Use: "server", Short: "starts the permission api server", Run: func(cmd *cobra.Command, args []string) { - serve(cmd.Context()) + serve(cmd.Context(), globalCfg) }, } @@ -49,18 +49,18 @@ func init() { otelx.MustViperFlags(viper.GetViper(), serverCmd.Flags()) } -func serve(ctx context.Context) { - err := otelx.InitTracer(config.AppConfig.Tracing, appName, logger) +func serve(ctx context.Context, cfg *config.AppConfig) { + err := otelx.InitTracer(cfg.Tracing, appName, logger) if err != nil { logger.Fatalw("unable to initialize tracing system", "error", err) } - spiceClient, err := spicedbx.NewClient(config.AppConfig.SpiceDB, config.AppConfig.Tracing.Enabled) + spiceClient, err := spicedbx.NewClient(cfg.SpiceDB, cfg.Tracing.Enabled) if err != nil { logger.Fatalw("unable to initialize spicedb client", "error", err) } - s := ginx.NewServer(logger.Desugar(), config.AppConfig.Server, versionx.BuildDetails()) + s := ginx.NewServer(logger.Desugar(), cfg.Server, versionx.BuildDetails()) r := api.NewRouter(spiceClient, logger) s = s.AddHandler(r). diff --git a/cmd/worker.go b/cmd/worker.go index 1b1388b2..03016c19 100644 --- a/cmd/worker.go +++ b/cmd/worker.go @@ -33,7 +33,7 @@ var workerCmd = &cobra.Command{ Use: "worker", Short: "starts a permission api queue working", Run: func(cmd *cobra.Command, args []string) { - worker(cmd.Context()) + worker(cmd.Context(), globalCfg) }, } @@ -43,13 +43,13 @@ func init() { otelx.MustViperFlags(viper.GetViper(), workerCmd.Flags()) } -func worker(ctx context.Context) { - err := otelx.InitTracer(config.AppConfig.Tracing, appName, logger) +func worker(ctx context.Context, cfg *config.AppConfig) { + err := otelx.InitTracer(cfg.Tracing, appName, logger) if err != nil { logger.Fatalw("unable to initialize tracing system", "error", err) } - spiceClient, err := spicedbx.NewClient(config.AppConfig.SpiceDB, config.AppConfig.Tracing.Enabled) + spiceClient, err := spicedbx.NewClient(cfg.SpiceDB, cfg.Tracing.Enabled) if err != nil { logger.Fatalw("unable to initialize spicedb client", "error", err) } diff --git a/internal/config/config.go b/internal/config/config.go index b60229f4..30eca1ba 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,7 +8,7 @@ import ( "go.infratographer.com/permissions-api/internal/spicedbx" ) -var AppConfig struct { +type AppConfig struct { Logging loggingx.Config Server ginx.Config SpiceDB spicedbx.Config