Add Swaggo annotations to all HTTP API handlers and Makefile target

- Add update_openapi target to Makefile that installs swag and generates docs
- Add general API info annotations to cmd/keel/main.go
- Add Swaggo annotations to all handlers in pkg/http/:
  - Core endpoints: healthHandler, versionHandler, userInfoHandler
  - Auth endpoints: loginHandler, logoutHandler, refreshHandler
  - Approvals endpoints: approvalsHandler, approvalSetHandler, approvalApproveHandler
  - Admin endpoints: resourcesHandler, policyUpdateHandler, trackedHandler,
    trackSetHandler, adminAuditLogHandler, statsHandler
  - Webhook endpoints: nativeHandler, dockerHubHandler, jfrogHandler, quayHandler,
    azureHandler, githubHandler, harborHandler, registryNotificationHandler
- Generate OpenAPI spec files in docs/ directory

Spec-Ref: helix-specs@58bef305:000159_all-http-api-handlers
feature/000159-all-http-api-handlers
Karolis 2026-02-18 19:46:34 +04:00
parent d4c6c2688f
commit 9a1c20f2ee
21 changed files with 5022 additions and 11 deletions

View File

@ -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
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

View File

@ -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"

1797
docs/docs.go Normal file

File diff suppressed because it is too large Load Diff

1773
docs/swagger.json Normal file

File diff suppressed because it is too large Load Diff

1184
docs/swagger.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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{}

View File

@ -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())

View File

@ -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 {

View File

@ -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 {

View File

@ -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'

View File

@ -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)

View File

@ -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())

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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()

View File

@ -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)

View File

@ -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