diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..b2f8dcb --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: cloud-bulldozer + +on: + push: + branches: [ master ] + +jobs: + + build-containers: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: sudo apt-get install qemu-user-static podman fuse-overlayfs + + - name: Login in quay + run: podman login quay.io -u ${QUAY_USER} -p ${QUAY_TOKEN} + env: + QUAY_USER: ${{ secrets.QUAY_USER }} + QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }} + + - name: Build binary and push containers + run: make diff --git a/Makefile b/Makefile index 901092c..d333014 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,20 @@ -PLATFORMS = linux/ppc64le,linux/arm64,linux/amd64,linux/s390x +PLATFORMS = linux/amd64,linux/ppc64le,linux/arm64,linux/s390x ENGINE ?= podman ORG ?= cloud-bulldozer REGISTRY ?= quay.io REG = $(REGISTRY)/$(ORG) -REPOS = etcd-perf nginx +REPOS = perfapp etcd-perf nginx all: build push build: - for repo in $(REPOS); do \ - $(ENGINE) build --jobs=4 --platform=$(PLATFORMS) --manifest=$(REG)/$$repo:latest $$repo; \ + @for repo in $(REPOS); do \ + echo -e "\033[2mBuilding $$repo\033[0m"; \ + $(ENGINE) build --jobs=4 --platform=$(PLATFORMS) --manifest=$(REG)/$$repo:latest $$repo; \ done push: for repo in $(REPOS); do \ - $(ENGINE) manifest push $(REG)/$$repo:latest $(REG)/$$repo:latest; \ + echo -e "\033[2mPushing $$repo\033[0m"; \ + $(ENGINE) manifest push $(REG)/$$repo:latest $(REG)/$$repo:latest; \ done diff --git a/perfapp/Containerfile b/perfapp/Containerfile new file mode 100644 index 0000000..1fd38a6 --- /dev/null +++ b/perfapp/Containerfile @@ -0,0 +1,11 @@ +FROM registry.access.redhat.com/ubi8/ubi:latest as builder +ARG TARGETARCH +RUN dnf clean all && dnf install -y golang +COPY . /perfApp +RUN cd perfApp && GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -o perfApp cmd/perfApp/perfApp.go + +FROM registry.access.redhat.com/ubi9/ubi-minimal:latest +MAINTAINER Raúl Sevilla + +COPY --from=builder perfApp/perfApp /usr/bin/perfApp +CMD perfApp diff --git a/perfapp/Makefile b/perfapp/Makefile new file mode 100644 index 0000000..9e5f994 --- /dev/null +++ b/perfapp/Makefile @@ -0,0 +1,15 @@ +# vim: ft=make ts=4 + +SRC=$(shell find . -name *.go) +BINARY=perfApp +GO_BUILD_RECIPE:=CGO_ENABLED=0 go build +ARCHS=amd64 ppc64le arm64 s390x + +build: clean + mkdir -p build + for arch in $(ARCHS); do \ + CGO_ENABLED=0 GOOS=linux GOARCH=$$arch go build -o build/$(BINARY)-$$arch cmd/perfApp/perfApp.go; \ + done + +clean: + rm -rf build diff --git a/perfapp/README.md b/perfapp/README.md new file mode 100644 index 0000000..2a84927 --- /dev/null +++ b/perfapp/README.md @@ -0,0 +1,7 @@ +# Performance application + +## Available workloads + +- Health: Returns 200. Does not need postgres connectivity. Available at `/health` +- Ready: Inserts a timestamp record in the ts table. Available at `/ready` +- Euler aproximation: Computes an Euler number approximation and writes compute time in the euler table. Available at `/euler` diff --git a/perfapp/cmd/perfApp/perfApp.go b/perfapp/cmd/perfApp/perfApp.go new file mode 100644 index 0000000..99172a0 --- /dev/null +++ b/perfapp/cmd/perfApp/perfApp.go @@ -0,0 +1,60 @@ +package main + +import ( + "net/http" + "os" + "os/signal" + "strconv" + "syscall" + + log "github.com/sirupsen/logrus" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "ocp.performance.io/perfapp/internal/perf" + "ocp.performance.io/perfapp/pkg/euler" + "ocp.performance.io/perfapp/pkg/health" + "ocp.performance.io/perfapp/pkg/ready" + "ocp.performance.io/perfapp/pkg/utils" +) + +var tables []map[string]string + +func main() { + customFormatter := new(log.TextFormatter) + customFormatter.TimestampFormat = "2006-01-02 15:04:05" + customFormatter.FullTimestamp = true + log.SetFormatter(customFormatter) + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT) + go handleInterrupt(c) + if os.Getenv("POSTGRESQL_HOSTNAME") != "" { + go func() { + if os.Getenv("POSTGRESQL_RETRY_INTERVAL") != "" { + retryInt, err := strconv.Atoi(os.Getenv("POSTGRESQL_RETRY_INTERVAL")) + if err != nil { + utils.ErrorHandler(err) + } + perf.DB.RetryInt = retryInt + } + perf.Connect2Db() + tables = append(tables, euler.Tables, ready.Tables) + if err := perf.CreateTables(tables); err != nil { + utils.ErrorHandler(err) + } + }() + http.HandleFunc("/euler", euler.Handler) + http.HandleFunc("/ready", ready.Handler) + } + http.Handle("/metrics", promhttp.Handler()) + http.HandleFunc("/health", health.Handler) + log.Printf("Listening at 8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + utils.ErrorHandler(err) + } +} + +func handleInterrupt(c <-chan os.Signal) { + <-c + log.Println("Interrupt signal received") + os.Exit(0) +} diff --git a/perfapp/deploy/perf-app.yml b/perfapp/deploy/perf-app.yml new file mode 100644 index 0000000..01c1995 --- /dev/null +++ b/perfapp/deploy/perf-app.yml @@ -0,0 +1,150 @@ +kind: Template +apiVersion: template.openshift.io/v1 +labels: + template: perf-app +metadata: + name: perf-app +objects: +- kind: Deployment + apiVersion: apps/v1 + metadata: + name: postgres-${IDENTIFIER} + spec: + template: + metadata: + labels: + name: postgres-${IDENTIFIER} + spec: + containers: + - name: postgresql + image: registry.redhat.io/rhscl/postgresql-10-rhel7:latest + ports: + - containerPort: 5432 + protocol: TCP + env: + - name: POSTGRESQL_USER + value: ${POSTGRESQL_USER} + - name: POSTGRESQL_PASSWORD + value: ${POSTGRESQL_PASSWORD} + - name: POSTGRESQL_DATABASE + value: ${POSTGRESQL_DATABASE} + resources: {} + imagePullPolicy: Always + capabilities: {} + securityContext: + capabilities: {} + privileged: false + restartPolicy: Always + serviceAccount: '' + replicas: 1 + selector: + matchLabels: + name: postgres-${IDENTIFIER} + triggers: + - type: ConfigChange + strategy: + type: RollingUpdate +- kind: Deployment + apiVersion: apps/v1 + metadata: + name: perfapp-${IDENTIFIER} + spec: + template: + metadata: + labels: + name: perfapp-${IDENTIFIER} + spec: + containers: + - name: perfapp + image: quay.io/cloud-bulldozer/perfapp:latest + readinessProbe: + httpGet: + path: ${LIVENESS_ENDPOINT} + port: 8080 + periodSeconds: 30 + failureThreshold: 1 + timeoutSeconds: 60 + initialDelaySeconds: 30 + ports: + - containerPort: 8080 + protocol: TCP + env: + - name: POSTGRESQL_USER + value: ${POSTGRESQL_USER} + - name: POSTGRESQL_PASSWORD + value: ${POSTGRESQL_PASSWORD} + - name: POSTGRESQL_DATABASE + value: ${POSTGRESQL_DATABASE} + - name: POSTGRESQL_HOSTNAME + value: postgresql-${IDENTIFIER} + - name: POSTGRESQL_PORT + value: '5432' + - name: POSTGRESQL_RETRY_INTERVAL + value: ${POSTGRESQL_RETRY_INTERVAL} + resources: {} + imagePullPolicy: Always + capabilities: {} + securityContext: + capabilities: {} + privileged: false + restartPolicy: Always + serviceAccount: '' + replicas: 1 + selector: + matchLabels: + name: perfapp-${IDENTIFIER} + triggers: + - type: ConfigChange + strategy: + type: RollingUpdate +- kind: Service + apiVersion: v1 + metadata: + name: postgresql-${IDENTIFIER} + spec: + selector: + name: postgres-${IDENTIFIER} + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 + portalIP: '' + type: ClusterIP + sessionAffinity: None + status: + loadBalancer: {} +- kind: Service + apiVersion: v1 + metadata: + name: perfapp-${IDENTIFIER} + spec: + selector: + name: perfapp-${IDENTIFIER} + ports: + - protocol: TCP + port: 8080 + targetPort: 8080 + portalIP: '' + type: ClusterIP + sessionAffinity: None + status: + loadBalancer: {} +parameters: +- name: IDENTIFIER + description: Number to append to the name of resources + value: '1' +- name: POSTGRESQL_USER + description: Postgresql database username + value: 'admin' +- name: POSTGRESQL_PASSWORD + description: Postgresql database password + value: 'secret' +- name: POSTGRESQL_DATABASE + description: Postgresql database name + value: 'mydb' +- name: POSTGRESQL_RETRY_INTERVAL + description: Postgresql connection retry interval + value: '5' +- name: LIVENESS_ENDPOINT + description: Liveness probe endpoint + value: '/ready' diff --git a/perfapp/go.mod b/perfapp/go.mod new file mode 100644 index 0000000..53038d5 --- /dev/null +++ b/perfapp/go.mod @@ -0,0 +1,21 @@ +module ocp.performance.io/perfapp + +go 1.20 + +require ( + github.com/lib/pq v1.3.0 + github.com/prometheus/client_golang v1.5.1 + github.com/sirupsen/logrus v1.4.2 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/golang/protobuf v1.3.2 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.9.1 // indirect + github.com/prometheus/procfs v0.0.8 // indirect + golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 // indirect +) diff --git a/perfapp/go.sum b/perfapp/go.sum new file mode 100644 index 0000000..0378d21 --- /dev/null +++ b/perfapp/go.sum @@ -0,0 +1,96 @@ +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/perfapp/hack/ClusterLoader.sh b/perfapp/hack/ClusterLoader.sh new file mode 100755 index 0000000..c3bfbe0 --- /dev/null +++ b/perfapp/hack/ClusterLoader.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -x + +ENGINE=podman +CL=quay.io/openshift/origin-tests:4.3 + +${ENGINE} run --rm -v ../deploy:/deploy:z -v ${KUBECONFIG:-~/.kube/config}:/root/.kube/config:z --rm -it ${CL} /bin/bash -c 'KUBECONFIG=/root/.kube/config VIPERCONFIG=/deploy/clusterloader.yml openshift-tests run-test "[Feature:Performance][Serial][Slow] Load cluster should load the cluster [Suite:openshift]"' diff --git a/perfapp/hack/load.sh b/perfapp/hack/load.sh new file mode 100755 index 0000000..917a9a5 --- /dev/null +++ b/perfapp/hack/load.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +COPIES=${1:-40} + +for i in $(seq ${COPIES}); do + echo $i + oc process -f ../deploy/perf-app.yml IDENTIFIER=${i} | oc apply -f - +done diff --git a/perfapp/hack/setenv.sh b/perfapp/hack/setenv.sh new file mode 100755 index 0000000..477e483 --- /dev/null +++ b/perfapp/hack/setenv.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +export POSTGRESQL_USER=admin +export POSTGRESQL_PASSWORD=secret +export POSTGRESQL_HOSTNAME=localhost +export POSTGRESQL_PORT=5432 +export POSTGRESQL_DATABASE=mydb diff --git a/perfapp/internal/perf/db.go b/perfapp/internal/perf/db.go new file mode 100644 index 0000000..aa5b2c8 --- /dev/null +++ b/perfapp/internal/perf/db.go @@ -0,0 +1,81 @@ +package perf + +import ( + "database/sql" + "fmt" + + log "github.com/sirupsen/logrus" + + "os" + "time" + + _ "github.com/lib/pq" +) + +// DBInfo Database connection information +type DBInfo struct { + DBUser string + DBPassword string + DBHost string + DBPort string + DBName string + RetryInt int + conn *sql.DB +} + +// DB DBInfo instance +var DB DBInfo = DBInfo{ + DBUser: os.Getenv("POSTGRESQL_USER"), + DBPassword: os.Getenv("POSTGRESQL_PASSWORD"), + DBHost: os.Getenv("POSTGRESQL_HOSTNAME"), + DBPort: os.Getenv("POSTGRESQL_PORT"), + DBName: os.Getenv("POSTGRESQL_DATABASE"), + RetryInt: 5, +} + +const dbTImeout = 10 + +// Connect2Db Connects to a Postgres database using DBInfo +func Connect2Db() { + if DB.DBPort == "" { + DB.DBPort = "5432" + } + connStr := fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=disable connect_timeout=%d", DB.DBUser, DB.DBPassword, DB.DBHost, DB.DBPort, DB.DBName, dbTImeout) + for { + log.Infof("Connecting with database %s:%s", DB.DBHost, DB.DBPort) + DB.conn, _ = sql.Open("postgres", connStr) + if err := DB.conn.Ping(); err != nil { + log.Warnln(err) + log.Warnf("Retrying connection with %s:%s in %d seconds", DB.DBHost, DB.DBPort, DB.RetryInt) + time.Sleep(time.Duration(DB.RetryInt) * time.Second) + continue + } + break + } + log.Println("Database connection successfully established") +} + +// QueryDB Performs a query on the database +func QueryDB(query string) error { + // Verify database connection by pinging database + if err := DB.conn.Ping(); err != nil { + return err + } + if _, err := DB.conn.Exec(query); err != nil { + return err + } + return nil +} + +// CreateTables Creates all tables at tableList +func CreateTables(tableList []map[string]string) error { + for k := range tableList { + for t, q := range tableList[k] { + log.Infof("Creating table %s: %s", t, q) + if err := QueryDB(q); err != nil { + return err + } + } + } + return nil +} diff --git a/perfapp/internal/perf/metrics.go b/perfapp/internal/perf/metrics.go new file mode 100644 index 0000000..d89d566 --- /dev/null +++ b/perfapp/internal/perf/metrics.go @@ -0,0 +1,12 @@ +package perf + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +// HTTPRequestDuration HTTP request duration in seconds +var HTTPRequestDuration prometheus.Summary = promauto.NewSummary(prometheus.SummaryOpts{ + Name: "http_request_duration_seconds", + Help: "Histogram of the HTTP requests duration", +}) diff --git a/perfapp/pkg/euler/euler.go b/perfapp/pkg/euler/euler.go new file mode 100644 index 0000000..dfea393 --- /dev/null +++ b/perfapp/pkg/euler/euler.go @@ -0,0 +1,39 @@ +package euler + +import ( + "fmt" + "math" + "net/http" + "time" + + "ocp.performance.io/perfapp/internal/perf" + log "github.com/sirupsen/logrus" +) + +// Tables Euler workload required tables +var Tables = map[string]string{"euler": "CREATE TABLE IF NOT EXISTS euler (date TIMESTAMP, elapsed FLOAT(24))"} + +// Handler Handle requests to compute euler number aproximation +func Handler(w http.ResponseWriter, r *http.Request) { + log.Println("Computing euler approximation") + now := time.Now() + calcEuler() + insert := fmt.Sprintf("INSERT INTO euler VALUES ('%s', '%f')", now.Format(time.RFC3339), time.Since(now).Seconds()) + if err := perf.QueryDB(insert); err != nil { + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintln(w, err.Error()) + } else { + fmt.Fprintln(w, "Ok") + log.Printf("Euler approximation computed in %f seconds", time.Since(now).Seconds()) + perf.HTTPRequestDuration.Observe(time.Since(now).Seconds()) + } +} + +func calcEuler() { + var n float64 + var x float64 + for math.E > x { + x = math.Pow((1 + 1/n), n) + n++ + } +} diff --git a/perfapp/pkg/health/health.go b/perfapp/pkg/health/health.go new file mode 100644 index 0000000..32cbc6f --- /dev/null +++ b/perfapp/pkg/health/health.go @@ -0,0 +1,14 @@ +package health + +import ( + "net/http" + + log "github.com/sirupsen/logrus" +) + +// Handler Handles health endpoint +func Handler(w http.ResponseWriter, r *http.Request) { + log.Info("Returning value for health endpoint") + w.WriteHeader(200) + w.Write([]byte("ok")) +} diff --git a/perfapp/pkg/ready/timestamp.go b/perfapp/pkg/ready/timestamp.go new file mode 100644 index 0000000..15d8fc8 --- /dev/null +++ b/perfapp/pkg/ready/timestamp.go @@ -0,0 +1,28 @@ +package ready + +import ( + "fmt" + "net/http" + "time" + + "ocp.performance.io/perfapp/internal/perf" + log "github.com/sirupsen/logrus" +) + +// Tables Euler workload required tables +var Tables = map[string]string{"ts": "CREATE TABLE IF NOT EXISTS ts (date TIMESTAMP)"} + +// Handler Handle timestamp requests +func Handler(w http.ResponseWriter, r *http.Request) { + log.Info("Inserting timestamp record in table") + now := time.Now() + insert := fmt.Sprintf("INSERT INTO ts VALUES ('%s')", now.Format(time.RFC3339)) + if err := perf.QueryDB(insert); err != nil { + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintln(w, err.Error()) + } else { + fmt.Fprintln(w, "Ok") + log.Printf("Timestamp inserted in %v ns", time.Since(now).Nanoseconds()) + perf.HTTPRequestDuration.Observe(time.Since(now).Seconds()) + } +} diff --git a/perfapp/pkg/utils/utils.go b/perfapp/pkg/utils/utils.go new file mode 100644 index 0000000..2440717 --- /dev/null +++ b/perfapp/pkg/utils/utils.go @@ -0,0 +1,9 @@ +package utils + +import ( + log "github.com/sirupsen/logrus" +) + +func ErrorHandler(msg error) { + log.Fatalln(msg.Error()) +}