Compare commits
	
		
			10 Commits 
		
	
	
		
			f66b124f5b
			...
			398e2bd9eb
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 398e2bd9eb | |
|  | 39a026fdb3 | |
|  | b0fe6b7384 | |
|  | c9edb55abc | |
|  | f8f611f5c5 | |
|  | 60d5fb77e0 | |
|  | 849bf54979 | |
|  | 4ed4ef4a27 | |
|  | 4ca2521937 | |
|  | c0f58ddc77 | 
|  | @ -8,15 +8,21 @@ branches: | |||
| depends_on: | ||||
|   - build | ||||
| 
 | ||||
| skip_clone: true | ||||
| 
 | ||||
| pipeline: | ||||
|   generate: | ||||
|   install-modules: | ||||
|     image: *vlang_image | ||||
|     pull: true | ||||
|     commands: | ||||
|       - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" | ||||
|       - chmod +x vieter | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       - 'cd src && v install' | ||||
| 
 | ||||
|   generate: | ||||
|     image: *vlang_image | ||||
|     commands: | ||||
|       # - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" | ||||
|       # - chmod +x vieter | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       - make | ||||
|       - ./vieter man man | ||||
|       - cd man | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| * Metrics endpoint for Prometheus integration | ||||
| * Search in list of targets using API & CLI | ||||
| * Allow filtering targets by arch value | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,6 +55,8 @@ Parameter | Description | |||
| limit | Maximum amount of results to return. | ||||
| offset | Offset of results. | ||||
| repo | Limit results to targets that publish to the given repo. | ||||
| query | Only return targets that have this substring in their URL, path or branch. | ||||
| arch | Only return targets that publish to this arch. | ||||
| 
 | ||||
| ## Get specific target | ||||
| 
 | ||||
|  |  | |||
|  | @ -63,6 +63,7 @@ fn (mut ti TargetsIterator) advance_window() { | |||
| 				|| it.path.contains(ti.filter.query) || it.branch.contains(ti.filter.query)) | ||||
| 		} | ||||
| 
 | ||||
| 		// We break out of the loop once we found a non-empty window | ||||
| 		if ti.window.len > 0 { | ||||
| 			break | ||||
| 		} | ||||
|  | @ -75,7 +76,8 @@ pub fn (mut ti TargetsIterator) next() ?Target { | |||
| 		return none | ||||
| 	} | ||||
| 
 | ||||
| 	// The first call to `next` will cause the sliding window to move to where the requested offset starts | ||||
| 	// The first call to `next` will cause the sliding window to move to where | ||||
| 	// the requested offset starts | ||||
| 	if !ti.started { | ||||
| 		ti.advance_window() | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,20 @@ | |||
| module server | ||||
| 
 | ||||
| import metrics | ||||
| import web | ||||
| 
 | ||||
| // v1_metrics serves a Prometheus-compatible metrics endpoint. | ||||
| ['/api/v1/metrics'; get; markused] | ||||
| fn (mut app App) v1_metrics() web.Result { | ||||
| 	if !app.conf.collect_metrics { | ||||
| 		return app.status(.not_found) | ||||
| 	} | ||||
| 
 | ||||
| 	mut exporter := metrics.new_prometheus_exporter([0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, | ||||
| 		10]) | ||||
| 	exporter.load('vieter_', app.collector) | ||||
| 
 | ||||
| 	// TODO stream to connection instead | ||||
| 	body := exporter.export_to_string() or { return app.status(.internal_server_error) } | ||||
| 	return app.body(.ok, 'text/plain', body) | ||||
| } | ||||
|  | @ -15,6 +15,7 @@ pub: | |||
| 	base_image           string = 'archlinux:base-devel' | ||||
| 	max_log_age          int    [empty_default] | ||||
| 	log_removal_schedule string = '0 0' | ||||
| 	collect_metrics      bool   [empty_default] | ||||
| } | ||||
| 
 | ||||
| // cmd returns the cli submodule that handles starting the server | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import util | |||
| import db | ||||
| import build { BuildJobQueue } | ||||
| import cron.expression | ||||
| import metrics | ||||
| 
 | ||||
| const ( | ||||
| 	log_file_name = 'vieter.log' | ||||
|  | @ -91,12 +92,19 @@ pub fn server(conf Config) ! { | |||
| 		util.exit_with_message(1, 'Failed to initialize database: $err.msg()') | ||||
| 	} | ||||
| 
 | ||||
| 	collector := if conf.collect_metrics { | ||||
| 		&metrics.MetricsCollector(metrics.new_default_collector()) | ||||
| 	} else { | ||||
| 		&metrics.MetricsCollector(metrics.new_null_collector()) | ||||
| 	} | ||||
| 
 | ||||
| 	mut app := &App{ | ||||
| 		logger: logger | ||||
| 		api_key: conf.api_key | ||||
| 		conf: conf | ||||
| 		repo: repo | ||||
| 		db: db | ||||
| 		collector: collector | ||||
| 		job_queue: build.new_job_queue(global_ce, conf.base_image) | ||||
| 	} | ||||
| 	app.init_job_queue() or { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ Module { | |||
|     dependencies: [ | ||||
|         'https://git.rustybever.be/vieter-v/conf', | ||||
|         'https://git.rustybever.be/vieter-v/docker', | ||||
|         'https://git.rustybever.be/vieter-v/aur' | ||||
|         'https://git.rustybever.be/vieter-v/aur', | ||||
|         'https://git.rustybever.be/vieter-v/metrics' | ||||
|     ] | ||||
| } | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import net.urllib | |||
| import time | ||||
| import json | ||||
| import log | ||||
| import metrics | ||||
| 
 | ||||
| // The Context struct represents the Context which hold the HTTP request and response. | ||||
| // It has fields for the query, form, files. | ||||
|  | @ -27,6 +28,8 @@ pub mut: | |||
| 	conn &net.TcpConn = unsafe { nil } | ||||
| 	// Gives access to a shared logger object | ||||
| 	logger shared log.Log | ||||
| 	// Used to collect metrics on the web server | ||||
| 	collector &metrics.MetricsCollector | ||||
| 	// time.ticks() from start of web connection handle. | ||||
| 	// You can use it to determine how much time is spent on your request. | ||||
| 	page_gen_start i64 | ||||
|  | @ -145,6 +148,15 @@ pub fn (ctx &Context) is_authenticated() bool { | |||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // body sends the given body as an HTTP response. | ||||
| pub fn (mut ctx Context) body(status http.Status, content_type string, body string) Result { | ||||
| 	ctx.status = status | ||||
| 	ctx.content_type = content_type | ||||
| 	ctx.send_response(body) | ||||
| 
 | ||||
| 	return Result{} | ||||
| } | ||||
| 
 | ||||
| // json<T> HTTP_OK with json_s as payload with content-type `application/json` | ||||
| pub fn (mut ctx Context) json<T>(status http.Status, j T) Result { | ||||
| 	ctx.status = status | ||||
|  | @ -319,6 +331,22 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) { | |||
| 			app.logger.flush() | ||||
| 		} | ||||
| 
 | ||||
| 		// Record how long request took to process | ||||
| 		labels := [ | ||||
| 			['method', app.req.method.str()]!, | ||||
| 			['path', app.req.url]!, | ||||
| 			// Not all methods properly set this value yet I think | ||||
| 			['status', app.status.int().str()]!, | ||||
| 		] | ||||
| 		app.collector.counter_increment(name: 'http_requests_total', labels: labels) | ||||
| 		// Prometheus prefers metrics containing base units, as defined here | ||||
| 		// https://prometheus.io/docs/practices/naming/ | ||||
| 		app.collector.histogram_record(f64(time.ticks() - app.page_gen_start) / 1000, | ||||
| 			 | ||||
| 			name: 'http_requests_duration_seconds' | ||||
| 			labels: labels | ||||
| 		) | ||||
| 
 | ||||
| 		unsafe { | ||||
| 			free(app) | ||||
| 		} | ||||
|  | @ -384,6 +412,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) { | |||
| 		static_mime_types: app.static_mime_types | ||||
| 		reader: reader | ||||
| 		logger: app.logger | ||||
| 		collector: app.collector | ||||
| 		api_key: app.api_key | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,3 +13,4 @@ api_update_frequency = 2 | |||
| image_rebuild_frequency = 1 | ||||
| max_concurrent_builds = 3 | ||||
| # max_log_age = 64 | ||||
| collect_metrics = true | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue