From 69a1e2561d86aa6691645361629be5cfef47288f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Roth?= Date: Sat, 5 Oct 2024 15:26:16 +0200 Subject: [PATCH] docs: improve swagger - use markdown files in swagger - automate version, use swager.conf template - embed swagger ui index.html as docs.html --- .gitignore | 5 +- Makefile | 10 ++- api/router.go | 27 ++++---- docs/Database.md | 1 + docs/Publish.md | 29 +++++++++ docs/Status.md | 10 +++ docs/api.md | 112 +++++++++++++++++++++++++++++++++ docs/docs.html | 142 ++++++++++++++++++++++++++++++++++++++++++ docs/index.go | 8 ++- docs/swagger.conf.tpl | 16 +++++ 10 files changed, 341 insertions(+), 19 deletions(-) create mode 100644 docs/Database.md create mode 100644 docs/Publish.md create mode 100644 docs/Status.md create mode 100644 docs/api.md create mode 100644 docs/docs.html create mode 100644 docs/swagger.conf.tpl diff --git a/.gitignore b/.gitignore index 673918cb..f2333751 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,7 @@ usr/bin/aptly dpkgs/ debian/changelog.dpkg-bak -docs/ +docs/docs.go +docs/swagger.json +docs/swagger.yaml +docs/swagger.conf diff --git a/Makefile b/Makefile index 632d7066..f0b945c1 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,9 @@ version: ## Print aptly version swagger-install: # Install swag @test -f $(BINPATH)/swag || GOOS=linux GOARCH=amd64 go install github.com/swaggo/swag/cmd/swag@latest + # Generate swagger.conf + cp docs/swagger.conf.tpl docs/swagger.conf + echo "// @version $(VERSION)" >> docs/swagger.conf azurite-start: azurite & \ @@ -55,7 +58,7 @@ azurite-stop: swagger: swagger-install # Generate swagger docs - @PATH=$(BINPATH)/:$(PATH) swag init --markdownFiles docs + @PATH=$(BINPATH)/:$(PATH) swag init --markdownFiles docs --generalInfo docs/swagger.conf etcd-install: # Install etcd @@ -107,7 +110,7 @@ serve: prepare swagger-install ## Run development server (auto recompiling) test -f $(BINPATH)/air || go install github.com/air-verse/air@v1.52.3 cp debian/aptly.conf ~/.aptly.conf sed -i /enableSwaggerEndpoint/s/false/true/ ~/.aptly.conf - PATH=$(BINPATH):$$PATH air -build.pre_cmd 'swag init -q --markdownFiles docs' -build.exclude_dir docs,system,debian,pgp/keyrings,pgp/test-bins,completion.d,man,deb/testdata,console,_man,systemd,obj-x86_64-linux-gnu -- api serve -listen 0.0.0.0:3142 + PATH=$(BINPATH):$$PATH air -build.pre_cmd 'swag init -q --markdownFiles docs --generalInfo docs/swagger.conf' -build.exclude_dir docs,system,debian,pgp/keyrings,pgp/test-bins,completion.d,man,deb/testdata,console,_man,systemd,obj-x86_64-linux-gnu -- api serve -listen 0.0.0.0:3142 dpkg: prepare swagger ## Build debian packages @test -n "$(DEBARCH)" || (echo "please define DEBARCH"; exit 1) @@ -205,7 +208,8 @@ man: ## Create man pages clean: ## remove local build and module cache # Clean all generated and build files - test -d .go/ && chmod u+w -R .go/ && rm -rf .go/ || true + find .go/ -type d ! -perm -u=w -exec chmod u+w {} \; + rm -rf .go/ rm -rf build/ obj-*-linux-gnu* tmp/ rm -f unit.out aptly.test VERSION docs/docs.go docs/swagger.json docs/swagger.yaml docs/swagger.conf find system/ -type d -name __pycache__ -exec rm -rf {} \; 2>/dev/null || true diff --git a/api/router.go b/api/router.go index ada14b88..f7c95fd3 100644 --- a/api/router.go +++ b/api/router.go @@ -12,7 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog/log" - _ "github.com/aptly-dev/aptly/docs" // import docs + "github.com/aptly-dev/aptly/docs" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" ) @@ -27,26 +27,22 @@ func apiMetricsGet() gin.HandlerFunc { } func redirectSwagger(c *gin.Context) { + if c.Request.URL.Path == "/docs/index.html" { + c.Redirect(http.StatusMovedPermanently, "/docs.html") + return + } if c.Request.URL.Path == "/docs/" { - c.Redirect(http.StatusMovedPermanently, "/docs/index.html") + c.Redirect(http.StatusMovedPermanently, "/docs.html") + return + } + if c.Request.URL.Path == "/docs" { + c.Redirect(http.StatusMovedPermanently, "/docs.html") return } c.Next() } // Router returns prebuilt with routes http.Handler -// @title Aptly API -// @version 1.0 -// @description Aptly REST API Documentation - -// @contact.name Aptly -// @contact.url http://github.com/aptly-dev/aptly -// @contact.email support@aptly.info - -// @license.name MIT License -// @license.url http://www. - -// @BasePath /api func Router(c *ctx.AptlyContext) http.Handler { if aptly.EnableDebug { gin.SetMode(gin.DebugMode) @@ -73,6 +69,9 @@ func Router(c *ctx.AptlyContext) http.Handler { router.Use(gin.Recovery(), gin.ErrorLogger()) if c.Config().EnableSwaggerEndpoint { + router.GET("docs.html", func(c *gin.Context) { + c.Data(http.StatusOK, "text/html; charset=utf-8", docs.DocsHTML) + }) router.Use(redirectSwagger) url := ginSwagger.URL("/docs/doc.json") router.GET("/docs/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url)) diff --git a/docs/Database.md b/docs/Database.md new file mode 100644 index 00000000..8786b3ff --- /dev/null +++ b/docs/Database.md @@ -0,0 +1 @@ +# Aptly Database Operations diff --git a/docs/Publish.md b/docs/Publish.md new file mode 100644 index 00000000..d87c87cc --- /dev/null +++ b/docs/Publish.md @@ -0,0 +1,29 @@ +# Aptly Publish Points +
+ +Publish snapshot or local repo as Debian repository which could be +served by HTTP/FTP/rsync server. Repository is signed by user's key with +GnuPG. Key should be created beforehand (see section GPG Keys below). +Published repository could be consumed directly by apt. + +Repositories could be published to Amazon S3 service: create bucket, +[configure publishing endpoint](/doc/feature/s3/) and use S3 endpoint when +publishing. + + +#### GPG Keys + +GPG key is required to sign any published repository. Key should be +generated before publishing first repository. + +Key generation, storage, backup and revocation is out of scope of this +document, there are many tutorials available, e.g. [this one](http://fedoraproject.org/wiki/Creating_GPG_Keys). + +Publiс part of the key should be exported from your keyring using `gpg --export --armor` and +imported into apt keyring using `apt-key` tool on all machines that would be using published +repositories. + +Signing releases is highly recommended, but if you want to skip it, you +can either use `gpgDisableSign` configuration option or `--skip-signing` +flag. +
diff --git a/docs/Status.md b/docs/Status.md new file mode 100644 index 00000000..e19de2db --- /dev/null +++ b/docs/Status.md @@ -0,0 +1,10 @@ +# Aptly Status Information +
+ +## Something + +Very interesting ... asd wer + + + +
diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000..9d236ce2 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,112 @@ + +Using aptly via REST API allows to achieve two goals: + +1. Remote access to aptly service: e.g. uploading packages and publishing them from CI server. +2. Concurrent access to aptly by multiple users. + +#### Quickstart + +Run `aptly api serve` to start HTTP service: + + $ aptly api serve + Starting web server at: :8080 (press Ctrl+C to quit)... + [GIN-debug] GET /api/version --> github.com/aptly-dev/aptly/api.apiVersion (4 handlers) + ... + +By default aptly would listen on `:8080`, but it could be changed with `-listen` flag. + +Usage: + + $ aptly api serve -listen=:8080 + +Flags: + +- `-listen=":8080"`: host:port for HTTP listening +- `-no-lock`: don't lock the database + +When `-no-lock` option is enabled, API server acquires and drops the lock +around all the operations, so that API and CLI could be used concurrently. + +Try some APIs: + + $ curl http://localhost:8080/api/version + {"Version":"0.9~dev"} + + $ curl -F file=@aptly_0.8_i386.deb http://localhost:8080/api/files/aptly_0.8 + ["aptly_0.8/aptly_0.8_i386.deb"] + + $ curl -X POST -H 'Content-Type: application/json' --data '{"Name": "aptly-repo"}' http://localhost:8080/api/repos + {"Name":"aptly-repo","Comment":"","DefaultDistribution":"","DefaultComponent":""} + + $ curl -X POST http://localhost:8080/api/repos/aptly-repo/file/aptly_0.8 + {"failedFiles":[],"report":{"warnings":[],"added":["aptly_0.8_i386 added"],"removed":[]}} + + $ curl http://localhost:8080/api/repos/aptly-repo/packages + ["Pi386 aptly 0.8 966561016b44ed80"] + + $ curl -X POST -H 'Content-Type: application/json' --data '{"Distribution": "wheezy", "Sources": [{"Name": "aptly-repo"}]}' http://localhost:8080/api/publish//repos + {"Architectures":["i386"],"Distribution":"wheezy","Label":"","Origin":"","Prefix":".","SourceKind":"local","Sources":[{"Component":"main","Name":"aptly-repo"}],"Storage":""} + +#### Security + +For security reasons it is advised to let Aptly listen on a Unix domain socket rather than a port. Sockets are subject to file permissions and thus allow for user-level access control while binding to a port only gives host-level access control. To use a socket simply run Aptly with a suitable listen flag such as `aptly api serve -listen=unix:///var/run/aptly.sock`. + +Aptly's HTTP API shouldn't be directly exposed to the Internet: there's no authentication/protection of APIs. To publish the API it could be proxied through a HTTP proxy or server (e.g. nginx) to add an authentication mechanism or disallow all non-GET requests. [Reference example](https://github.com/sepich/nginx-ldap) for LDAP based per-repo access with nginx. + +#### Notes + +1. Some things (for example, S3 publishing endpoints) could be set up only using configuration file, so it requires restart of aptly HTTP server in order for changes to take effect. +1. GPG key passphrase can't be input on console, so either passwordless GPG keys are required or passphrase should be specified in API parameters. +1. Some script might be required to start/stop aptly HTTP service. +1. Some parameters are given as part of URLs, which requires proper url encoding. Unfortunately, at the moment it's not possible to pass URL arguments with `/` in them. + +### How to implement equivalent of aptly commands using API + +* `aptly mirror`: [mirror API](/doc/api/mirror) + * `list`: list + * `create`: create + * `drop`: delete + * `show`: show + * `search`: show packages/search + * `update`: update mirror +* `aptly repo`: [local repos API](/doc/api/repos) + * `add`: [file upload API](/doc/api/files) + add packages from uploaded file + * `copy`: show packages/search + add packages by key + * `create`: create + * `drop`: delete + * `edit`: edit + * `import`: not available, as mirror API is not implemented yet + * `list`: list + * `move`: show packages/search + add packages by key + delete packages by key + * `remove`: show packages/search + delete packages by key + * `rename`: not available yet, should be part of edit API + * `search`: show packages/search + * `show`: show +* `aptly snapshot`: [snapshots API](/doc/api/snapshots) + * `create`: + * empty: create snapshot with empty package references + * from mirror: not available yet + * from local repo: create snapshot from local repo + * `diff`: snapshot difference API + * `drop`: delete + * `filter`: show packages/search + create snapshot from package references + * `list`: list + * `merge`: show packages/search + processing + create snapshot from package references + * `pull`: show packages/search + processing + create snapshot from package references (might be implemented as API in future versions) + * `rename`: edit + * `search`: show packages/search + * `show`: show + * `verify`: not available yet +* `aptly publish`: [publish API](/doc/api/publish) + * `drop`: delete + * `list`: list + * `repo`: publish repo + * `snapshot`: publish snapshot + * `switch`: switch/update + * `update`: switch/update +* `aptly package`: [packages API](/doc/api/packages) + * `search`: not available yet + * `show`: only one format, with package key as input +* `aptly graph`: [graph API](/doc/api/misc) +* `aptly version`: [version API](/doc/api/misc) +* `aptly db`: not available yet diff --git a/docs/docs.html b/docs/docs.html new file mode 100644 index 00000000..cb405d9b --- /dev/null +++ b/docs/docs.html @@ -0,0 +1,142 @@ + + + + + Swagger UI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + diff --git a/docs/index.go b/docs/index.go index da325d03..8350449a 100644 --- a/docs/index.go +++ b/docs/index.go @@ -1,3 +1,9 @@ package docs -import _ "github.com/swaggo/swag" // make sure swag is in go.mod +import ( + _ "embed" + _ "github.com/swaggo/swag" // make sure swag is in go.mod +) + +//go:embed docs.html +var DocsHTML []byte diff --git a/docs/swagger.conf.tpl b/docs/swagger.conf.tpl new file mode 100644 index 00000000..670470b9 --- /dev/null +++ b/docs/swagger.conf.tpl @@ -0,0 +1,16 @@ +package docs + +// @title Aptly API +// @description.markdown + +// @contact.name Aptly +// @contact.url http://github.com/aptly-dev/aptly + +// @Tag.name Publish +// @Tag.description.markdown +// @Tag.name Database +// @Tag.description.markdown +// @Tag.name Status +// @Tag.description.markdown + +// version will be appended here: