diff --git a/Makefile b/Makefile index d22df3f7..a216a78b 100644 --- a/Makefile +++ b/Makefile @@ -61,8 +61,8 @@ build: install: @echo "++ Installing keel" - # CGO_ENABLED=0 GOOS=linux go install -ldflags "$(LDFLAGS)" github.com/keel-hq/keel/cmd/keel - GOOS=linux go install -ldflags "$(LDFLAGS)" github.com/keel-hq/keel/cmd/keel + # CGO_ENABLED=0 GOOS=linux go install -ldflags "$(LDFLAGS)" github.com/keel-hq/keel/cmd/keel + GOOS=linux go install -ldflags "$(LDFLAGS)" github.com/keel-hq/keel/cmd/keel install-debug: @echo "++ Installing keel with debug flags" @@ -87,7 +87,7 @@ run: keel --no-incluster --ui-dir ui/dist lint-ui: - cd ui && yarn + cd ui && yarn yarn run lint --no-fix && yarn run build run-ui: @@ -98,4 +98,9 @@ build-ui: docker push keelhq/keel:ui run-debug: install - DEBUG=true keel --no-incluster \ No newline at end of file + DEBUG=true keel --no-incluster + +update_openapi: + @echo "++ Generating OpenAPI spec" + go install github.com/swaggo/swag/cmd/swag@latest + swag init -g cmd/keel/main.go -o docs diff --git a/cmd/keel/main.go b/cmd/keel/main.go index ab951b95..ad358cfd 100644 --- a/cmd/keel/main.go +++ b/cmd/keel/main.go @@ -1,5 +1,20 @@ package main +// @title Keel API +// @version 1.0 +// @description Automated Kubernetes deployment updates. Keel is a tool for automating Kubernetes deployment updates. +// @termsOfService https://keel.sh/terms/ + +// @contact.name Keel Support +// @contact.url https://keel.sh +// @contact.email support@keel.sh + +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @host localhost:9300 +// @BasePath / + import ( "os" "os/signal" diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 00000000..3cef557a --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,1797 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "termsOfService": "https://keel.sh/terms/", + "contact": { + "name": "Keel Support", + "url": "https://keel.sh", + "email": "support@keel.sh" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/healthz": { + "get": { + "description": "Returns 200 OK if the service is healthy", + "produces": [ + "text/plain" + ], + "tags": [ + "health" + ], + "summary": "Health check endpoint", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/approvals": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all approvals (both active and archived)", + "produces": [ + "application/json" + ], + "tags": [ + "approvals" + ], + "summary": "List approvals", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/types.Approval" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Sets or removes approval requirements for a resource", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "approvals" + ], + "summary": "Update approval requirements", + "parameters": [ + { + "description": "Approval update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.resourceApprovalsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.APIResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Resource not found", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Performs an action on an approval (approve, reject, delete, or archive)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "approvals" + ], + "summary": "Approve, reject, delete, or archive an approval", + "parameters": [ + { + "description": "Approval action request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.approveRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.Approval" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Approval not found", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/audit": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns paginated audit logs with optional filtering", + "produces": [ + "application/json" + ], + "tags": [ + "audit" + ], + "summary": "Get audit logs", + "parameters": [ + { + "type": "integer", + "description": "Maximum number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Offset for pagination", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "description": "Comma-separated list of resource kinds to filter", + "name": "filter", + "in": "query" + }, + { + "type": "string", + "description": "Filter by email", + "name": "email", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.auditLogsResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/info": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns information about the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.UserInfo" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/login": { + "post": { + "description": "Authenticates a user with username and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login user", + "parameters": [ + { + "description": "Login credentials", + "name": "credentials", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.loginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.loginResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Username or password incorrect", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/logout": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Logs out the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Logs out the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/refresh": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Generates a new authentication token for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Refresh authentication token", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.loginResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns information about the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.UserInfo" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/policies": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the Keel policy for a specific resource", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "policies" + ], + "summary": "Update resource policy", + "parameters": [ + { + "description": "Policy update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.resourcePolicyUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.APIResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Resource not found", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/resources": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all tracked Kubernetes resources with their policies", + "produces": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "List resources", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/http.resource" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/stats": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns aggregated statistics about webhooks, approvals, and updates", + "produces": [ + "application/json" + ], + "tags": [ + "stats" + ], + "summary": "Get statistics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/tracked": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all tracked container images with their triggers and policies", + "produces": [ + "application/json" + ], + "tags": [ + "tracked" + ], + "summary": "List tracked images", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/http.trackedImage" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the trigger type and poll schedule for a tracked resource", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tracked" + ], + "summary": "Update tracking settings", + "parameters": [ + { + "description": "Track settings request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.trackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.APIResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Resource not found", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/azure": { + "post": { + "description": "Receives and processes Azure Container Registry webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Azure Container Registry webhook", + "parameters": [ + { + "description": "Azure webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.azureWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/dockerhub": { + "post": { + "description": "Receives and processes DockerHub webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger DockerHub webhook", + "parameters": [ + { + "description": "DockerHub webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.dockerHubWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/github": { + "post": { + "description": "Receives and processes GitHub webhook notifications for container registry events (both GitHub Packages and GitHub Container Registry)", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger GitHub webhook", + "parameters": [ + { + "type": "string", + "description": "GitHub event type (package or registry_package)", + "name": "X-GitHub-Event", + "in": "header", + "required": true + }, + { + "description": "GitHub webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/harbor": { + "post": { + "description": "Receives and processes Harbor registry webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Harbor webhook", + "parameters": [ + { + "description": "Harbor webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.harborWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/jfrog": { + "post": { + "description": "Receives and processes JFrog Artifactory webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger JFrog webhook", + "parameters": [ + { + "description": "JFrog webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.jfrogWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/native": { + "post": { + "description": "Triggers a deployment update event directly with repository and tag information", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger native webhook", + "parameters": [ + { + "description": "Repository information with name and tag", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.Repository" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/quay": { + "post": { + "description": "Receives and processes Quay.io webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Quay webhook", + "parameters": [ + { + "description": "Quay webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.quayWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/registry": { + "post": { + "description": "Receives and processes Docker Registry V2 notification webhooks for image push events. Compatible with Docker Registry, GitLab Container Registry, and Harbor.", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Docker Registry notification webhook", + "parameters": [ + { + "description": "Registry notification payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.registryNotification" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/version": { + "get": { + "description": "Returns the current version information of Keel", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Get Keel version", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.VersionInfo" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "http.APIResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + } + } + }, + "http.UserInfo": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "id": { + "type": "string" + }, + "last_login_ip": { + "type": "string" + }, + "last_login_time": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "role_id": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "http.approveRequest": { + "type": "object", + "properties": { + "action": { + "description": "defaults to approve", + "type": "string" + }, + "id": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "voter": { + "type": "string" + } + } + }, + "http.auditLogsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/types.AuditLog" + } + }, + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "http.azureWebhook": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "host": { + "type": "string" + } + } + }, + "target": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + }, + "http.dockerHubWebhook": { + "type": "object", + "properties": { + "callback_url": { + "type": "string" + }, + "push_data": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": {} + }, + "pushed_at": { + "type": "integer" + }, + "pusher": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "repository": { + "type": "object", + "properties": { + "comment_count": { + "type": "integer" + }, + "date_created": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "dockerfile": { + "type": "string" + }, + "full_description": { + "type": "string" + }, + "is_official": { + "type": "boolean" + }, + "is_private": { + "type": "boolean" + }, + "is_trusted": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "repo_name": { + "type": "string" + }, + "repo_url": { + "type": "string" + }, + "star_count": { + "type": "integer" + }, + "status": { + "type": "string" + } + } + } + } + }, + "http.harborWebhook": { + "type": "object", + "properties": { + "event_data": { + "type": "object", + "properties": { + "repository": { + "type": "object", + "properties": { + "date_created": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "repo_full_name": { + "type": "string" + }, + "repo_type": { + "type": "string" + } + } + }, + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "resource_url": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + } + }, + "occur_at": { + "type": "integer" + }, + "operator": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "http.jfrogWebhook": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "image_name": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "platforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "architecture": { + "type": "string" + }, + "os": { + "type": "string" + } + } + } + }, + "repo_key": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "tag": { + "type": "string" + } + } + }, + "domain": { + "type": "string" + }, + "event_type": { + "type": "string" + }, + "jpd_origin": { + "type": "string" + }, + "source": { + "type": "string" + }, + "subscription_key": { + "type": "string" + } + } + }, + "http.loginRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "http.loginResponse": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + } + }, + "http.quayWebhook": { + "type": "object", + "properties": { + "docker_url": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "updated_tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "http.registryNotification": { + "type": "object", + "properties": { + "events": { + "type": "array", + "items": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "actor": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "id": { + "type": "string" + }, + "request": { + "type": "object", + "properties": { + "addr": { + "type": "string" + }, + "host": { + "type": "string" + }, + "id": { + "type": "string" + }, + "method": { + "type": "string" + }, + "useragent": { + "type": "string" + } + } + }, + "source": { + "type": "object", + "properties": { + "addr": { + "type": "string" + }, + "instanceID": { + "type": "string" + } + } + }, + "target": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "mediaType": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "tag": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "timestamp": { + "type": "string" + } + } + } + } + } + }, + "http.resource": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "identifier": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "type": "string" + } + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/k8s.Status" + } + } + }, + "http.resourceApprovalsUpdateRequest": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "votesRequired": { + "type": "integer" + } + } + }, + "http.resourcePolicyUpdateRequest": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "provider": { + "type": "string" + } + } + }, + "http.trackRequest": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "trigger": { + "type": "string" + } + } + }, + "http.trackedImage": { + "type": "object", + "properties": { + "image": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "pollSchedule": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "registry": { + "type": "string" + }, + "trigger": { + "type": "string" + } + } + }, + "k8s.Status": { + "type": "object", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.\n+optional", + "type": "integer" + }, + "readyReplicas": { + "description": "Total number of ready pods targeted by this deployment.\n+optional", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).\n+optional", + "type": "integer" + }, + "unavailableReplica": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of\npods that are still required for the deployment to have 100% available capacity. They may\neither be pods that are running but not yet available or pods that still have not been created.\n+optional", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.\n+optional", + "type": "integer" + } + } + }, + "types.Approval": { + "type": "object", + "properties": { + "archived": { + "description": "Archived is set to true once approval is finally approved/rejected", + "type": "boolean" + }, + "createdAt": { + "description": "When this approval was created", + "type": "string" + }, + "currentVersion": { + "type": "string" + }, + "deadline": { + "description": "Deadline for this request", + "type": "string" + }, + "digest": { + "description": "Digest is used to verify that images are the ones that got the approvals.\nIf digest doesn't match for the image, votes are reset.", + "type": "string" + }, + "event": { + "description": "Event that triggered evaluation", + "allOf": [ + { + "$ref": "#/definitions/types.Event" + } + ] + }, + "id": { + "type": "string" + }, + "identifier": { + "description": "Identifier is used to inform user about specific\nHelm release or k8s deployment\nie: k8s \u003cnamespace\u003e/\u003cdeployment name\u003e\n helm: \u003cnamespace\u003e/\u003crelease name\u003e", + "type": "string" + }, + "message": { + "type": "string" + }, + "newVersion": { + "type": "string" + }, + "provider": { + "description": "Provider name - Kubernetes/Helm", + "allOf": [ + { + "$ref": "#/definitions/types.ProviderType" + } + ] + }, + "rejected": { + "description": "Explicitly rejected approval\ncan be set directly by user\nso even if deadline is not reached approval\ncould be turned down", + "type": "boolean" + }, + "updatedAt": { + "description": "WHen this approval was updated", + "type": "string" + }, + "voters": { + "description": "Voters is a list of voter\nIDs for audit", + "allOf": [ + { + "$ref": "#/definitions/types.JSONB" + } + ] + }, + "votesReceived": { + "type": "integer" + }, + "votesRequired": { + "description": "Requirements for the update such as number of votes\nand deadline", + "type": "integer" + } + } + }, + "types.AuditLog": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "action": { + "description": "create/delete/update", + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/types.JSONB" + }, + "payload": { + "description": "can be used for bigger messages such as webhook payload", + "type": "string" + }, + "payloadType": { + "type": "string" + }, + "resourceKind": { + "description": "approval/deployment/daemonset/statefulset/etc...", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "types.Event": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "repository": { + "$ref": "#/definitions/types.Repository" + }, + "triggerName": { + "description": "optional field to identify trigger", + "type": "string" + } + } + }, + "types.JSONB": { + "type": "object", + "additionalProperties": true + }, + "types.ProviderType": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "ProviderTypeUnknown", + "ProviderTypeKubernetes", + "ProviderTypeHelm" + ] + }, + "types.Repository": { + "type": "object", + "properties": { + "digest": { + "description": "optional digest field", + "type": "string" + }, + "host": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "types.VersionInfo": { + "type": "object", + "properties": { + "apiVersion": { + "type": "string" + }, + "arch": { + "type": "string" + }, + "buildDate": { + "type": "string" + }, + "experimental": { + "type": "boolean" + }, + "goVersion": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "os": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "version": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "localhost:9300", + BasePath: "/", + Schemes: []string{}, + Title: "Keel API", + Description: "Automated Kubernetes deployment updates. Keel is a tool for automating Kubernetes deployment updates.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 00000000..2c36375d --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,1773 @@ +{ + "swagger": "2.0", + "info": { + "description": "Automated Kubernetes deployment updates. Keel is a tool for automating Kubernetes deployment updates.", + "title": "Keel API", + "termsOfService": "https://keel.sh/terms/", + "contact": { + "name": "Keel Support", + "url": "https://keel.sh", + "email": "support@keel.sh" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "host": "localhost:9300", + "basePath": "/", + "paths": { + "/healthz": { + "get": { + "description": "Returns 200 OK if the service is healthy", + "produces": [ + "text/plain" + ], + "tags": [ + "health" + ], + "summary": "Health check endpoint", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/approvals": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all approvals (both active and archived)", + "produces": [ + "application/json" + ], + "tags": [ + "approvals" + ], + "summary": "List approvals", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/types.Approval" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Sets or removes approval requirements for a resource", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "approvals" + ], + "summary": "Update approval requirements", + "parameters": [ + { + "description": "Approval update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.resourceApprovalsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.APIResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Resource not found", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Performs an action on an approval (approve, reject, delete, or archive)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "approvals" + ], + "summary": "Approve, reject, delete, or archive an approval", + "parameters": [ + { + "description": "Approval action request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.approveRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.Approval" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Approval not found", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/audit": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns paginated audit logs with optional filtering", + "produces": [ + "application/json" + ], + "tags": [ + "audit" + ], + "summary": "Get audit logs", + "parameters": [ + { + "type": "integer", + "description": "Maximum number of results", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "Offset for pagination", + "name": "offset", + "in": "query" + }, + { + "type": "string", + "description": "Comma-separated list of resource kinds to filter", + "name": "filter", + "in": "query" + }, + { + "type": "string", + "description": "Filter by email", + "name": "email", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.auditLogsResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/info": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns information about the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.UserInfo" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/login": { + "post": { + "description": "Authenticates a user with username and password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login user", + "parameters": [ + { + "description": "Login credentials", + "name": "credentials", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.loginRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.loginResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "401": { + "description": "Username or password incorrect", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/logout": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Logs out the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Logs out the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Logout user", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/refresh": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Generates a new authentication token for the current user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Refresh authentication token", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.loginResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/auth/user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns information about the currently authenticated user", + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Get current user info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.UserInfo" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/policies": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the Keel policy for a specific resource", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "policies" + ], + "summary": "Update resource policy", + "parameters": [ + { + "description": "Policy update request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.resourcePolicyUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.APIResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Resource not found", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/resources": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all tracked Kubernetes resources with their policies", + "produces": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "List resources", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/http.resource" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/stats": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns aggregated statistics about webhooks, approvals, and updates", + "produces": [ + "application/json" + ], + "tags": [ + "stats" + ], + "summary": "Get statistics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/tracked": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a list of all tracked container images with their triggers and policies", + "produces": [ + "application/json" + ], + "tags": [ + "tracked" + ], + "summary": "List tracked images", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/http.trackedImage" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the trigger type and poll schedule for a tracked resource", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "tracked" + ], + "summary": "Update tracking settings", + "parameters": [ + { + "description": "Track settings request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.trackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/http.APIResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + }, + "404": { + "description": "Resource not found", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/azure": { + "post": { + "description": "Receives and processes Azure Container Registry webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Azure Container Registry webhook", + "parameters": [ + { + "description": "Azure webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.azureWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/dockerhub": { + "post": { + "description": "Receives and processes DockerHub webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger DockerHub webhook", + "parameters": [ + { + "description": "DockerHub webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.dockerHubWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/github": { + "post": { + "description": "Receives and processes GitHub webhook notifications for container registry events (both GitHub Packages and GitHub Container Registry)", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger GitHub webhook", + "parameters": [ + { + "type": "string", + "description": "GitHub event type (package or registry_package)", + "name": "X-GitHub-Event", + "in": "header", + "required": true + }, + { + "description": "GitHub webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/harbor": { + "post": { + "description": "Receives and processes Harbor registry webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Harbor webhook", + "parameters": [ + { + "description": "Harbor webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.harborWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/jfrog": { + "post": { + "description": "Receives and processes JFrog Artifactory webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger JFrog webhook", + "parameters": [ + { + "description": "JFrog webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.jfrogWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/native": { + "post": { + "description": "Triggers a deployment update event directly with repository and tag information", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger native webhook", + "parameters": [ + { + "description": "Repository information with name and tag", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.Repository" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/quay": { + "post": { + "description": "Receives and processes Quay.io webhook notifications for image push events", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Quay webhook", + "parameters": [ + { + "description": "Quay webhook payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.quayWebhook" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/v1/webhooks/registry": { + "post": { + "description": "Receives and processes Docker Registry V2 notification webhooks for image push events. Compatible with Docker Registry, GitLab Container Registry, and Harbor.", + "consumes": [ + "application/json" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "webhooks" + ], + "summary": "Trigger Docker Registry notification webhook", + "parameters": [ + { + "description": "Registry notification payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/http.registryNotification" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, + "/version": { + "get": { + "description": "Returns the current version information of Keel", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Get Keel version", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.VersionInfo" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + } + }, + "definitions": { + "http.APIResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + } + } + }, + "http.UserInfo": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "id": { + "type": "string" + }, + "last_login_ip": { + "type": "string" + }, + "last_login_time": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "role_id": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "username": { + "type": "string" + } + } + }, + "http.approveRequest": { + "type": "object", + "properties": { + "action": { + "description": "defaults to approve", + "type": "string" + }, + "id": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "voter": { + "type": "string" + } + } + }, + "http.auditLogsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/types.AuditLog" + } + }, + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "http.azureWebhook": { + "type": "object", + "properties": { + "request": { + "type": "object", + "properties": { + "host": { + "type": "string" + } + } + }, + "target": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + }, + "http.dockerHubWebhook": { + "type": "object", + "properties": { + "callback_url": { + "type": "string" + }, + "push_data": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": {} + }, + "pushed_at": { + "type": "integer" + }, + "pusher": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "repository": { + "type": "object", + "properties": { + "comment_count": { + "type": "integer" + }, + "date_created": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "dockerfile": { + "type": "string" + }, + "full_description": { + "type": "string" + }, + "is_official": { + "type": "boolean" + }, + "is_private": { + "type": "boolean" + }, + "is_trusted": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "repo_name": { + "type": "string" + }, + "repo_url": { + "type": "string" + }, + "star_count": { + "type": "integer" + }, + "status": { + "type": "string" + } + } + } + } + }, + "http.harborWebhook": { + "type": "object", + "properties": { + "event_data": { + "type": "object", + "properties": { + "repository": { + "type": "object", + "properties": { + "date_created": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "repo_full_name": { + "type": "string" + }, + "repo_type": { + "type": "string" + } + } + }, + "resources": { + "type": "array", + "items": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "resource_url": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } + } + }, + "occur_at": { + "type": "integer" + }, + "operator": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "http.jfrogWebhook": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "image_name": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "platforms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "architecture": { + "type": "string" + }, + "os": { + "type": "string" + } + } + } + }, + "repo_key": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "tag": { + "type": "string" + } + } + }, + "domain": { + "type": "string" + }, + "event_type": { + "type": "string" + }, + "jpd_origin": { + "type": "string" + }, + "source": { + "type": "string" + }, + "subscription_key": { + "type": "string" + } + } + }, + "http.loginRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "http.loginResponse": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + } + }, + "http.quayWebhook": { + "type": "object", + "properties": { + "docker_url": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "updated_tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "http.registryNotification": { + "type": "object", + "properties": { + "events": { + "type": "array", + "items": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "actor": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "id": { + "type": "string" + }, + "request": { + "type": "object", + "properties": { + "addr": { + "type": "string" + }, + "host": { + "type": "string" + }, + "id": { + "type": "string" + }, + "method": { + "type": "string" + }, + "useragent": { + "type": "string" + } + } + }, + "source": { + "type": "object", + "properties": { + "addr": { + "type": "string" + }, + "instanceID": { + "type": "string" + } + } + }, + "target": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "mediaType": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "tag": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "timestamp": { + "type": "string" + } + } + } + } + } + }, + "http.resource": { + "type": "object", + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "identifier": { + "type": "string" + }, + "images": { + "type": "array", + "items": { + "type": "string" + } + }, + "kind": { + "type": "string" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/k8s.Status" + } + } + }, + "http.resourceApprovalsUpdateRequest": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "votesRequired": { + "type": "integer" + } + } + }, + "http.resourcePolicyUpdateRequest": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "provider": { + "type": "string" + } + } + }, + "http.trackRequest": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "trigger": { + "type": "string" + } + } + }, + "http.trackedImage": { + "type": "object", + "properties": { + "image": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "pollSchedule": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "registry": { + "type": "string" + }, + "trigger": { + "type": "string" + } + } + }, + "k8s.Status": { + "type": "object", + "properties": { + "availableReplicas": { + "description": "Total number of available pods (ready for at least minReadySeconds) targeted by this deployment.\n+optional", + "type": "integer" + }, + "readyReplicas": { + "description": "Total number of ready pods targeted by this deployment.\n+optional", + "type": "integer" + }, + "replicas": { + "description": "Total number of non-terminated pods targeted by this deployment (their labels match the selector).\n+optional", + "type": "integer" + }, + "unavailableReplica": { + "description": "Total number of unavailable pods targeted by this deployment. This is the total number of\npods that are still required for the deployment to have 100% available capacity. They may\neither be pods that are running but not yet available or pods that still have not been created.\n+optional", + "type": "integer" + }, + "updatedReplicas": { + "description": "Total number of non-terminated pods targeted by this deployment that have the desired template spec.\n+optional", + "type": "integer" + } + } + }, + "types.Approval": { + "type": "object", + "properties": { + "archived": { + "description": "Archived is set to true once approval is finally approved/rejected", + "type": "boolean" + }, + "createdAt": { + "description": "When this approval was created", + "type": "string" + }, + "currentVersion": { + "type": "string" + }, + "deadline": { + "description": "Deadline for this request", + "type": "string" + }, + "digest": { + "description": "Digest is used to verify that images are the ones that got the approvals.\nIf digest doesn't match for the image, votes are reset.", + "type": "string" + }, + "event": { + "description": "Event that triggered evaluation", + "allOf": [ + { + "$ref": "#/definitions/types.Event" + } + ] + }, + "id": { + "type": "string" + }, + "identifier": { + "description": "Identifier is used to inform user about specific\nHelm release or k8s deployment\nie: k8s \u003cnamespace\u003e/\u003cdeployment name\u003e\n helm: \u003cnamespace\u003e/\u003crelease name\u003e", + "type": "string" + }, + "message": { + "type": "string" + }, + "newVersion": { + "type": "string" + }, + "provider": { + "description": "Provider name - Kubernetes/Helm", + "allOf": [ + { + "$ref": "#/definitions/types.ProviderType" + } + ] + }, + "rejected": { + "description": "Explicitly rejected approval\ncan be set directly by user\nso even if deadline is not reached approval\ncould be turned down", + "type": "boolean" + }, + "updatedAt": { + "description": "WHen this approval was updated", + "type": "string" + }, + "voters": { + "description": "Voters is a list of voter\nIDs for audit", + "allOf": [ + { + "$ref": "#/definitions/types.JSONB" + } + ] + }, + "votesReceived": { + "type": "integer" + }, + "votesRequired": { + "description": "Requirements for the update such as number of votes\nand deadline", + "type": "integer" + } + } + }, + "types.AuditLog": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "action": { + "description": "create/delete/update", + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/types.JSONB" + }, + "payload": { + "description": "can be used for bigger messages such as webhook payload", + "type": "string" + }, + "payloadType": { + "type": "string" + }, + "resourceKind": { + "description": "approval/deployment/daemonset/statefulset/etc...", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "types.Event": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "repository": { + "$ref": "#/definitions/types.Repository" + }, + "triggerName": { + "description": "optional field to identify trigger", + "type": "string" + } + } + }, + "types.JSONB": { + "type": "object", + "additionalProperties": true + }, + "types.ProviderType": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "ProviderTypeUnknown", + "ProviderTypeKubernetes", + "ProviderTypeHelm" + ] + }, + "types.Repository": { + "type": "object", + "properties": { + "digest": { + "description": "optional digest field", + "type": "string" + }, + "host": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "types.VersionInfo": { + "type": "object", + "properties": { + "apiVersion": { + "type": "string" + }, + "arch": { + "type": "string" + }, + "buildDate": { + "type": "string" + }, + "experimental": { + "type": "boolean" + }, + "goVersion": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "os": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "version": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 00000000..02f76523 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,1184 @@ +basePath: / +definitions: + http.APIResponse: + properties: + status: + type: string + type: object + http.UserInfo: + properties: + avatar: + type: string + id: + type: string + last_login_ip: + type: string + last_login_time: + type: integer + name: + type: string + role_id: + type: string + status: + type: integer + username: + type: string + type: object + http.approveRequest: + properties: + action: + description: defaults to approve + type: string + id: + type: string + identifier: + type: string + voter: + type: string + type: object + http.auditLogsResponse: + properties: + data: + items: + $ref: '#/definitions/types.AuditLog' + type: array + limit: + type: integer + offset: + type: integer + total: + type: integer + type: object + http.azureWebhook: + properties: + request: + properties: + host: + type: string + type: object + target: + properties: + digest: + type: string + repository: + type: string + tag: + type: string + type: object + type: object + http.dockerHubWebhook: + properties: + callback_url: + type: string + push_data: + properties: + images: + items: {} + type: array + pushed_at: + type: integer + pusher: + type: string + tag: + type: string + type: object + repository: + properties: + comment_count: + type: integer + date_created: + type: integer + description: + type: string + dockerfile: + type: string + full_description: + type: string + is_official: + type: boolean + is_private: + type: boolean + is_trusted: + type: boolean + name: + type: string + namespace: + type: string + owner: + type: string + repo_name: + type: string + repo_url: + type: string + star_count: + type: integer + status: + type: string + type: object + type: object + http.harborWebhook: + properties: + event_data: + properties: + repository: + properties: + date_created: + type: integer + name: + type: string + namespace: + type: string + repo_full_name: + type: string + repo_type: + type: string + type: object + resources: + items: + properties: + digest: + type: string + resource_url: + type: string + tag: + type: string + type: object + type: array + type: object + occur_at: + type: integer + operator: + type: string + type: + type: string + type: object + http.jfrogWebhook: + properties: + data: + properties: + image_name: + type: string + name: + type: string + path: + type: string + platforms: + items: + properties: + architecture: + type: string + os: + type: string + type: object + type: array + repo_key: + type: string + sha256: + type: string + size: + type: integer + tag: + type: string + type: object + domain: + type: string + event_type: + type: string + jpd_origin: + type: string + source: + type: string + subscription_key: + type: string + type: object + http.loginRequest: + properties: + password: + type: string + username: + type: string + type: object + http.loginResponse: + properties: + token: + type: string + type: object + http.quayWebhook: + properties: + docker_url: + type: string + homepage: + type: string + name: + type: string + namespace: + type: string + repository: + type: string + updated_tags: + items: + type: string + type: array + type: object + http.registryNotification: + properties: + events: + items: + properties: + action: + type: string + actor: + properties: + name: + type: string + type: object + id: + type: string + request: + properties: + addr: + type: string + host: + type: string + id: + type: string + method: + type: string + useragent: + type: string + type: object + source: + properties: + addr: + type: string + instanceID: + type: string + type: object + target: + properties: + digest: + type: string + length: + type: integer + mediaType: + type: string + repository: + type: string + size: + type: integer + tag: + type: string + url: + type: string + type: object + timestamp: + type: string + type: object + type: array + type: object + http.resource: + properties: + annotations: + additionalProperties: + type: string + type: object + identifier: + type: string + images: + items: + type: string + type: array + kind: + type: string + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + policy: + type: string + provider: + type: string + status: + $ref: '#/definitions/k8s.Status' + type: object + http.resourceApprovalsUpdateRequest: + properties: + identifier: + type: string + provider: + type: string + votesRequired: + type: integer + type: object + http.resourcePolicyUpdateRequest: + properties: + identifier: + type: string + policy: + type: string + provider: + type: string + type: object + http.trackRequest: + properties: + identifier: + type: string + provider: + type: string + schedule: + type: string + trigger: + type: string + type: object + http.trackedImage: + properties: + image: + type: string + namespace: + type: string + policy: + type: string + pollSchedule: + type: string + provider: + type: string + registry: + type: string + trigger: + type: string + type: object + k8s.Status: + properties: + availableReplicas: + description: |- + Total number of available pods (ready for at least minReadySeconds) targeted by this deployment. + +optional + type: integer + readyReplicas: + description: |- + Total number of ready pods targeted by this deployment. + +optional + type: integer + replicas: + description: |- + Total number of non-terminated pods targeted by this deployment (their labels match the selector). + +optional + type: integer + unavailableReplica: + description: |- + Total number of unavailable pods targeted by this deployment. This is the total number of + pods that are still required for the deployment to have 100% available capacity. They may + either be pods that are running but not yet available or pods that still have not been created. + +optional + type: integer + updatedReplicas: + description: |- + Total number of non-terminated pods targeted by this deployment that have the desired template spec. + +optional + type: integer + type: object + types.Approval: + properties: + archived: + description: Archived is set to true once approval is finally approved/rejected + type: boolean + createdAt: + description: When this approval was created + type: string + currentVersion: + type: string + deadline: + description: Deadline for this request + type: string + digest: + description: |- + Digest is used to verify that images are the ones that got the approvals. + If digest doesn't match for the image, votes are reset. + type: string + event: + allOf: + - $ref: '#/definitions/types.Event' + description: Event that triggered evaluation + id: + type: string + identifier: + description: |- + Identifier is used to inform user about specific + Helm release or k8s deployment + ie: k8s / + helm: / + type: string + message: + type: string + newVersion: + type: string + provider: + allOf: + - $ref: '#/definitions/types.ProviderType' + description: Provider name - Kubernetes/Helm + rejected: + description: |- + Explicitly rejected approval + can be set directly by user + so even if deadline is not reached approval + could be turned down + type: boolean + updatedAt: + description: WHen this approval was updated + type: string + voters: + allOf: + - $ref: '#/definitions/types.JSONB' + description: |- + Voters is a list of voter + IDs for audit + votesReceived: + type: integer + votesRequired: + description: |- + Requirements for the update such as number of votes + and deadline + type: integer + type: object + types.AuditLog: + properties: + accountId: + type: string + action: + description: create/delete/update + type: string + createdAt: + type: string + email: + type: string + id: + type: string + identifier: + type: string + message: + type: string + metadata: + $ref: '#/definitions/types.JSONB' + payload: + description: can be used for bigger messages such as webhook payload + type: string + payloadType: + type: string + resourceKind: + description: approval/deployment/daemonset/statefulset/etc... + type: string + updatedAt: + type: string + username: + type: string + type: object + types.Event: + properties: + createdAt: + type: string + repository: + $ref: '#/definitions/types.Repository' + triggerName: + description: optional field to identify trigger + type: string + type: object + types.JSONB: + additionalProperties: true + type: object + types.ProviderType: + enum: + - 0 + - 1 + - 2 + type: integer + x-enum-varnames: + - ProviderTypeUnknown + - ProviderTypeKubernetes + - ProviderTypeHelm + types.Repository: + properties: + digest: + description: optional digest field + type: string + host: + type: string + name: + type: string + tag: + type: string + type: object + types.VersionInfo: + properties: + apiVersion: + type: string + arch: + type: string + buildDate: + type: string + experimental: + type: boolean + goVersion: + type: string + kernelVersion: + type: string + name: + type: string + os: + type: string + revision: + type: string + version: + type: string + type: object +host: localhost:9300 +info: + contact: + email: support@keel.sh + name: Keel Support + url: https://keel.sh + description: Automated Kubernetes deployment updates. Keel is a tool for automating + Kubernetes deployment updates. + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: https://keel.sh/terms/ + title: Keel API + version: "1.0" +paths: + /healthz: + get: + description: Returns 200 OK if the service is healthy + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + summary: Health check endpoint + tags: + - health + /v1/approvals: + get: + description: Returns a list of all approvals (both active and archived) + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/types.Approval' + type: array + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: List approvals + tags: + - approvals + post: + consumes: + - application/json + description: Performs an action on an approval (approve, reject, delete, or + archive) + parameters: + - description: Approval action request + in: body + name: request + required: true + schema: + $ref: '#/definitions/http.approveRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/types.Approval' + "400": + description: Bad request + schema: + type: string + "404": + description: Approval not found + schema: + type: string + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Approve, reject, delete, or archive an approval + tags: + - approvals + put: + consumes: + - application/json + description: Sets or removes approval requirements for a resource + parameters: + - description: Approval update request + in: body + name: request + required: true + schema: + $ref: '#/definitions/http.resourceApprovalsUpdateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.APIResponse' + "400": + description: Bad request + schema: + type: string + "404": + description: Resource not found + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Update approval requirements + tags: + - approvals + /v1/audit: + get: + description: Returns paginated audit logs with optional filtering + parameters: + - description: Maximum number of results + in: query + name: limit + type: integer + - description: Offset for pagination + in: query + name: offset + type: integer + - description: Comma-separated list of resource kinds to filter + in: query + name: filter + type: string + - description: Filter by email + in: query + name: email + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.auditLogsResponse' + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Get audit logs + tags: + - audit + /v1/auth/info: + get: + description: Returns information about the currently authenticated user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.UserInfo' + "401": + description: Unauthorized + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Get current user info + tags: + - auth + /v1/auth/login: + post: + consumes: + - application/json + description: Authenticates a user with username and password + parameters: + - description: Login credentials + in: body + name: credentials + required: true + schema: + $ref: '#/definitions/http.loginRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.loginResponse' + "400": + description: Bad request + schema: + type: string + "401": + description: Username or password incorrect + schema: + type: string + summary: Login user + tags: + - auth + /v1/auth/logout: + get: + description: Logs out the currently authenticated user + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Logout user + tags: + - auth + post: + description: Logs out the currently authenticated user + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Logout user + tags: + - auth + /v1/auth/refresh: + get: + description: Generates a new authentication token for the current user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.loginResponse' + "401": + description: Unauthorized + schema: + type: string + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Refresh authentication token + tags: + - auth + /v1/auth/user: + get: + description: Returns information about the currently authenticated user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.UserInfo' + "401": + description: Unauthorized + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Get current user info + tags: + - auth + /v1/policies: + put: + consumes: + - application/json + description: Updates the Keel policy for a specific resource + parameters: + - description: Policy update request + in: body + name: request + required: true + schema: + $ref: '#/definitions/http.resourcePolicyUpdateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.APIResponse' + "400": + description: Bad request + schema: + type: string + "404": + description: Resource not found + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Update resource policy + tags: + - policies + /v1/resources: + get: + description: Returns a list of all tracked Kubernetes resources with their policies + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/http.resource' + type: array + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: List resources + tags: + - resources + /v1/stats: + get: + description: Returns aggregated statistics about webhooks, approvals, and updates + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Get statistics + tags: + - stats + /v1/tracked: + get: + description: Returns a list of all tracked container images with their triggers + and policies + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/http.trackedImage' + type: array + "500": + description: Internal server error + schema: + type: string + security: + - ApiKeyAuth: [] + summary: List tracked images + tags: + - tracked + put: + consumes: + - application/json + description: Updates the trigger type and poll schedule for a tracked resource + parameters: + - description: Track settings request + in: body + name: request + required: true + schema: + $ref: '#/definitions/http.trackRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/http.APIResponse' + "400": + description: Bad request + schema: + type: string + "404": + description: Resource not found + schema: + type: string + security: + - ApiKeyAuth: [] + summary: Update tracking settings + tags: + - tracked + /v1/webhooks/azure: + post: + consumes: + - application/json + description: Receives and processes Azure Container Registry webhook notifications + for image push events + parameters: + - description: Azure webhook payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/http.azureWebhook' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger Azure Container Registry webhook + tags: + - webhooks + /v1/webhooks/dockerhub: + post: + consumes: + - application/json + description: Receives and processes DockerHub webhook notifications for image + push events + parameters: + - description: DockerHub webhook payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/http.dockerHubWebhook' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger DockerHub webhook + tags: + - webhooks + /v1/webhooks/github: + post: + consumes: + - application/json + description: Receives and processes GitHub webhook notifications for container + registry events (both GitHub Packages and GitHub Container Registry) + parameters: + - description: GitHub event type (package or registry_package) + in: header + name: X-GitHub-Event + required: true + type: string + - description: GitHub webhook payload + in: body + name: payload + required: true + schema: + type: object + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger GitHub webhook + tags: + - webhooks + /v1/webhooks/harbor: + post: + consumes: + - application/json + description: Receives and processes Harbor registry webhook notifications for + image push events + parameters: + - description: Harbor webhook payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/http.harborWebhook' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger Harbor webhook + tags: + - webhooks + /v1/webhooks/jfrog: + post: + consumes: + - application/json + description: Receives and processes JFrog Artifactory webhook notifications + for image push events + parameters: + - description: JFrog webhook payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/http.jfrogWebhook' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger JFrog webhook + tags: + - webhooks + /v1/webhooks/native: + post: + consumes: + - application/json + description: Triggers a deployment update event directly with repository and + tag information + parameters: + - description: Repository information with name and tag + in: body + name: payload + required: true + schema: + $ref: '#/definitions/types.Repository' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger native webhook + tags: + - webhooks + /v1/webhooks/quay: + post: + consumes: + - application/json + description: Receives and processes Quay.io webhook notifications for image + push events + parameters: + - description: Quay webhook payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/http.quayWebhook' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger Quay webhook + tags: + - webhooks + /v1/webhooks/registry: + post: + consumes: + - application/json + description: Receives and processes Docker Registry V2 notification webhooks + for image push events. Compatible with Docker Registry, GitLab Container Registry, + and Harbor. + parameters: + - description: Registry notification payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/http.registryNotification' + produces: + - text/plain + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad request + schema: + type: string + summary: Trigger Docker Registry notification webhook + tags: + - webhooks + /version: + get: + description: Returns the current version information of Keel + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/types.VersionInfo' + "500": + description: Internal server error + schema: + type: string + summary: Get Keel version + tags: + - health +swagger: "2.0" diff --git a/pkg/http/approvals_endpoint.go b/pkg/http/approvals_endpoint.go index 61ae6660..a07c95a1 100644 --- a/pkg/http/approvals_endpoint.go +++ b/pkg/http/approvals_endpoint.go @@ -25,6 +25,15 @@ const ( actionArchive = "archive" ) +// approvalsHandler godoc +// @Summary List approvals +// @Description Returns a list of all approvals (both active and archived) +// @Tags approvals +// @Produce json +// @Success 200 {array} types.Approval +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/approvals [get] func (s *TriggerServer) approvalsHandler(resp http.ResponseWriter, req *http.Request) { // lists all (both archived) @@ -55,7 +64,18 @@ type resourceApprovalsUpdateRequest struct { VotesRequired int `json:"votesRequired"` } -// approvalSetHandler allows to set/remove approvals for resources +// approvalSetHandler godoc +// @Summary Update approval requirements +// @Description Sets or removes approval requirements for a resource +// @Tags approvals +// @Accept json +// @Produce json +// @Param request body resourceApprovalsUpdateRequest true "Approval update request" +// @Success 200 {object} APIResponse +// @Failure 400 {string} string "Bad request" +// @Failure 404 {string} string "Resource not found" +// @Security ApiKeyAuth +// @Router /v1/approvals [put] func (s *TriggerServer) approvalSetHandler(resp http.ResponseWriter, req *http.Request) { var approvalUpdateRequest resourceApprovalsUpdateRequest @@ -110,6 +130,19 @@ func (s *TriggerServer) approvalSetHandler(resp http.ResponseWriter, req *http.R fmt.Fprintf(resp, "resource with identifier '%s' not found", approvalUpdateRequest.Identifier) } +// approvalApproveHandler godoc +// @Summary Approve, reject, delete, or archive an approval +// @Description Performs an action on an approval (approve, reject, delete, or archive) +// @Tags approvals +// @Accept json +// @Produce json +// @Param request body approveRequest true "Approval action request" +// @Success 200 {object} types.Approval +// @Failure 400 {string} string "Bad request" +// @Failure 404 {string} string "Approval not found" +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/approvals [post] func (s *TriggerServer) approvalApproveHandler(resp http.ResponseWriter, req *http.Request) { var ar approveRequest diff --git a/pkg/http/audit_endpoint.go b/pkg/http/audit_endpoint.go index a2877e43..aaa0472c 100644 --- a/pkg/http/audit_endpoint.go +++ b/pkg/http/audit_endpoint.go @@ -8,6 +8,19 @@ import ( "github.com/keel-hq/keel/types" ) +// adminAuditLogHandler godoc +// @Summary Get audit logs +// @Description Returns paginated audit logs with optional filtering +// @Tags audit +// @Produce json +// @Param limit query int false "Maximum number of results" +// @Param offset query int false "Offset for pagination" +// @Param filter query string false "Comma-separated list of resource kinds to filter" +// @Param email query string false "Filter by email" +// @Success 200 {object} auditLogsResponse +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/audit [get] func (s *TriggerServer) adminAuditLogHandler(resp http.ResponseWriter, req *http.Request) { query := &types.AuditLogQuery{} diff --git a/pkg/http/auth.go b/pkg/http/auth.go index 0cbf581f..8e675ca2 100644 --- a/pkg/http/auth.go +++ b/pkg/http/auth.go @@ -87,6 +87,16 @@ func extractToken(req *http.Request) string { return token } +// logoutHandler godoc +// @Summary Logout user +// @Description Logs out the currently authenticated user +// @Tags auth +// @Produce json +// @Success 200 {object} map[string]interface{} +// @Failure 401 {string} string "Unauthorized" +// @Security ApiKeyAuth +// @Router /v1/auth/logout [post] +// @Router /v1/auth/logout [get] func (s *TriggerServer) logoutHandler(resp http.ResponseWriter, req *http.Request) { resp.WriteHeader(200) @@ -98,6 +108,22 @@ type loginRequest struct { Password string `json:"password"` } +// loginResponse represents the authentication response +type loginResponse struct { + Token string `json:"token"` +} + +// loginHandler godoc +// @Summary Login user +// @Description Authenticates a user with username and password +// @Tags auth +// @Accept json +// @Produce json +// @Param credentials body loginRequest true "Login credentials" +// @Success 200 {object} loginResponse +// @Failure 400 {string} string "Bad request" +// @Failure 401 {string} string "Username or password incorrect" +// @Router /v1/auth/login [post] func (s *TriggerServer) loginHandler(resp http.ResponseWriter, req *http.Request) { var lr loginRequest @@ -131,6 +157,16 @@ func (s *TriggerServer) loginHandler(resp http.ResponseWriter, req *http.Request response(authResp, 200, nil, resp, req) } +// refreshHandler godoc +// @Summary Refresh authentication token +// @Description Generates a new authentication token for the current user +// @Tags auth +// @Produce json +// @Success 200 {object} loginResponse +// @Failure 401 {string} string "Unauthorized" +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/auth/refresh [get] func (s *TriggerServer) refreshHandler(resp http.ResponseWriter, req *http.Request) { user := auth.GetAccountFromCtx(req.Context()) diff --git a/pkg/http/azure_webhook_trigger.go b/pkg/http/azure_webhook_trigger.go index 17164112..dcf78202 100644 --- a/pkg/http/azure_webhook_trigger.go +++ b/pkg/http/azure_webhook_trigger.go @@ -56,6 +56,16 @@ type azureWebhook struct { } `json:"request"` } +// azureHandler godoc +// @Summary Trigger Azure Container Registry webhook +// @Description Receives and processes Azure Container Registry webhook notifications for image push events +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body azureWebhook true "Azure webhook payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/azure [post] func (s *TriggerServer) azureHandler(resp http.ResponseWriter, req *http.Request) { aw := azureWebhook{} if err := json.NewDecoder(req.Body).Decode(&aw); err != nil { diff --git a/pkg/http/dockerhub_webhook_trigger.go b/pkg/http/dockerhub_webhook_trigger.go index f8790852..6f394e80 100644 --- a/pkg/http/dockerhub_webhook_trigger.go +++ b/pkg/http/dockerhub_webhook_trigger.go @@ -80,7 +80,16 @@ type dockerHubWebhook struct { } `json:"repository"` } -// dockerHubHandler - used to react to dockerhub webhooks +// dockerHubHandler godoc +// @Summary Trigger DockerHub webhook +// @Description Receives and processes DockerHub webhook notifications for image push events +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body dockerHubWebhook true "DockerHub webhook payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/dockerhub [post] func (s *TriggerServer) dockerHubHandler(resp http.ResponseWriter, req *http.Request) { dw := dockerHubWebhook{} if err := json.NewDecoder(req.Body).Decode(&dw); err != nil { diff --git a/pkg/http/github_webhook_trigger.go b/pkg/http/github_webhook_trigger.go index 3e84d6df..eeb6f873 100644 --- a/pkg/http/github_webhook_trigger.go +++ b/pkg/http/github_webhook_trigger.go @@ -3,12 +3,13 @@ package http import ( "encoding/json" "fmt" - "github.com/keel-hq/keel/types" - "github.com/prometheus/client_golang/prometheus" "net/http" "strings" "time" + "github.com/keel-hq/keel/types" + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" ) @@ -58,7 +59,17 @@ type githubPackageV2Webhook struct { } `json:"package"` } -// githubHandler - used to react to github webhooks +// githubHandler godoc +// @Summary Trigger GitHub webhook +// @Description Receives and processes GitHub webhook notifications for container registry events (both GitHub Packages and GitHub Container Registry) +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param X-GitHub-Event header string true "GitHub event type (package or registry_package)" +// @Param payload body object true "GitHub webhook payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/github [post] func (s *TriggerServer) githubHandler(resp http.ResponseWriter, req *http.Request) { // GitHub provides different webhook events for each registry. // Github Package uses 'registry_package' diff --git a/pkg/http/harbor_webhook_trigger.go b/pkg/http/harbor_webhook_trigger.go index 5edf866f..944cf8fc 100644 --- a/pkg/http/harbor_webhook_trigger.go +++ b/pkg/http/harbor_webhook_trigger.go @@ -68,6 +68,16 @@ type harborWebhook struct { } `json:"event_data"` } +// harborHandler godoc +// @Summary Trigger Harbor webhook +// @Description Receives and processes Harbor registry webhook notifications for image push events +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body harborWebhook true "Harbor webhook payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/harbor [post] func (s *TriggerServer) harborHandler(resp http.ResponseWriter, req *http.Request) { hn := harborWebhook{} if err := json.NewDecoder(req.Body).Decode(&hn); err != nil { @@ -81,7 +91,7 @@ func (s *TriggerServer) harborHandler(resp http.ResponseWriter, req *http.Reques "event": hn, }).Debug("harborHandler: received event, looking for a pushImage tag") - if hn.Type == "pushImage" || hn.Type == "PUSH_ARTIFACT" { + if hn.Type == "pushImage" || hn.Type == "PUSH_ARTIFACT" { // go trough all the ressource items for _, e := range hn.EventData.Resources { imageRepo, err := image.Parse(e.ResourceURL) diff --git a/pkg/http/http.go b/pkg/http/http.go index ef3e8d96..49255428 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -207,10 +207,25 @@ func (s *TriggerServer) registerWebhookRoutes(mux *mux.Router) { } } +// healthHandler godoc +// @Summary Health check endpoint +// @Description Returns 200 OK if the service is healthy +// @Tags health +// @Produce plain +// @Success 200 {string} string "OK" +// @Router /healthz [get] func (s *TriggerServer) healthHandler(resp http.ResponseWriter, req *http.Request) { resp.WriteHeader(http.StatusOK) } +// versionHandler godoc +// @Summary Get Keel version +// @Description Returns the current version information of Keel +// @Tags health +// @Produce json +// @Success 200 {object} types.VersionInfo +// @Failure 500 {string} string "Internal server error" +// @Router /version [get] func (s *TriggerServer) versionHandler(resp http.ResponseWriter, req *http.Request) { v := version.GetKeelVersion() @@ -298,6 +313,16 @@ type UserInfo struct { RoleID string `json:"role_id"` } +// userInfoHandler godoc +// @Summary Get current user info +// @Description Returns information about the currently authenticated user +// @Tags auth +// @Produce json +// @Success 200 {object} UserInfo +// @Failure 401 {string} string "Unauthorized" +// @Security ApiKeyAuth +// @Router /v1/auth/info [get] +// @Router /v1/auth/user [get] func (s *TriggerServer) userInfoHandler(resp http.ResponseWriter, req *http.Request) { user := auth.GetAccountFromCtx(req.Context()) diff --git a/pkg/http/jfrog_webhook_trigger.go b/pkg/http/jfrog_webhook_trigger.go index c9350adc..03700d75 100644 --- a/pkg/http/jfrog_webhook_trigger.go +++ b/pkg/http/jfrog_webhook_trigger.go @@ -92,6 +92,16 @@ type jfrogWebhook struct { Source string `json:"source"` } +// jfrogHandler godoc +// @Summary Trigger JFrog webhook +// @Description Receives and processes JFrog Artifactory webhook notifications for image push events +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body jfrogWebhook true "JFrog webhook payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/jfrog [post] func (s *TriggerServer) jfrogHandler(resp http.ResponseWriter, req *http.Request) { jw := jfrogWebhook{} if err := json.NewDecoder(req.Body).Decode(&jw); err != nil { diff --git a/pkg/http/native_webhook_trigger.go b/pkg/http/native_webhook_trigger.go index 955f8393..9cbef8b2 100644 --- a/pkg/http/native_webhook_trigger.go +++ b/pkg/http/native_webhook_trigger.go @@ -24,7 +24,16 @@ func init() { prometheus.MustRegister(newNativeWebhooksCounter) } -// nativeHandler - used to trigger event directly +// nativeHandler godoc +// @Summary Trigger native webhook +// @Description Triggers a deployment update event directly with repository and tag information +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body types.Repository true "Repository information with name and tag" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/native [post] func (s *TriggerServer) nativeHandler(resp http.ResponseWriter, req *http.Request) { repo := types.Repository{} if err := json.NewDecoder(req.Body).Decode(&repo); err != nil { diff --git a/pkg/http/policy_endpoint.go b/pkg/http/policy_endpoint.go index f6324dcb..e08a7585 100644 --- a/pkg/http/policy_endpoint.go +++ b/pkg/http/policy_endpoint.go @@ -14,6 +14,18 @@ type resourcePolicyUpdateRequest struct { Provider string `json:"provider"` } +// policyUpdateHandler godoc +// @Summary Update resource policy +// @Description Updates the Keel policy for a specific resource +// @Tags policies +// @Accept json +// @Produce json +// @Param request body resourcePolicyUpdateRequest true "Policy update request" +// @Success 200 {object} APIResponse +// @Failure 400 {string} string "Bad request" +// @Failure 404 {string} string "Resource not found" +// @Security ApiKeyAuth +// @Router /v1/policies [put] func (s *TriggerServer) policyUpdateHandler(resp http.ResponseWriter, req *http.Request) { var policyRequest resourcePolicyUpdateRequest dec := json.NewDecoder(req.Body) diff --git a/pkg/http/quay_webhook_trigger.go b/pkg/http/quay_webhook_trigger.go index 7e0ac09f..072fe2d3 100644 --- a/pkg/http/quay_webhook_trigger.go +++ b/pkg/http/quay_webhook_trigger.go @@ -45,6 +45,16 @@ type quayWebhook struct { UpdatedTags []string `json:"updated_tags"` } +// quayHandler godoc +// @Summary Trigger Quay webhook +// @Description Receives and processes Quay.io webhook notifications for image push events +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body quayWebhook true "Quay webhook payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/quay [post] func (s *TriggerServer) quayHandler(resp http.ResponseWriter, req *http.Request) { qw := quayWebhook{} if err := json.NewDecoder(req.Body).Decode(&qw); err != nil { diff --git a/pkg/http/registry_notifications.go b/pkg/http/registry_notifications.go index ba48799b..563be3f4 100644 --- a/pkg/http/registry_notifications.go +++ b/pkg/http/registry_notifications.go @@ -88,6 +88,16 @@ type registryNotification struct { } `json:"events"` } +// registryNotificationHandler godoc +// @Summary Trigger Docker Registry notification webhook +// @Description Receives and processes Docker Registry V2 notification webhooks for image push events. Compatible with Docker Registry, GitLab Container Registry, and Harbor. +// @Tags webhooks +// @Accept json +// @Produce plain +// @Param payload body registryNotification true "Registry notification payload" +// @Success 200 {string} string "OK" +// @Failure 400 {string} string "Bad request" +// @Router /v1/webhooks/registry [post] func (s *TriggerServer) registryNotificationHandler(resp http.ResponseWriter, req *http.Request) { rn := registryNotification{} if err := json.NewDecoder(req.Body).Decode(&rn); err != nil { diff --git a/pkg/http/resources_endpoint.go b/pkg/http/resources_endpoint.go index ab84f6e1..5e7809dc 100644 --- a/pkg/http/resources_endpoint.go +++ b/pkg/http/resources_endpoint.go @@ -22,6 +22,15 @@ type resource struct { Status k8s.Status `json:"status"` } +// resourcesHandler godoc +// @Summary List resources +// @Description Returns a list of all tracked Kubernetes resources with their policies +// @Tags resources +// @Produce json +// @Success 200 {array} resource +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/resources [get] func (s *TriggerServer) resourcesHandler(resp http.ResponseWriter, req *http.Request) { vals := s.grc.Values() diff --git a/pkg/http/stats_endpoint.go b/pkg/http/stats_endpoint.go index a373fa95..d3db916a 100644 --- a/pkg/http/stats_endpoint.go +++ b/pkg/http/stats_endpoint.go @@ -14,6 +14,15 @@ type dailyStats struct { Updates int `json:"updates"` } +// statsHandler godoc +// @Summary Get statistics +// @Description Returns aggregated statistics about webhooks, approvals, and updates +// @Tags stats +// @Produce json +// @Success 200 {object} map[string]interface{} +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/stats [get] func (s *TriggerServer) statsHandler(resp http.ResponseWriter, req *http.Request) { stats, err := s.store.AuditStatistics(&types.AuditLogStatsQuery{}) response(stats, 200, err, resp, req) diff --git a/pkg/http/tracked_endpoint.go b/pkg/http/tracked_endpoint.go index 4d233893..31cd07c6 100644 --- a/pkg/http/tracked_endpoint.go +++ b/pkg/http/tracked_endpoint.go @@ -19,6 +19,15 @@ type trackedImage struct { Registry string `json:"registry"` } +// trackedHandler godoc +// @Summary List tracked images +// @Description Returns a list of all tracked container images with their triggers and policies +// @Tags tracked +// @Produce json +// @Success 200 {array} trackedImage +// @Failure 500 {string} string "Internal server error" +// @Security ApiKeyAuth +// @Router /v1/tracked [get] func (s *TriggerServer) trackedHandler(resp http.ResponseWriter, req *http.Request) { trackedImages, err := s.providers.TrackedImages() @@ -46,6 +55,18 @@ type trackRequest struct { Schedule string `json:"schedule"` } +// trackSetHandler godoc +// @Summary Update tracking settings +// @Description Updates the trigger type and poll schedule for a tracked resource +// @Tags tracked +// @Accept json +// @Produce json +// @Param request body trackRequest true "Track settings request" +// @Success 200 {object} APIResponse +// @Failure 400 {string} string "Bad request" +// @Failure 404 {string} string "Resource not found" +// @Security ApiKeyAuth +// @Router /v1/tracked [put] func (s *TriggerServer) trackSetHandler(resp http.ResponseWriter, req *http.Request) { var trackReq trackRequest