From 4cb845537bb38dcf1b8450b7487c5d7e222df944 Mon Sep 17 00:00:00 2001 From: "lukasz.widera@vshn.ch" Date: Tue, 2 Apr 2024 13:29:10 +0200 Subject: [PATCH 1/6] draft: spks billing --- Makefile.vars.mk | 2 +- go.mod | 17 ++-- go.sum | 38 +++++---- main.go | 1 + pkg/cmd/spks.go | 209 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 239 insertions(+), 28 deletions(-) create mode 100644 pkg/cmd/spks.go diff --git a/Makefile.vars.mk b/Makefile.vars.mk index 05882bc..0007f60 100644 --- a/Makefile.vars.mk +++ b/Makefile.vars.mk @@ -19,7 +19,7 @@ IMG_TAG ?= latest # Image URL to use all building/pushing image targets CONTAINER_IMG ?= ghcr.io/$(PROJECT_OWNER)/$(PROJECT_NAME):$(IMG_TAG) -ANTORA_PREVIEW_CMD ?= $(DOCKER_CMD) run --rm --publish 35729:35729 --publish 2020:2020 --volume "${PWD}/.git":/preview/antora/.git --volume "${PWD}/docs":/preview/antora/docs docker.io/vshn/antora-preview:3.0.1.1 --style=syn --antora=docs +ANTORA_PREVIEW_CMD ?= $(DOCKER_CMD) run --rm --publish 35729:35729 --publish 2020:2020 --volume "${PWD}/.git":/preview/antora/.git --volume "${PWD}/docs":/preview/antora/docs ghcr.io/vshn/antora-preview --style=syn --antora=docs # TEST:integration ENVTEST_ADDITIONAL_FLAGS ?= --bin-dir "$(go_bin)" diff --git a/go.mod b/go.mod index bc8ffd3..725d839 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,13 @@ require ( github.com/exoscale/egoscale v0.90.1 github.com/go-logr/logr v1.3.0 github.com/go-logr/zapr v1.3.0 + github.com/prometheus/client_golang v1.19.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.24.4 github.com/vshn/provider-cloudscale v0.5.0 github.com/vshn/provider-exoscale v0.8.1 go.uber.org/zap v1.26.0 - golang.org/x/oauth2 v0.12.0 + golang.org/x/oauth2 v0.16.0 gopkg.in/dnaeon/go-vcr.v3 v3.1.2 k8s.io/api v0.29.0 k8s.io/apimachinery v0.29.0 @@ -61,15 +62,13 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/lopezator/migrator v0.3.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/afero v1.9.3 // indirect @@ -77,16 +76,16 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.16.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 85900f6..f6a4d69 100644 --- a/go.sum +++ b/go.sum @@ -291,6 +291,8 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -335,8 +337,6 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -344,6 +344,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= @@ -354,13 +356,13 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -478,8 +480,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -550,8 +552,8 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190517181255-950ef44c6e07/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -562,8 +564,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -622,14 +624,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -810,8 +812,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/main.go b/main.go index df828ed..dc83c9e 100644 --- a/main.go +++ b/main.go @@ -122,6 +122,7 @@ func newApp() (context.Context, context.CancelFunc, *cli.App) { Commands: []*cli.Command{ cmd.ExoscaleCmds(), cmd.CloudscaleCmds(), + cmd.SpksCMD(), }, ExitErrHandler: func(c *cli.Context, err error) { if err != nil { diff --git a/pkg/cmd/spks.go b/pkg/cmd/spks.go new file mode 100644 index 0000000..9a63004 --- /dev/null +++ b/pkg/cmd/spks.go @@ -0,0 +1,209 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "os/signal" + + "time" + + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/api" + v1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" + "github.com/urfave/cli/v2" + "github.com/vshn/billing-collector-cloudservices/pkg/log" + "github.com/vshn/billing-collector-cloudservices/pkg/odoo" +) + +func SpksCMD() *cli.Command { + var ( + prometheusQueryArr = [4]string{ + "count(max_over_time(crossplane_resource_info{kind=\"compositemariadbinstances\", service_level=\"standard\"}[1d:1d]))", + "count(max_over_time(crossplane_resource_info{kind=\"compositemariadbinstances\", service_level=\"premium\"}[1d:1d]))", + "count(max_over_time(crossplane_resource_info{kind=\"compositeredisinstances\", service_level=\"standard\"}[1d:1d]))", + "count(max_over_time(crossplane_resource_info{kind=\"compositeredisinstances\", service_level=\"premium\"}[1d:1d]))", + } + odooURL string + odooOauthTokenURL string + odooClientId string + odooClientSecret string + salesOrder string + prometheusURL string + UnitID string + ) + + return &cli.Command{ + Name: "spks", + Usage: "Collect metrics from spks", + Before: addCommandName, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "odoo-url", Usage: "URL of the Odoo Metered Billing API", + EnvVars: []string{"ODOO_URL"}, Destination: &odooURL, Value: "https://preprod.central.vshn.ch/api/v2/product_usage_report_POST"}, + &cli.StringFlag{Name: "odoo-oauth-token-url", Usage: "Oauth Token URL to authenticate with Odoo metered billing API", + EnvVars: []string{"ODOO_OAUTH_TOKEN_URL"}, Destination: &odooOauthTokenURL, Required: true, DefaultText: defaultTextForRequiredFlags}, + &cli.StringFlag{Name: "odoo-oauth-client-id", Usage: "Client ID of the oauth client to interact with Odoo metered billing API", + EnvVars: []string{"ODOO_OAUTH_CLIENT_ID"}, Destination: &odooClientId, Required: true, DefaultText: defaultTextForRequiredFlags}, + &cli.StringFlag{Name: "odoo-oauth-client-secret", Usage: "Client secret of the oauth client to interact with Odoo metered billing API", + EnvVars: []string{"ODOO_OAUTH_CLIENT_SECRET"}, Destination: &odooClientSecret, Required: true, DefaultText: defaultTextForRequiredFlags}, + &cli.StringFlag{Name: "sales-order", Usage: "Sales order for APPUiO Managed clusters", + EnvVars: []string{"SALES_ORDER"}, Destination: &salesOrder, Required: false, DefaultText: defaultTextForOptionalFlags, Value: "S10121"}, + &cli.StringFlag{Name: "prometheus-url", Usage: "URL of the Prometheus API", + EnvVars: []string{"PROMETHEUS_URL"}, Destination: &prometheusURL, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "http://prometheus-monitoring-application.monitoring-application.svc.cluster.local:9090"}, + &cli.StringFlag{Name: "unit-id", Usage: "Unit ID for the consumed units", + EnvVars: []string{"UNIT_ID"}, Destination: &UnitID, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "uom_uom_68_b1811ca1"}, + }, + Action: func(c *cli.Context) error { + // this function is intended to run once a day + // it will query the prometheus metrics for the last 24 hours + // it will be run via kubernetes cronjob at midnight + logger := log.Logger(c.Context) + logger.Info("starting spks data collector") + + logger.Info("Getting specific metric from thanos") + + client, err := api.NewClient(api.Config{ + Address: prometheusURL, + }) + if err != nil { + logger.Error(err, "Error creating Prometheus client") + return err + } + + v1api := v1.NewAPI(client) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + // send this value to odoo + + mariadbStandard, err := QueryPrometheus(ctx, v1api, prometheusQueryArr[0], logger) + if err != nil { + logger.Error(err, "Error querying Prometheus") + return err + } + mariadbPremium, err := QueryPrometheus(ctx, v1api, prometheusQueryArr[1], logger) + if err != nil { + logger.Error(err, "Error querying Prometheus") + return err + } + redisStandard, err := QueryPrometheus(ctx, v1api, prometheusQueryArr[2], logger) + if err != nil { + logger.Error(err, "Error querying Prometheus") + return err + } + redisPremium, err := QueryPrometheus(ctx, v1api, prometheusQueryArr[3], logger) + if err != nil { + logger.Error(err, "Error querying Prometheus") + return err + } + + odooClient := odoo.NewOdooAPIClient(c.Context, odooURL, odooOauthTokenURL, odooClientId, odooClientSecret, logger) + location, err := time.LoadLocation("Europe/Zurich") + if err != nil { + return fmt.Errorf("load loaction: %w", err) + } + + from := time.Now().In(location).UTC() + to := time.Now().In(location).Add(-(time.Hour * 24)).UTC() + + billingRecords := []odoo.OdooMeteredBillingRecord{ + { + ProductID: "appcat-spks-mariadb-standard", + InstanceID: "mariadb-standard", + ItemDescription: "appcat-spks-mariadb-standard", + ItemGroupDescription: "SPKS", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(mariadbStandard), + TimeRange: odoo.TimeRange{ + From: from, + To: to, + }, + }, + { + ProductID: "appcat-spks-mariadb-premium", + InstanceID: "mariadb-premium", + ItemDescription: "appcat-spks-mariadb-premium", + ItemGroupDescription: "SPKS", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(mariadbPremium), + TimeRange: odoo.TimeRange{ + From: from, + To: to, + }, + }, + { + ProductID: "appcat-spks-redis-standard", + InstanceID: "redis-standard", + ItemDescription: "appcat-spks-redis-standard", + ItemGroupDescription: "SPKS", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(redisStandard), + TimeRange: odoo.TimeRange{ + From: from, + To: to, + }, + }, + { + ProductID: "appcat-spks-redis-premium", + InstanceID: "redis-premium", + ItemDescription: "appcat-spks-redis-premium", + ItemGroupDescription: "SPKS", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(redisPremium), + TimeRange: odoo.TimeRange{ + From: from, + To: to, + }, + }, + } + + ticker := time.NewTicker(24 * time.Hour) + sigint := make(chan os.Signal, 1) + signal.Notify(sigint, os.Interrupt) + + for loop := true; loop; { + select { + case <-ticker.C: + err = odooClient.SendData(billingRecords) + if err != nil { + logger.Error(err, "can't export data to Odoo") + panic(err) + } + case <-sigint: + loop = false + // this one breaks select{} statement + // immediately and finish function + break + } + } + return nil + }, + } +} + +func QueryPrometheus(ctx context.Context, v1api v1.API, query string, logger logr.Logger) (int, error) { + result, warnings, err := v1api.Query(ctx, query, time.Now(), v1.WithTimeout(5*time.Second)) + if err != nil { + logger.Error(err, "Error querying Prometheus") + return 0, err + } + if len(warnings) > 0 { + logger.Info("Warnings", "warnings from Prometheus query", warnings) + } + + switch result.Type() { + case model.ValVector: + vectorVal := result.(model.Vector) + if len(vectorVal) != 1 { + return 0, fmt.Errorf("expected 1 result, got %d", len(vectorVal)) + } + default: + return 0, fmt.Errorf("result type is not Vector: %s", result.Type()) + + } + return int(result.(model.Vector)[0].Value), nil +} From 68bd538bcd8b381bbb4ec88331255a731a2d84fa Mon Sep 17 00:00:00 2001 From: "lukasz.widera@vshn.ch" Date: Wed, 3 Apr 2024 10:43:55 +0200 Subject: [PATCH 2/6] PR suggestions + antistarvation mechanism --- pkg/cmd/spks.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/cmd/spks.go b/pkg/cmd/spks.go index 9a63004..ef93d54 100644 --- a/pkg/cmd/spks.go +++ b/pkg/cmd/spks.go @@ -55,9 +55,6 @@ func SpksCMD() *cli.Command { EnvVars: []string{"UNIT_ID"}, Destination: &UnitID, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "uom_uom_68_b1811ca1"}, }, Action: func(c *cli.Context) error { - // this function is intended to run once a day - // it will query the prometheus metrics for the last 24 hours - // it will be run via kubernetes cronjob at midnight logger := log.Logger(c.Context) logger.Info("starting spks data collector") @@ -103,8 +100,8 @@ func SpksCMD() *cli.Command { return fmt.Errorf("load loaction: %w", err) } - from := time.Now().In(location).UTC() - to := time.Now().In(location).Add(-(time.Hour * 24)).UTC() + from := time.Now().In(location).Add(-(time.Hour * 24)).UTC() + to := time.Now().In(location).UTC() billingRecords := []odoo.OdooMeteredBillingRecord{ { @@ -165,13 +162,22 @@ func SpksCMD() *cli.Command { sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt) + // ensure first data export after restart + // I want to avoid situation when data is not exported because of constant restarts + err = odooClient.SendData(billingRecords) + if err != nil { + logger.Error(err, "can't export data to Odoo") + return err + } + for loop := true; loop; { select { case <-ticker.C: + // this runs every 24 hours after program start err = odooClient.SendData(billingRecords) if err != nil { logger.Error(err, "can't export data to Odoo") - panic(err) + return err } case <-sigint: loop = false From d05964f5eae8d02a2917bd2a4af89700074f1e3f Mon Sep 17 00:00:00 2001 From: wejdross Date: Thu, 4 Apr 2024 11:46:25 +0200 Subject: [PATCH 3/6] Update pkg/cmd/spks.go Co-authored-by: Tobias Brunner --- pkg/cmd/spks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/spks.go b/pkg/cmd/spks.go index ef93d54..d8796e8 100644 --- a/pkg/cmd/spks.go +++ b/pkg/cmd/spks.go @@ -47,7 +47,7 @@ func SpksCMD() *cli.Command { EnvVars: []string{"ODOO_OAUTH_CLIENT_ID"}, Destination: &odooClientId, Required: true, DefaultText: defaultTextForRequiredFlags}, &cli.StringFlag{Name: "odoo-oauth-client-secret", Usage: "Client secret of the oauth client to interact with Odoo metered billing API", EnvVars: []string{"ODOO_OAUTH_CLIENT_SECRET"}, Destination: &odooClientSecret, Required: true, DefaultText: defaultTextForRequiredFlags}, - &cli.StringFlag{Name: "sales-order", Usage: "Sales order for APPUiO Managed clusters", + &cli.StringFlag{Name: "sales-order", Usage: "Sales order to report billing data to", EnvVars: []string{"SALES_ORDER"}, Destination: &salesOrder, Required: false, DefaultText: defaultTextForOptionalFlags, Value: "S10121"}, &cli.StringFlag{Name: "prometheus-url", Usage: "URL of the Prometheus API", EnvVars: []string{"PROMETHEUS_URL"}, Destination: &prometheusURL, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "http://prometheus-monitoring-application.monitoring-application.svc.cluster.local:9090"}, From bf633e8eafb90364a1f2d44845ac4f702ab32c78 Mon Sep 17 00:00:00 2001 From: wejdross Date: Thu, 4 Apr 2024 11:46:35 +0200 Subject: [PATCH 4/6] Update pkg/cmd/spks.go Co-authored-by: Tobias Brunner --- pkg/cmd/spks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/spks.go b/pkg/cmd/spks.go index d8796e8..8cd174d 100644 --- a/pkg/cmd/spks.go +++ b/pkg/cmd/spks.go @@ -97,7 +97,7 @@ func SpksCMD() *cli.Command { odooClient := odoo.NewOdooAPIClient(c.Context, odooURL, odooOauthTokenURL, odooClientId, odooClientSecret, logger) location, err := time.LoadLocation("Europe/Zurich") if err != nil { - return fmt.Errorf("load loaction: %w", err) + return fmt.Errorf("load location: %w", err) } from := time.Now().In(location).Add(-(time.Hour * 24)).UTC() From d9ec091b005bbbe4377498d2a93199b4e4b835f2 Mon Sep 17 00:00:00 2001 From: wejdross Date: Thu, 4 Apr 2024 11:46:43 +0200 Subject: [PATCH 5/6] Update pkg/cmd/spks.go Co-authored-by: Tobias Brunner --- pkg/cmd/spks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/spks.go b/pkg/cmd/spks.go index 8cd174d..eb66db1 100644 --- a/pkg/cmd/spks.go +++ b/pkg/cmd/spks.go @@ -51,7 +51,7 @@ func SpksCMD() *cli.Command { EnvVars: []string{"SALES_ORDER"}, Destination: &salesOrder, Required: false, DefaultText: defaultTextForOptionalFlags, Value: "S10121"}, &cli.StringFlag{Name: "prometheus-url", Usage: "URL of the Prometheus API", EnvVars: []string{"PROMETHEUS_URL"}, Destination: &prometheusURL, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "http://prometheus-monitoring-application.monitoring-application.svc.cluster.local:9090"}, - &cli.StringFlag{Name: "unit-id", Usage: "Unit ID for the consumed units", + &cli.StringFlag{Name: "unit-id", Usage: "Metered Billing UoM ID for the consumed units", EnvVars: []string{"UNIT_ID"}, Destination: &UnitID, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "uom_uom_68_b1811ca1"}, }, Action: func(c *cli.Context) error { From 545c46e9ca3775b1417e096a66f4afe39d0023e5 Mon Sep 17 00:00:00 2001 From: "lukasz.widera@vshn.ch" Date: Thu, 4 Apr 2024 13:14:22 +0200 Subject: [PATCH 6/6] Removing superfluous fields in billing structs --- pkg/cmd/spks.go | 52 +++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/pkg/cmd/spks.go b/pkg/cmd/spks.go index ef93d54..70701c6 100644 --- a/pkg/cmd/spks.go +++ b/pkg/cmd/spks.go @@ -48,11 +48,11 @@ func SpksCMD() *cli.Command { &cli.StringFlag{Name: "odoo-oauth-client-secret", Usage: "Client secret of the oauth client to interact with Odoo metered billing API", EnvVars: []string{"ODOO_OAUTH_CLIENT_SECRET"}, Destination: &odooClientSecret, Required: true, DefaultText: defaultTextForRequiredFlags}, &cli.StringFlag{Name: "sales-order", Usage: "Sales order for APPUiO Managed clusters", - EnvVars: []string{"SALES_ORDER"}, Destination: &salesOrder, Required: false, DefaultText: defaultTextForOptionalFlags, Value: "S10121"}, + EnvVars: []string{"SALES_ORDER"}, Destination: &salesOrder, DefaultText: defaultTextForOptionalFlags, Value: "S10121"}, &cli.StringFlag{Name: "prometheus-url", Usage: "URL of the Prometheus API", EnvVars: []string{"PROMETHEUS_URL"}, Destination: &prometheusURL, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "http://prometheus-monitoring-application.monitoring-application.svc.cluster.local:9090"}, &cli.StringFlag{Name: "unit-id", Usage: "Unit ID for the consumed units", - EnvVars: []string{"UNIT_ID"}, Destination: &UnitID, Required: true, DefaultText: defaultTextForRequiredFlags, Value: "uom_uom_68_b1811ca1"}, + EnvVars: []string{"UNIT_ID"}, Destination: &UnitID, DefaultText: defaultTextForRequiredFlags, Value: "uom_uom_68_b1811ca1"}, }, Action: func(c *cli.Context) error { logger := log.Logger(c.Context) @@ -105,52 +105,44 @@ func SpksCMD() *cli.Command { billingRecords := []odoo.OdooMeteredBillingRecord{ { - ProductID: "appcat-spks-mariadb-standard", - InstanceID: "mariadb-standard", - ItemDescription: "appcat-spks-mariadb-standard", - ItemGroupDescription: "SPKS", - SalesOrder: salesOrder, - UnitID: UnitID, - ConsumedUnits: float64(mariadbStandard), + ProductID: "appcat-spks-mariadb-standard", + InstanceID: "mariadb-standard", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(mariadbStandard), TimeRange: odoo.TimeRange{ From: from, To: to, }, }, { - ProductID: "appcat-spks-mariadb-premium", - InstanceID: "mariadb-premium", - ItemDescription: "appcat-spks-mariadb-premium", - ItemGroupDescription: "SPKS", - SalesOrder: salesOrder, - UnitID: UnitID, - ConsumedUnits: float64(mariadbPremium), + ProductID: "appcat-spks-mariadb-premium", + InstanceID: "mariadb-premium", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(mariadbPremium), TimeRange: odoo.TimeRange{ From: from, To: to, }, }, { - ProductID: "appcat-spks-redis-standard", - InstanceID: "redis-standard", - ItemDescription: "appcat-spks-redis-standard", - ItemGroupDescription: "SPKS", - SalesOrder: salesOrder, - UnitID: UnitID, - ConsumedUnits: float64(redisStandard), + ProductID: "appcat-spks-redis-standard", + InstanceID: "redis-standard", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(redisStandard), TimeRange: odoo.TimeRange{ From: from, To: to, }, }, { - ProductID: "appcat-spks-redis-premium", - InstanceID: "redis-premium", - ItemDescription: "appcat-spks-redis-premium", - ItemGroupDescription: "SPKS", - SalesOrder: salesOrder, - UnitID: UnitID, - ConsumedUnits: float64(redisPremium), + ProductID: "appcat-spks-redis-premium", + InstanceID: "redis-premium", + SalesOrder: salesOrder, + UnitID: UnitID, + ConsumedUnits: float64(redisPremium), TimeRange: odoo.TimeRange{ From: from, To: to,