diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000..904e2dd --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,32 @@ +- job: + name: rally_exporter:image:build + parent: vexxhost-build-docker-image + provides: rally_exporter:image + vars: &rally_exporter_images + docker_images: + - context: . + repository: vexxhost/rally-exporter + +- job: + name: rally_exporter:image:upload + parent: vexxhost-upload-docker-image + provides: rally_exporter:image + vars: *rally_exporter_images + +- job: + name: rally_exporter:image:promote + parent: vexxhost-promote-docker-image + vars: *rally_exporter_images + +- project: + check: + jobs: + - golangci-lint + - rally_exporter:image:build + gate: + jobs: + - golangci-lint + - rally_exporter:image:upload + promote: + jobs: + - rally_exporter:image:promote diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..246a3f5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM golang:1.13-stretch AS builder +WORKDIR /go/src/app +COPY . . +RUN go build + +FROM xrally/xrally-openstack:1.7.0 +COPY --from=builder /go/src/app/rally-exporter /rally-exporter +ENTRYPOINT ["/rally-exporter"] \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d4d042a --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module opendev.org/vexxhost/rally-exporter + +go 1.13 + +require ( + github.com/DATA-DOG/go-sqlmock v1.4.1 + github.com/gophercloud/utils v0.0.0-20191212191830-4533a07bd492 + github.com/jinzhu/gorm v1.9.11 + github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 + github.com/prometheus/common v0.2.0 + github.com/stretchr/testify v1.5.1 + gopkg.in/alecthomas/kingpin.v2 v2.2.6 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7ecd2bb --- /dev/null +++ b/go.sum @@ -0,0 +1,154 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= +github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gophercloud/gophercloud v0.0.0-20190212181753-892256c46858 h1:QBK4YvYPJGsghVtVJcEZcQ70UNGFnWUCjtv2NTcEFHA= +github.com/gophercloud/gophercloud v0.0.0-20190212181753-892256c46858/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gophercloud/utils v0.0.0-20191212191830-4533a07bd492 h1:NAwq2GgRiqbNLw1cA7KUdt7lDR/NzJtk4EXGxO3gqas= +github.com/gophercloud/utils v0.0.0-20191212191830-4533a07bd492/go.mod h1:SZ9FTKibIotDtCrxAU/evccoyu1yhKST6hgBvwTB5Eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE= +github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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 v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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/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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/models/common.go b/models/common.go new file mode 100644 index 0000000..e159378 --- /dev/null +++ b/models/common.go @@ -0,0 +1,10 @@ +package models + +import ( + "time" +) + +type Model struct { + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/models/environments.go b/models/environments.go new file mode 100644 index 0000000..0092c4c --- /dev/null +++ b/models/environments.go @@ -0,0 +1,14 @@ +package models + +type Environment struct { + Model + + ID uint `gorm:"primary_key;not null"` + UUID string `gorm:"type:varchar(36);not null"` + Name string `gorm:"type:varchar(255);not null"` + Description string `gorm:"type:text"` + Status string `gorm:"type:varchar(36);not null"` + Extras string `gorm:"type:text"` + Config string `gorm:"type:text"` + Spec string `gorm:"type:text"` +} diff --git a/models/platforms.go b/models/platforms.go new file mode 100644 index 0000000..f6cdf0a --- /dev/null +++ b/models/platforms.go @@ -0,0 +1,15 @@ +package models + +type Platform struct { + Model + + ID uint `gorm:"primary_key;not null"` + UUID string `gorm:"type:varchar(36);not null"` + EnvUUID string `gorm:"type:varchar(36);not null"` + Status string `gorm:"type:varchar(36);not null"` + PluginName string `gorm:"type:varchar(36);not null"` + PluginSpec string `gorm:"type:text;not null"` + PluginData string `gorm:"type:text"` + PlatformName string `gorm:"type:varchar(36)"` + PlatformData string `gorm:"type:text"` +} diff --git a/models/tasks.go b/models/tasks.go new file mode 100644 index 0000000..b7f3370 --- /dev/null +++ b/models/tasks.go @@ -0,0 +1,34 @@ +package models + +type Task struct { + Model + + ID uint `gorm:"primary_key;not null"` + UUID string `gorm:"type:varchar(36);not null"` + EnvUUID string `gorm:"type:varchar(36);not null"` + InputTask string `gorm:"type:text"` + Title string `gorm:"type:varchar(128)"` + Description string `gorm:"type:text"` + ValidationResult string `gorm:"type:text;not null"` + ValidationDuration float64 `gorm:"type:float"` + TaskDuration float64 `gorm:"type:float"` + PassSLA bool + Status string `gorm:"type:varchar(36)"` +} + +type Subtask struct { + Model + + ID uint `gorm:"primary_key;not null"` + UUID string `gorm:"type:varchar(36);not null"` + TaskUUID string `gorm:"type:varchar(36);not null"` + Title string `gorm:"type:varchar(128)"` + Description string `gorm:"type:text"` + Contexts string `gorm:"type:text;not null"` + ContextsResults string `gorm:"type:text;not null"` + SLA string `gorm:"type:text;not null"` + RunInParallel bool `gorm:"not null"` + Duration float64 `gorm:"type:float"` + PassSLA bool + Status string `gorm:"type:varchar(36)"` +} diff --git a/rally/deployment.go b/rally/deployment.go new file mode 100644 index 0000000..cf09a47 --- /dev/null +++ b/rally/deployment.go @@ -0,0 +1,20 @@ +package rally + +type OpenStackUser struct { + Username string `json:"username"` + Password string `json:"password"` + UserDomainName string `json:"user_domain_name"` + ProjectName string `json:"project_name"` + ProjectDomainName string `json:"project_domain_name"` +} + +type OpenStackDeployment struct { + AuthURL string `json:"auth_url"` + RegionName string `json:"region_name"` + EndpointType string `json:"endpoint_type"` + Users []OpenStackUser `json:"users"` +} + +type Deployment struct { + OpenStackDeployment `json:"openstack"` +} diff --git a/rally/runner.go b/rally/runner.go new file mode 100644 index 0000000..c0e5f1c --- /dev/null +++ b/rally/runner.go @@ -0,0 +1,179 @@ +package rally + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/exec" + "time" + + "github.com/gophercloud/utils/openstack/clientconfig" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" + + "opendev.org/vexxhost/rally-exporter/models" +) + +// TaskLabels has the common labels used by every task. +var TaskLabels = []string{"title"} + +// PeriodicRunner defines the structure of the collector +type PeriodicRunner struct { + prometheus.Collector + + CloudName string + TaskFile string + + TaskDuration *prometheus.Desc + TaskSLADesc *prometheus.Desc + TaskTime *prometheus.Desc +} + +// NewPeriodicRunner creates a rally runner which executes every 5 minutes +func NewPeriodicRunner(cloudName string, taskFile string) *PeriodicRunner { + return &PeriodicRunner{ + CloudName: cloudName, + TaskFile: taskFile, + TaskDuration: prometheus.NewDesc( + "rally_task_duration", + "Rally task duration", + TaskLabels, nil, + ), + TaskSLADesc: prometheus.NewDesc( + "rally_task_passed", + "Rally task passed", + TaskLabels, nil, + ), + TaskTime: prometheus.NewDesc( + "rally_task_time", + "Rally last run time", + []string{}, nil, + ), + } +} + +// Run starts the PeriodicRunner which runs Rally every 5 minutes. +func (runner *PeriodicRunner) Run() { + runner.createDeployment() + + for { + log.Info("Starting Rally run...") + cmd := exec.Command("rally", "task", "start", runner.TaskFile) + output, err := cmd.CombinedOutput() + if err != nil { + log.Error("Failed test, output below:") + } else { + log.Info("Successful run, output below:") + } + fmt.Println(string(output)) + + time.Sleep(5 * time.Minute) + } +} + +func (runner *PeriodicRunner) createDeployment() { + opts := &clientconfig.ClientOpts{Cloud: runner.CloudName} + cloud, err := clientconfig.GetCloudFromYAML(opts) + if err != nil { + log.Fatal(err) + } + + deployment := &Deployment{ + OpenStackDeployment: OpenStackDeployment{ + AuthURL: cloud.AuthInfo.AuthURL, + RegionName: cloud.RegionName, + EndpointType: cloud.EndpointType, + Users: []OpenStackUser{ + OpenStackUser{ + Username: cloud.AuthInfo.Username, + Password: cloud.AuthInfo.Password, + UserDomainName: cloud.AuthInfo.UserDomainName, + ProjectName: cloud.AuthInfo.ProjectName, + ProjectDomainName: cloud.AuthInfo.ProjectDomainName, + }, + }, + }, + } + + b, err := json.Marshal(deployment) + if err != nil { + log.Fatal(err) + } + + file, err := ioutil.TempFile("/tmp", "deployment") + if err != nil { + log.Fatal(err) + } + defer os.Remove(file.Name()) + _, err = file.Write(b) + if err != nil { + log.Fatal(err) + } + + cmd := exec.Command("rally", "deployment", "create", "--filename", file.Name(), "--name", runner.CloudName) + _, err = cmd.Output() + if err != nil { + log.Fatal("Failed to install Rally deployment: ", err) + } +} + +// Describe provides all the descriptions of all the information for the collector +func (runner *PeriodicRunner) Describe(ch chan<- *prometheus.Desc) { + ch <- runner.TaskDuration + ch <- runner.TaskSLADesc + ch <- runner.TaskTime +} + +func getLatestTask(db *gorm.DB) (*models.Task, error) { + task := &models.Task{} + err := db.Not("status", []string{"running"}).Last(task).Error + + return task, err +} + +// Collect grabs all the data from the Rally database +func (runner *PeriodicRunner) Collect(ch chan<- prometheus.Metric) { + db, err := gorm.Open("sqlite3", "/home/rally/data/rally.db") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + task, err := getLatestTask(db) + if err != nil { + log.Error(err) + return + } + + subtasks := []models.Subtask{} + if err := db.Where(&models.Subtask{TaskUUID: task.UUID}).Find(&subtasks).Error; err != nil { + log.Fatal(err) + } + + for _, subtask := range subtasks { + ch <- prometheus.MustNewConstMetric( + runner.TaskDuration, + prometheus.GaugeValue, + subtask.Duration, subtask.Title, + ) + + passSLA := float64(0) + if subtask.PassSLA && subtask.Status == "finished" { + passSLA = float64(1) + } + ch <- prometheus.MustNewConstMetric( + runner.TaskSLADesc, + prometheus.GaugeValue, + passSLA, subtask.Title, + ) + } + + ch <- prometheus.MustNewConstMetric( + runner.TaskTime, + prometheus.GaugeValue, + float64(task.UpdatedAt.Unix()), + ) +} diff --git a/rally/runner_test.go b/rally/runner_test.go new file mode 100644 index 0000000..b4af09d --- /dev/null +++ b/rally/runner_test.go @@ -0,0 +1,57 @@ +package rally + +import ( + "regexp" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/jinzhu/gorm" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type RunnerSuite struct { + suite.Suite + DB *gorm.DB + mock sqlmock.Sqlmock +} + +func (s *RunnerSuite) SetupSuite() { + db, mock, err := sqlmock.New() + require.NoError(s.T(), err) + + s.DB, err = gorm.Open("sqlite3", db) + require.NoError(s.T(), err) + + s.DB.LogMode(true) + s.mock = mock +} + +func (s *RunnerSuite) TestGetLatestTaskWithNoRecords() { + s.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "tasks" WHERE ("tasks"."status" NOT IN (?)) ORDER BY "tasks"."id" DESC LIMIT 1`)). + WithArgs("running"). + WillReturnRows( + sqlmock.NewRows([]string{"id", "status"}), + ) + + _, err := getLatestTask(s.DB) + require.Equal(s.T(), err, gorm.ErrRecordNotFound) +} + +func (s *RunnerSuite) TestGetLatestTask() { + s.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "tasks" WHERE ("tasks"."status" NOT IN (?)) ORDER BY "tasks"."id" DESC LIMIT 1`)). + WithArgs("running"). + WillReturnRows( + sqlmock.NewRows([]string{"id", "status"}). + AddRow(1, "finished"), + ) + + task, err := getLatestTask(s.DB) + require.NoError(s.T(), err) + require.Equal(s.T(), "finished", task.Status) +} +func TestRunnerSuite(t *testing.T) { + suite.Run(t, new(RunnerSuite)) +} diff --git a/rally_exporter.go b/rally_exporter.go new file mode 100644 index 0000000..6883491 --- /dev/null +++ b/rally_exporter.go @@ -0,0 +1,60 @@ +package main + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/log" + "github.com/prometheus/common/version" + "gopkg.in/alecthomas/kingpin.v2" + + "opendev.org/vexxhost/rally-exporter/rally" +) + +func main() { + var ( + listenAddress = kingpin.Flag( + "web.listen-address", + "Address on which to expose metrics and web interface.", + ).Default(":9355").String() + metricsPath = kingpin.Flag( + "web.telemetry-path", + "Path under which to expose metrics.", + ).Default("/metrics").String() + cloud = kingpin.Arg( + "cloud", + "Name of the cloud from clouds.yaml", + ).Required().String() + task = kingpin.Arg( + "file", + "Name of the task file", + ).Required().String() + ) + + kingpin.Version(version.Print("rally-exporter")) + kingpin.HelpFlag.Short('h') + kingpin.Parse() + + runner := rally.NewPeriodicRunner(*cloud, *task) + prometheus.MustRegister(runner) + + go runner.Run() + + http.Handle("/metrics", promhttp.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + _, err := w.Write([]byte(` + Rally Exporter + +

Rally Exporter

+

Metrics

+ + `)) + if err != nil { + log.Error(err) + } + }) + + log.Infoln("Listening on", *listenAddress) + log.Fatal(http.ListenAndServe(*listenAddress, nil)) +}