From a83b7f64a08b2ad5c10dcf8807ab39dfcf6d714d Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 11:20:20 -0500 Subject: [PATCH 01/76] tests if docker gets built in gitlab --- .gitlab/.gitlab-ci.yml | 21 +++++++++++++++- .gitlab/Dockerfile | 56 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 .gitlab/Dockerfile diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 0e41bfd1e..cbb7eeffb 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -3,6 +3,9 @@ include: - template: Security/License-Scanning.gitlab-ci.yml - template: Security/Dependency-Scanning.gitlab-ci.yml +variables: + GO_VERSION: 1.16.3 + stages: - test - build @@ -121,4 +124,20 @@ build for darwin arm64: - go build -o featurebase_darwin_arm64 ./cmd/featurebase artifacts: paths: - - featurebase_darwin_arm64 \ No newline at end of file + - featurebase_darwin_arm64 + +# Build a Docker image with CI/CD and push to the GitLab registry. +build container fb: + image: docker:latest + stage: build + services: + - docker:dind + script: + - | + docker build + --build-arg GO_VERSION=$(GO_VERSION) + --tag featurebase:$(VERSION) . + - docker push "$CI_REGISTRY_IMAGE${tag}" + - @echo Created docker image: featurebase:$(VERSION) + + diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile new file mode 100644 index 000000000..115855034 --- /dev/null +++ b/.gitlab/Dockerfile @@ -0,0 +1,56 @@ +ARG GO_VERSION=latest + +####################### +### Lattice builder ### +####################### + +FROM moleculacorp/nodejs:latest as lattice-builder +WORKDIR /lattice + +COPY lattice/package.json ./ +COPY lattice/yarn.lock ./ +RUN yarn install + +COPY lattice ./ +RUN yarn build + +###################### +### Pilosa builder ### +###################### + +FROM golang:${GO_VERSION} as pilosa-builder +ARG MAKE_FLAGS +WORKDIR /pilosa + +RUN go get github.com/rakyll/statik + +COPY . ./ +COPY --from=lattice-builder /lattice/build /lattice +RUN /go/bin/statik -src=/lattice -dest=/pilosa + +RUN make build FLAGS="-o build/featurebase" ${MAKE_FLAGS} + +##################### +### Pilosa runner ### +##################### + +FROM alpine:3.13.2 as runner + +LABEL maintainer "dev@molecula.com" + +RUN apk add --no-cache curl jq + +COPY --from=pilosa-builder /pilosa/build/featurebase / + +COPY LICENSE /LICENSE +COPY NOTICE /NOTICE + +EXPOSE 10101 +VOLUME /data + +ENV PILOSA_DATA_DIR /data +ENV PILOSA_BIND 0.0.0.0:10101 +ENV PILOSA_BIND_GRPC 0.0.0.0:20101 + +ENTRYPOINT ["/featurebase"] +CMD ["server"] From aa0d5040e542eeac9691887cdbd7e4ee89ab57dc Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:21:05 -0500 Subject: [PATCH 02/76] remove @ from yaml --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index cbb7eeffb..c4142bbbe 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -138,6 +138,6 @@ build container fb: --build-arg GO_VERSION=$(GO_VERSION) --tag featurebase:$(VERSION) . - docker push "$CI_REGISTRY_IMAGE${tag}" - - @echo Created docker image: featurebase:$(VERSION) + - echo Created docker image: featurebase:$(VERSION) From e2f3a645c169b6ca6992fe9d2ff1f0d37cef5782 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:28:26 -0500 Subject: [PATCH 03/76] try to fix yml syntax --- .gitlab/.gitlab-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index c4142bbbe..5797b8978 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -133,8 +133,7 @@ build container fb: services: - docker:dind script: - - | - docker build + - docker build --build-arg GO_VERSION=$(GO_VERSION) --tag featurebase:$(VERSION) . - docker push "$CI_REGISTRY_IMAGE${tag}" From 9e2992424de0e9d94c291d99bf0cd8e326bdfa44 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:41:38 -0500 Subject: [PATCH 04/76] same --- .gitlab/.gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 5797b8978..b7ad25eaa 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -133,9 +133,7 @@ build container fb: services: - docker:dind script: - - docker build - --build-arg GO_VERSION=$(GO_VERSION) - --tag featurebase:$(VERSION) . + - docker build --build-arg GO_VERSION=$(GO_VERSION) --tag featurebase:$(VERSION) . - docker push "$CI_REGISTRY_IMAGE${tag}" - echo Created docker image: featurebase:$(VERSION) From 86c45fba1c2a02d0ee80289ccefdc7085993508f Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:49:49 -0500 Subject: [PATCH 05/76] same --- .gitlab/.gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index b7ad25eaa..6286e7b27 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -133,8 +133,8 @@ build container fb: services: - docker:dind script: - - docker build --build-arg GO_VERSION=$(GO_VERSION) --tag featurebase:$(VERSION) . + - docker build --build-arg GO_VERSION=$(GO_VERSION) --tag featurebase:test . - docker push "$CI_REGISTRY_IMAGE${tag}" - - echo Created docker image: featurebase:$(VERSION) + - echo 'Created docker image featurebase:test' From 93a5cab3fcdbf8a85c33bbb309caa8769b30bce8 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 13:05:14 -0500 Subject: [PATCH 06/76] same --- .gitlab/.gitlab-ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 6286e7b27..e1b4d9048 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -133,7 +133,15 @@ build container fb: services: - docker:dind script: - - docker build --build-arg GO_VERSION=$(GO_VERSION) --tag featurebase:test . + - | + if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then + tag="" + echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'" + else + tag=":$CI_COMMIT_REF_SLUG" + echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag" + fi + - docker build --build-arg GO_VERSION=$GO_VERSION -t "$CI_REGISTRY_IMAGE${tag}" . - docker push "$CI_REGISTRY_IMAGE${tag}" - echo 'Created docker image featurebase:test' From 35dc4aa07a7286ee3cae5f3481134700c22ee10c Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 13:47:29 -0500 Subject: [PATCH 07/76] same2 --- .gitlab/.gitlab-ci.yml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index e1b4d9048..23d1ef7a5 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -4,7 +4,7 @@ include: - template: Security/Dependency-Scanning.gitlab-ci.yml variables: - GO_VERSION: 1.16.3 + GOVERSION: 1.16.3 stages: - test @@ -133,16 +133,9 @@ build container fb: services: - docker:dind script: - - | - if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then - tag="" - echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'" - else - tag=":$CI_COMMIT_REF_SLUG" - echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag" - fi - - docker build --build-arg GO_VERSION=$GO_VERSION -t "$CI_REGISTRY_IMAGE${tag}" . - - docker push "$CI_REGISTRY_IMAGE${tag}" - - echo 'Created docker image featurebase:test' + - tag = "'$CI_REGISTRY_IMAGE':'$CI_COMMIT_SHORT_SHA'" + - docker build --build-arg GO_VERSION=$GOVERSION -t "$tag" . + - docker push "$tag" + - echo "Created docker featurebase image with tag '$tag'" From bde29ec792a85a6cec82cebb1ea0abcd1a4b3305 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:01:05 -0500 Subject: [PATCH 08/76] same3 --- .gitlab/.gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 23d1ef7a5..63865b1da 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -133,9 +133,9 @@ build container fb: services: - docker:dind script: - - tag = "'$CI_REGISTRY_IMAGE':'$CI_COMMIT_SHORT_SHA'" - - docker build --build-arg GO_VERSION=$GOVERSION -t "$tag" . - - docker push "$tag" - - echo "Created docker featurebase image with tag '$tag'" + - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA + - docker build --build-arg GO_VERSION=$GOVERSION -t $tag . + - docker push $tag + - echo Created docker featurebase image with tag "$tag" From 5b0244e228b84403b59118c00294e41ce8930837 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:15:07 -0500 Subject: [PATCH 09/76] same4 --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 63865b1da..e71c3e200 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -4,7 +4,7 @@ include: - template: Security/Dependency-Scanning.gitlab-ci.yml variables: - GOVERSION: 1.16.3 + GOVERSION: "1.16.3" stages: - test From 554d3f1e356585e7f87931e5a5170e08f2db188f Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 14:34:24 -0500 Subject: [PATCH 10/76] same5 --- .gitlab/.gitlab-ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index e71c3e200..ab5a5c5bf 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -128,10 +128,16 @@ build for darwin arm64: # Build a Docker image with CI/CD and push to the GitLab registry. build container fb: - image: docker:latest + image: docker:stable stage: build services: - docker:dind + variables: + DOCKER_HOST: tcp://docker:2375/ + DOCKER_DRIVER: overlay2 + before_script: + - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY + - docker info script: - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag . From 225b4a7a584d1f1be572e3c48931653e828cd22e Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:17:23 -0500 Subject: [PATCH 11/76] try with shell runner instead of dind --- .gitlab/.gitlab-ci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index ab5a5c5bf..f8deb9dc1 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -130,13 +130,9 @@ build for darwin arm64: build container fb: image: docker:stable stage: build - services: - - docker:dind - variables: - DOCKER_HOST: tcp://docker:2375/ - DOCKER_DRIVER: overlay2 + tags: + - shell before_script: - - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker info script: - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA From 686f3a868467e4499551009c53efec2569405b9a Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:23:20 -0500 Subject: [PATCH 12/76] remove lattice from dockerfile --- .gitlab/Dockerfile | 46 +++++++--------------------------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index 115855034..81a8f989a 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -1,50 +1,18 @@ -ARG GO_VERSION=latest - -####################### -### Lattice builder ### -####################### - -FROM moleculacorp/nodejs:latest as lattice-builder -WORKDIR /lattice - -COPY lattice/package.json ./ -COPY lattice/yarn.lock ./ -RUN yarn install - -COPY lattice ./ -RUN yarn build - -###################### -### Pilosa builder ### -###################### - -FROM golang:${GO_VERSION} as pilosa-builder -ARG MAKE_FLAGS -WORKDIR /pilosa - -RUN go get github.com/rakyll/statik - -COPY . ./ -COPY --from=lattice-builder /lattice/build /lattice -RUN /go/bin/statik -src=/lattice -dest=/pilosa - -RUN make build FLAGS="-o build/featurebase" ${MAKE_FLAGS} - -##################### -### Pilosa runner ### -##################### - -FROM alpine:3.13.2 as runner +FROM alpine:3.14.2 LABEL maintainer "dev@molecula.com" +LABEL org.opencontainers.image.authors="dev@molecula.com" RUN apk add --no-cache curl jq -COPY --from=pilosa-builder /pilosa/build/featurebase / - COPY LICENSE /LICENSE COPY NOTICE /NOTICE +ARG SHORT_SHA +COPY .cloudbuild/featurebase_linux_amd64 / + +RUN mv /featurebase_linux_amd64 /featurebase +RUN chmod ugo+x /featurebase EXPOSE 10101 VOLUME /data From 34a04d276e60775085fa933ec11ef998fe5dff9c Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:38:07 -0500 Subject: [PATCH 13/76] change path to bin --- .gitlab/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index 81a8f989a..fda808fa3 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -9,7 +9,7 @@ COPY LICENSE /LICENSE COPY NOTICE /NOTICE ARG SHORT_SHA -COPY .cloudbuild/featurebase_linux_amd64 / +COPY .gitlab/featurebase_linux_amd64 / RUN mv /featurebase_linux_amd64 /featurebase RUN chmod ugo+x /featurebase From dd56bdf645e4a01280d06219138e76584afc29e5 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:50:35 -0500 Subject: [PATCH 14/76] runs after linux arm64 build --- .gitlab/.gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index f8deb9dc1..524ae1fbc 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -130,6 +130,8 @@ build for darwin arm64: build container fb: image: docker:stable stage: build + needs: + - "build for linux arm64" tags: - shell before_script: From 00b6c2ff8aa1b77a962bc85ba7a6d64602438e80 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:59:03 -0500 Subject: [PATCH 15/76] change dockerfile path --- .gitlab/.gitlab-ci.yml | 72 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 524ae1fbc..70f941f9c 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -7,7 +7,7 @@ variables: GOVERSION: "1.16.3" stages: - - test + # - test - build install lattice: @@ -90,55 +90,55 @@ build for linux amd64: paths: - featurebase_linux_amd64 -build for linux arm64: - stage: build - image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 - variables: - GOOS: "linux" - GOARCH: "arm64" - script: - - go build -o featurebase_linux_arm64 ./cmd/featurebase - artifacts: - paths: - - featurebase_linux_arm64 +# build for linux arm64: +# stage: build +# image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 +# variables: +# GOOS: "linux" +# GOARCH: "arm64" +# script: +# - go build -o featurebase_linux_arm64 ./cmd/featurebase +# artifacts: +# paths: +# - featurebase_linux_arm64 -build for darwin amd64: - stage: build - image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 - variables: - GOOS: "darwin" - GOARCH: "amd64" - script: - - go build -o featurebase_darwin_amd64 ./cmd/featurebase - artifacts: - paths: - - featurebase_darwin_amd64 +# build for darwin amd64: +# stage: build +# image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 +# variables: +# GOOS: "darwin" +# GOARCH: "amd64" +# script: +# - go build -o featurebase_darwin_amd64 ./cmd/featurebase +# artifacts: +# paths: +# - featurebase_darwin_amd64 -build for darwin arm64: - stage: build - image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 - variables: - GOOS: "darwin" - GOARCH: "arm64" - script: - - go build -o featurebase_darwin_arm64 ./cmd/featurebase - artifacts: - paths: - - featurebase_darwin_arm64 +# build for darwin arm64: +# stage: build +# image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 +# variables: +# GOOS: "darwin" +# GOARCH: "arm64" +# script: +# - go build -o featurebase_darwin_arm64 ./cmd/featurebase +# artifacts: +# paths: +# - featurebase_darwin_arm64 # Build a Docker image with CI/CD and push to the GitLab registry. build container fb: image: docker:stable stage: build needs: - - "build for linux arm64" + - "build for linux amd64" tags: - shell before_script: - docker info script: - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - - docker build --build-arg GO_VERSION=$GOVERSION -t $tag . + - docker build --build-arg GO_VERSION=$GOVERSION -t $tag .gitlab/Dockerfile - docker push $tag - echo Created docker featurebase image with tag "$tag" From 7b39ab2bbde4435aebe734eea751837e6161259d Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:09:34 -0500 Subject: [PATCH 16/76] same --- .gitlab/.gitlab-ci.yml | 124 ++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 70f941f9c..0cd7688e0 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -10,73 +10,73 @@ stages: # - test - build -install lattice: - stage: test - image: node:14 - script: - - echo "Installing lattice dependencies..." - - cd lattice - - yarn install +# install lattice: +# stage: test +# image: node:14 +# script: +# - echo "Installing lattice dependencies..." +# - cd lattice +# - yarn install -build lattice: - stage: test - image: node:14 - variables: - CI: "true" - script: - - echo "Building lattice..." - - cd lattice - - yarn build - - tar -czvf lattice.tar.gz lattice/build - artifacts: - paths: - - lattice/lattice.tar.gz - needs: - - job: install lattice - allow_failure: true +# build lattice: +# stage: test +# image: node:14 +# variables: +# CI: "true" +# script: +# - echo "Building lattice..." +# - cd lattice +# - yarn build +# - tar -czvf lattice.tar.gz lattice/build +# artifacts: +# paths: +# - lattice/lattice.tar.gz +# needs: +# - job: install lattice +# allow_failure: true -run go tests: - stage: test - image: golang:1.16.9 - script: - - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverprofile=coverage.out - artifacts: - paths: - - coverage.out +# run go tests: +# stage: test +# image: golang:1.16.9 +# script: +# - echo "Running featurebase unit tests..." +# - go test -covermode=atomic -coverprofile=coverage.out +# artifacts: +# paths: +# - coverage.out -run go tests with output: - stage: test - image: golang:1.16.9 - script: - - echo "Running featurebase unit tests to capture JSON output..." - - go test -json > test-report.out - artifacts: - paths: - - test-report.out +# run go tests with output: +# stage: test +# image: golang:1.16.9 +# script: +# - echo "Running featurebase unit tests to capture JSON output..." +# - go test -json > test-report.out +# artifacts: +# paths: +# - test-report.out -generate coverage html: - stage: test - image: golang:1.16.9 - script: - - echo "Generating coverage HTML.." - - go tool cover -html coverage.out -o coverage.html - artifacts: - paths: - - coverage.html - needs: - - job: run go tests +# generate coverage html: +# stage: test +# image: golang:1.16.9 +# script: +# - echo "Generating coverage HTML.." +# - go tool cover -html coverage.out -o coverage.html +# artifacts: +# paths: +# - coverage.html +# needs: +# - job: run go tests -upload to sonarcloud: - stage: test - image: sonarsource/sonar-scanner-cli:4.6 - variables: - SONAR_TOKEN: $SONAR_TOKEN - script: - - sonar-scanner -Dsonar.projectKey=molecula_featurebase -Dsonar.organization=molecula -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.go.coverage.reportPaths=coverage.out -Dsonar.go.tests.reportPaths=test-report.out - needs: - - job: run go tests - - job: run go tests with output +# upload to sonarcloud: +# stage: test +# image: sonarsource/sonar-scanner-cli:4.6 +# variables: +# SONAR_TOKEN: $SONAR_TOKEN +# script: +# - sonar-scanner -Dsonar.projectKey=molecula_featurebase -Dsonar.organization=molecula -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.go.coverage.reportPaths=coverage.out -Dsonar.go.tests.reportPaths=test-report.out +# needs: +# - job: run go tests +# - job: run go tests with output build for linux amd64: stage: build From 3fcdc6ff9ebdc76cbac97fdecf35e6d27bc21987 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:11:03 -0500 Subject: [PATCH 17/76] same --- .gitlab/.gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 0cd7688e0..f00c3abd0 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -1,7 +1,7 @@ -include: - - template: Security/SAST.gitlab-ci.yml - - template: Security/License-Scanning.gitlab-ci.yml - - template: Security/Dependency-Scanning.gitlab-ci.yml +# include: +# - template: Security/SAST.gitlab-ci.yml +# - template: Security/License-Scanning.gitlab-ci.yml +# - template: Security/Dependency-Scanning.gitlab-ci.yml variables: GOVERSION: "1.16.3" From 2839ef53c9f6bef9167a722b5a6439d056fc2c41 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:30:13 -0500 Subject: [PATCH 18/76] add dir --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index f00c3abd0..4c87ac632 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -138,7 +138,7 @@ build container fb: - docker info script: - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - - docker build --build-arg GO_VERSION=$GOVERSION -t $tag .gitlab/Dockerfile + - docker build --build-arg GO_VERSION=$GOVERSION -t $tag .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" From adcbc4c801ce9e58419c68a91d37373f1cc74410 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 18:06:30 -0500 Subject: [PATCH 19/76] add -f flag --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 4c87ac632..17280a5eb 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -138,7 +138,7 @@ build container fb: - docker info script: - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - - docker build --build-arg GO_VERSION=$GOVERSION -t $tag .gitlab/Dockerfile . + - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" From 4ec0f7428a124954a840e654e6e6d16d665e7b2b Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 19:10:25 -0500 Subject: [PATCH 20/76] change dir --- .gitlab/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index fda808fa3..feec45395 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -9,7 +9,7 @@ COPY LICENSE /LICENSE COPY NOTICE /NOTICE ARG SHORT_SHA -COPY .gitlab/featurebase_linux_amd64 / +COPY featurebase_linux_amd64 / RUN mv /featurebase_linux_amd64 /featurebase RUN chmod ugo+x /featurebase From 7cde102944d3c229b47acf3148c15b99af5ef123 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 08:48:02 -0500 Subject: [PATCH 21/76] move binary to gitlab --- .gitlab/.gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 17280a5eb..0c3f01f52 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -137,6 +137,7 @@ build container fb: before_script: - docker info script: + - mv featurebase_linux_amd64 .gitlab/ - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag From 5f152af53a7143418fd149dc5bb554d79fd75897 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 09:17:29 -0500 Subject: [PATCH 22/76] print stuff --- .gitlab/.gitlab-ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 0c3f01f52..a312706dd 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -137,7 +137,12 @@ build container fb: before_script: - docker info script: + - pwd - mv featurebase_linux_amd64 .gitlab/ + - echo LIST ROOT DIR + - ls . + - echo LIST .gitlab + - ls .gitlab/ - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag From 3aec5fd9de9403a0cd400ebf6f3cabb70897e1ce Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 09:38:42 -0500 Subject: [PATCH 23/76] print more stuff --- .gitlab/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index feec45395..95ba069f4 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -4,6 +4,8 @@ LABEL maintainer "dev@molecula.com" LABEL org.opencontainers.image.authors="dev@molecula.com" RUN apk add --no-cache curl jq +RUN pwd +RUN ls . COPY LICENSE /LICENSE COPY NOTICE /NOTICE From 30fb649126fee033adc94e074cbc491fe5b669f1 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:18:42 -0500 Subject: [PATCH 24/76] make directory changes --- .gitlab/Dockerfile | 14 ++-- .gitlab/LICENSE | 202 +++++++++++++++++++++++++++++++++++++++++++++ .gitlab/NOTICE | 120 +++++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 6 deletions(-) create mode 100644 .gitlab/LICENSE create mode 100644 .gitlab/NOTICE diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index 95ba069f4..be6913740 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -3,18 +3,20 @@ FROM alpine:3.14.2 LABEL maintainer "dev@molecula.com" LABEL org.opencontainers.image.authors="dev@molecula.com" +WORKDIR /featurebase + RUN apk add --no-cache curl jq RUN pwd RUN ls . -COPY LICENSE /LICENSE -COPY NOTICE /NOTICE +COPY LICENSE . +COPY NOTICE . +COPY .gitlab/featurebase_linux_amd64 . + ARG SHORT_SHA -COPY featurebase_linux_amd64 / - -RUN mv /featurebase_linux_amd64 /featurebase -RUN chmod ugo+x /featurebase +# RUN mv /featurebase_linux_amd64 /featurebase +RUN chmod ugo+x . EXPOSE 10101 VOLUME /data diff --git a/.gitlab/LICENSE b/.gitlab/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/.gitlab/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.gitlab/NOTICE b/.gitlab/NOTICE new file mode 100644 index 000000000..594272a27 --- /dev/null +++ b/.gitlab/NOTICE @@ -0,0 +1,120 @@ +Software license +================ + +Copyright (C) 2017-2018 Pilosa Corp. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"). +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Third-party software licenses +============================= + +The file /pilosa/lru/lru.go contains a redistribution of lru +(github.com/golang/groupcache/lru); the license follows: + + Copyright 2013 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +The file /roaring/btree.go contains a modified redistribution of b +(https://github.com/cznic/b); the license follows: + + Copyright (c) 2014 The b Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the names of the authors nor the names of the + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The file /server/tlsconfig.go contains a modified redistribution of bridge +(https://github.com/robustirc/bridge); the license follows: + + Copyright © 2014-2015 The RobustIRC Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of RobustIRC nor the names of contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The files /logger/filewriter.go and /logger/filewriter_test.go contain a modified redistribution of reopen (github.com/client9/reopen); the license follows: + + The MIT License (MIT) + + Copyright (c) 2015 Nick Galbreath + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. From 7e1b629504a9a4ccbd3fcc1c5e488385289e85a7 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:33:34 -0500 Subject: [PATCH 25/76] change docker push url --- .gitlab/.gitlab-ci.yml | 2 +- .gitlab/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index a312706dd..2de4ea4e2 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -145,7 +145,7 @@ build container fb: - ls .gitlab/ - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - - docker push $tag + - docker push registry.gitlab.com/molecula/featurebase:$tag - echo Created docker featurebase image with tag "$tag" diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index be6913740..8b6516c80 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -25,4 +25,4 @@ ENV PILOSA_BIND 0.0.0.0:10101 ENV PILOSA_BIND_GRPC 0.0.0.0:20101 ENTRYPOINT ["/featurebase"] -CMD ["server"] +CMD ["server"] \ No newline at end of file From 841e79f343dcd246d54555fde293f71dd046ddac Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:51:26 -0500 Subject: [PATCH 26/76] same --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 2de4ea4e2..e5ceabaf8 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -145,7 +145,7 @@ build container fb: - ls .gitlab/ - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - - docker push registry.gitlab.com/molecula/featurebase:$tag + - docker push registry.gitlab.com/molecula/featurebase:$CI_COMMIT_SHORT_SHA - echo Created docker featurebase image with tag "$tag" From 466c5a5627eaad0703ffd4492ff2ce11a7ec5b27 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:00:47 -0500 Subject: [PATCH 27/76] same --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index e5ceabaf8..ed6e0246d 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -145,7 +145,7 @@ build container fb: - ls .gitlab/ - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - - docker push registry.gitlab.com/molecula/featurebase:$CI_COMMIT_SHORT_SHA + - docker push registry.gitlab.com/molecula/featurebase/featurebase:$CI_COMMIT_SHORT_SHA - echo Created docker featurebase image with tag "$tag" From 9669745de6de52090846bfb204c358ab6c2c146d Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 12:04:31 -0500 Subject: [PATCH 28/76] change tag --- .gitlab/.gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index ed6e0246d..af51cd085 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -143,9 +143,9 @@ build container fb: - ls . - echo LIST .gitlab - ls .gitlab/ - - tag=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA + - tag=$CI_REGISTRY_IMAGE/featurebase:$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - - docker push registry.gitlab.com/molecula/featurebase/featurebase:$CI_COMMIT_SHORT_SHA + - docker push $tag - echo Created docker featurebase image with tag "$tag" From c1746fae5b423c12d52b7490722fcd792783c122 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 13:33:21 -0500 Subject: [PATCH 29/76] add login --- .gitlab/.gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index af51cd085..120f2e46c 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -134,8 +134,11 @@ build container fb: - "build for linux amd64" tags: - shell + # variables: + # - DOCKER_DEPLOY_USER: $DOCKER_DEPLOY_USER + # - DOCKER_DEPLOY_TOKEN: $DOCKER_DEPLOY_TOKEN before_script: - - docker info + - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN registry.gitlab.com/molecula/featurebase script: - pwd - mv featurebase_linux_amd64 .gitlab/ From 2517e11afd88a6f999f6cc8639509f6c18122d45 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 14:09:29 -0500 Subject: [PATCH 30/76] try without mv --- .gitlab/.gitlab-ci.yml | 14 +++----------- .gitlab/Dockerfile | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 120f2e46c..d18a7b222 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -134,19 +134,11 @@ build container fb: - "build for linux amd64" tags: - shell - # variables: - # - DOCKER_DEPLOY_USER: $DOCKER_DEPLOY_USER - # - DOCKER_DEPLOY_TOKEN: $DOCKER_DEPLOY_TOKEN before_script: - - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN registry.gitlab.com/molecula/featurebase + - cat $DOCKER_DEPLOY_TOKEN | docker login -u $DOCKER_DEPLOY_USER --password-stdin $CI_REGISTRY_IMAGE script: - - pwd - - mv featurebase_linux_amd64 .gitlab/ - - echo LIST ROOT DIR - - ls . - - echo LIST .gitlab - - ls .gitlab/ - - tag=$CI_REGISTRY_IMAGE/featurebase:$CI_COMMIT_SHORT_SHA + # - mv featurebase_linux_amd64 .gitlab/ + - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index 8b6516c80..5402770ac 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -11,7 +11,7 @@ RUN ls . COPY LICENSE . COPY NOTICE . -COPY .gitlab/featurebase_linux_amd64 . +COPY featurebase_linux_amd64 . ARG SHORT_SHA From fba75f1a0c88c8e876570fa40c3a75b064e113cc Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 14:16:08 -0500 Subject: [PATCH 31/76] same --- .gitlab/.gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index d18a7b222..f8cd0e7ba 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -135,7 +135,8 @@ build container fb: tags: - shell before_script: - - cat $DOCKER_DEPLOY_TOKEN | docker login -u $DOCKER_DEPLOY_USER --password-stdin $CI_REGISTRY_IMAGE + # - cat $DOCKER_DEPLOY_TOKEN | docker login -u $DOCKER_DEPLOY_USER --password-stdin $CI_REGISTRY_IMAGE + - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN $CI_REGISTRY_IMAGE script: # - mv featurebase_linux_amd64 .gitlab/ - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA From e1945bcf1c96dd00a348aa46105c75faa5a4dbd8 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 14:20:51 -0500 Subject: [PATCH 32/76] change commit name to commit slug --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index f8cd0e7ba..9fa49644a 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -139,7 +139,7 @@ build container fb: - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN $CI_REGISTRY_IMAGE script: # - mv featurebase_linux_amd64 .gitlab/ - - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA + - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_REF_SLUG_$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" From 3cf8f9db5eeab0896b0ead185188f888156d2e47 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:18:13 -0500 Subject: [PATCH 33/76] change tag --- .gitlab/.gitlab-ci.yml | 8 +- .gitlab/LICENSE | 202 ----------------------------------------- .gitlab/NOTICE | 120 ------------------------ 3 files changed, 3 insertions(+), 327 deletions(-) delete mode 100644 .gitlab/LICENSE delete mode 100644 .gitlab/NOTICE diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 9fa49644a..b96e75c45 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -126,7 +126,7 @@ build for linux amd64: # paths: # - featurebase_darwin_arm64 -# Build a Docker image with CI/CD and push to the GitLab registry. +# Build a FB Docker image with CI/CD and push to the GitLab registry. build container fb: image: docker:stable stage: build @@ -135,11 +135,9 @@ build container fb: tags: - shell before_script: - # - cat $DOCKER_DEPLOY_TOKEN | docker login -u $DOCKER_DEPLOY_USER --password-stdin $CI_REGISTRY_IMAGE - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN $CI_REGISTRY_IMAGE - script: - # - mv featurebase_linux_amd64 .gitlab/ - - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_REF_SLUG_$CI_COMMIT_SHORT_SHA + script: + - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_REF_SLUG/$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" diff --git a/.gitlab/LICENSE b/.gitlab/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/.gitlab/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/.gitlab/NOTICE b/.gitlab/NOTICE deleted file mode 100644 index 594272a27..000000000 --- a/.gitlab/NOTICE +++ /dev/null @@ -1,120 +0,0 @@ -Software license -================ - -Copyright (C) 2017-2018 Pilosa Corp. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"). -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Third-party software licenses -============================= - -The file /pilosa/lru/lru.go contains a redistribution of lru -(github.com/golang/groupcache/lru); the license follows: - - Copyright 2013 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -The file /roaring/btree.go contains a modified redistribution of b -(https://github.com/cznic/b); the license follows: - - Copyright (c) 2014 The b Authors. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the names of the authors nor the names of the - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The file /server/tlsconfig.go contains a modified redistribution of bridge -(https://github.com/robustirc/bridge); the license follows: - - Copyright © 2014-2015 The RobustIRC Authors. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of RobustIRC nor the names of contributors may be used - to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The files /logger/filewriter.go and /logger/filewriter_test.go contain a modified redistribution of reopen (github.com/client9/reopen); the license follows: - - The MIT License (MIT) - - Copyright (c) 2015 Nick Galbreath - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. From b1ee2c7a593ff2d84439fa0328ccb01141a0443f Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:25:52 -0500 Subject: [PATCH 34/76] same --- .gitlab/.gitlab-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index b96e75c45..e9041601f 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -135,9 +135,10 @@ build container fb: tags: - shell before_script: - - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN $CI_REGISTRY_IMAGE + # - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN $CI_REGISTRY_IMAGE + - echo "${DOCKER_DEPLOY_TOKEN}" | docker login -u ${DOCKER_DEPLOY_USER} --password-stdin ${CI_REGISTRY} script: - - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_REF_SLUG/$CI_COMMIT_SHORT_SHA + - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_REF_SLUG$CI_COMMIT_SHORT_SHA - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" From 64fd686ec49e6d84e98a6b606c5453aca9803e65 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:46:02 -0500 Subject: [PATCH 35/76] tag with slug and sha --- .gitlab/.gitlab-ci.yml | 2 +- .gitlab/Dockerfile | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index e9041601f..48da8b317 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -138,7 +138,7 @@ build container fb: # - docker login -u $DOCKER_DEPLOY_USER -p $DOCKER_DEPLOY_TOKEN $CI_REGISTRY_IMAGE - echo "${DOCKER_DEPLOY_TOKEN}" | docker login -u ${DOCKER_DEPLOY_USER} --password-stdin ${CI_REGISTRY} script: - - tag=$CI_REGISTRY_IMAGE/server:$CI_COMMIT_REF_SLUG$CI_COMMIT_SHORT_SHA + - tag=${CI_REGISTRY_IMAGE}/server:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA} - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . - docker push $tag - echo Created docker featurebase image with tag "$tag" diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index 5402770ac..b22392ce1 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -6,16 +6,11 @@ LABEL org.opencontainers.image.authors="dev@molecula.com" WORKDIR /featurebase RUN apk add --no-cache curl jq -RUN pwd -RUN ls . COPY LICENSE . COPY NOTICE . COPY featurebase_linux_amd64 . -ARG SHORT_SHA - -# RUN mv /featurebase_linux_amd64 /featurebase RUN chmod ugo+x . EXPOSE 10101 VOLUME /data From 44148ed79d3156f00f2c701f2c2a213d58d14655 Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Wed, 20 Oct 2021 12:21:05 -0500 Subject: [PATCH 36/76] Build featurebase docker image in CI. Add to registry. --- .gitlab/.gitlab-ci.yml | 26 +++++++++++---------- .gitlab/Dockerfile | 51 ++++++++---------------------------------- 2 files changed, 23 insertions(+), 54 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index cbb7eeffb..cda5430cc 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -4,7 +4,7 @@ include: - template: Security/Dependency-Scanning.gitlab-ci.yml variables: - GO_VERSION: 1.16.3 + GOVERSION: "1.16.3" stages: - test @@ -126,18 +126,20 @@ build for darwin arm64: paths: - featurebase_darwin_arm64 -# Build a Docker image with CI/CD and push to the GitLab registry. +# Build a FB Docker image with CI/CD and push to the GitLab registry. build container fb: - image: docker:latest + image: docker:stable stage: build - services: - - docker:dind - script: - - | - docker build - --build-arg GO_VERSION=$(GO_VERSION) - --tag featurebase:$(VERSION) . - - docker push "$CI_REGISTRY_IMAGE${tag}" - - @echo Created docker image: featurebase:$(VERSION) + needs: + - "build for linux amd64" + tags: + - shell + before_script: + - echo "${DOCKER_DEPLOY_TOKEN}" | docker login -u ${DOCKER_DEPLOY_USER} --password-stdin ${CI_REGISTRY} + script: + - tag=${CI_REGISTRY_IMAGE}/server:${CI_COMMIT_REF_SLUG} + - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . + - docker push $tag + - echo Created docker featurebase image with tag "$tag" diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile index 115855034..b22392ce1 100644 --- a/.gitlab/Dockerfile +++ b/.gitlab/Dockerfile @@ -1,50 +1,17 @@ -ARG GO_VERSION=latest - -####################### -### Lattice builder ### -####################### - -FROM moleculacorp/nodejs:latest as lattice-builder -WORKDIR /lattice - -COPY lattice/package.json ./ -COPY lattice/yarn.lock ./ -RUN yarn install - -COPY lattice ./ -RUN yarn build - -###################### -### Pilosa builder ### -###################### - -FROM golang:${GO_VERSION} as pilosa-builder -ARG MAKE_FLAGS -WORKDIR /pilosa - -RUN go get github.com/rakyll/statik - -COPY . ./ -COPY --from=lattice-builder /lattice/build /lattice -RUN /go/bin/statik -src=/lattice -dest=/pilosa - -RUN make build FLAGS="-o build/featurebase" ${MAKE_FLAGS} - -##################### -### Pilosa runner ### -##################### - -FROM alpine:3.13.2 as runner +FROM alpine:3.14.2 LABEL maintainer "dev@molecula.com" +LABEL org.opencontainers.image.authors="dev@molecula.com" + +WORKDIR /featurebase RUN apk add --no-cache curl jq -COPY --from=pilosa-builder /pilosa/build/featurebase / - -COPY LICENSE /LICENSE -COPY NOTICE /NOTICE +COPY LICENSE . +COPY NOTICE . +COPY featurebase_linux_amd64 . +RUN chmod ugo+x . EXPOSE 10101 VOLUME /data @@ -53,4 +20,4 @@ ENV PILOSA_BIND 0.0.0.0:10101 ENV PILOSA_BIND_GRPC 0.0.0.0:20101 ENTRYPOINT ["/featurebase"] -CMD ["server"] +CMD ["server"] \ No newline at end of file From c9d25909be31f7d47dc69c1558d97bef02d5e23e Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Thu, 21 Oct 2021 16:27:43 -0500 Subject: [PATCH 37/76] cleanup --- .gitlab/.gitlab-ci.yml | 200 ++++++++++++++++++++--------------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 76baad586..cda5430cc 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -1,82 +1,82 @@ -# include: -# - template: Security/SAST.gitlab-ci.yml -# - template: Security/License-Scanning.gitlab-ci.yml -# - template: Security/Dependency-Scanning.gitlab-ci.yml +include: + - template: Security/SAST.gitlab-ci.yml + - template: Security/License-Scanning.gitlab-ci.yml + - template: Security/Dependency-Scanning.gitlab-ci.yml variables: GOVERSION: "1.16.3" stages: - # - test + - test - build -# install lattice: -# stage: test -# image: node:14 -# script: -# - echo "Installing lattice dependencies..." -# - cd lattice -# - yarn install +install lattice: + stage: test + image: node:14 + script: + - echo "Installing lattice dependencies..." + - cd lattice + - yarn install -# build lattice: -# stage: test -# image: node:14 -# variables: -# CI: "true" -# script: -# - echo "Building lattice..." -# - cd lattice -# - yarn build -# - tar -czvf lattice.tar.gz lattice/build -# artifacts: -# paths: -# - lattice/lattice.tar.gz -# needs: -# - job: install lattice -# allow_failure: true +build lattice: + stage: test + image: node:14 + variables: + CI: "true" + script: + - echo "Building lattice..." + - cd lattice + - yarn build + - tar -czvf lattice.tar.gz lattice/build + artifacts: + paths: + - lattice/lattice.tar.gz + needs: + - job: install lattice + allow_failure: true -# run go tests: -# stage: test -# image: golang:1.16.9 -# script: -# - echo "Running featurebase unit tests..." -# - go test -covermode=atomic -coverprofile=coverage.out -# artifacts: -# paths: -# - coverage.out +run go tests: + stage: test + image: golang:1.16.9 + script: + - echo "Running featurebase unit tests..." + - go test -covermode=atomic -coverprofile=coverage.out + artifacts: + paths: + - coverage.out -# run go tests with output: -# stage: test -# image: golang:1.16.9 -# script: -# - echo "Running featurebase unit tests to capture JSON output..." -# - go test -json > test-report.out -# artifacts: -# paths: -# - test-report.out +run go tests with output: + stage: test + image: golang:1.16.9 + script: + - echo "Running featurebase unit tests to capture JSON output..." + - go test -json > test-report.out + artifacts: + paths: + - test-report.out -# generate coverage html: -# stage: test -# image: golang:1.16.9 -# script: -# - echo "Generating coverage HTML.." -# - go tool cover -html coverage.out -o coverage.html -# artifacts: -# paths: -# - coverage.html -# needs: -# - job: run go tests +generate coverage html: + stage: test + image: golang:1.16.9 + script: + - echo "Generating coverage HTML.." + - go tool cover -html coverage.out -o coverage.html + artifacts: + paths: + - coverage.html + needs: + - job: run go tests -# upload to sonarcloud: -# stage: test -# image: sonarsource/sonar-scanner-cli:4.6 -# variables: -# SONAR_TOKEN: $SONAR_TOKEN -# script: -# - sonar-scanner -Dsonar.projectKey=molecula_featurebase -Dsonar.organization=molecula -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.go.coverage.reportPaths=coverage.out -Dsonar.go.tests.reportPaths=test-report.out -# needs: -# - job: run go tests -# - job: run go tests with output +upload to sonarcloud: + stage: test + image: sonarsource/sonar-scanner-cli:4.6 + variables: + SONAR_TOKEN: $SONAR_TOKEN + script: + - sonar-scanner -Dsonar.projectKey=molecula_featurebase -Dsonar.organization=molecula -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.go.coverage.reportPaths=coverage.out -Dsonar.go.tests.reportPaths=test-report.out + needs: + - job: run go tests + - job: run go tests with output build for linux amd64: stage: build @@ -90,41 +90,41 @@ build for linux amd64: paths: - featurebase_linux_amd64 -# build for linux arm64: -# stage: build -# image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 -# variables: -# GOOS: "linux" -# GOARCH: "arm64" -# script: -# - go build -o featurebase_linux_arm64 ./cmd/featurebase -# artifacts: -# paths: -# - featurebase_linux_arm64 +build for linux arm64: + stage: build + image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + variables: + GOOS: "linux" + GOARCH: "arm64" + script: + - go build -o featurebase_linux_arm64 ./cmd/featurebase + artifacts: + paths: + - featurebase_linux_arm64 -# build for darwin amd64: -# stage: build -# image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 -# variables: -# GOOS: "darwin" -# GOARCH: "amd64" -# script: -# - go build -o featurebase_darwin_amd64 ./cmd/featurebase -# artifacts: -# paths: -# - featurebase_darwin_amd64 +build for darwin amd64: + stage: build + image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + variables: + GOOS: "darwin" + GOARCH: "amd64" + script: + - go build -o featurebase_darwin_amd64 ./cmd/featurebase + artifacts: + paths: + - featurebase_darwin_amd64 -# build for darwin arm64: -# stage: build -# image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 -# variables: -# GOOS: "darwin" -# GOARCH: "arm64" -# script: -# - go build -o featurebase_darwin_arm64 ./cmd/featurebase -# artifacts: -# paths: -# - featurebase_darwin_arm64 +build for darwin arm64: + stage: build + image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + variables: + GOOS: "darwin" + GOARCH: "arm64" + script: + - go build -o featurebase_darwin_arm64 ./cmd/featurebase + artifacts: + paths: + - featurebase_darwin_arm64 # Build a FB Docker image with CI/CD and push to the GitLab registry. build container fb: From 7e14008ed8c7c408053b442dc40ba64313628d82 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 27 Oct 2021 13:35:42 -0500 Subject: [PATCH 38/76] add golangci-lint to gitlab container --- .gitlab/.gitlab-ci.yml | 20 +++++++++++++++++++- .golangci.yml | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 0e41bfd1e..20329e6e0 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -64,6 +64,24 @@ generate coverage html: needs: - job: run go tests +install goci: + stage: build + image: golang:1.16.9 + script: + - echo "Installing golangci-lint" + - GO111MODULE=off go get github.com/golangci/golangci-lint/cmd/golangci-lint + artifacts: + paths: + - /go/bin/golangci-lint + +run goci: + stage: test + image: golang:1.16.9 + script: + - echo "Running golangci-lint" + needs: + - job: install goci + upload to sonarcloud: stage: test image: sonarsource/sonar-scanner-cli:4.6 @@ -121,4 +139,4 @@ build for darwin arm64: - go build -o featurebase_darwin_arm64 ./cmd/featurebase artifacts: paths: - - featurebase_darwin_arm64 \ No newline at end of file + - featurebase_darwin_arm64 diff --git a/.golangci.yml b/.golangci.yml index 3c51570cc..1b186c89e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,39 @@ run: + #skip the protobuf generated files + skip-dirs: + - pb + - proto skip-files: - pql/pql.peg.go +linters-settings: + gofmt: + simplify: true + govet: + # report about shadowed variables + check-shadowing: true + + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + + # enable or disable analyzers by name + # run `go tool vet help` to see all analyzers + enable: + - atomicalign + enable-all: false + disable: + - shadow + disable-all: false + + staticcheck: + # Select the Go version to target. The default is '1.13'. + go: "1.16" + # https://staticcheck.io/docs/options#checks + checks: [ "all" ] + + From f4a47346b0b83c14cb04edc2a95e02287e5d0e71 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Thu, 28 Oct 2021 09:28:32 -0500 Subject: [PATCH 39/76] add golangci-lint to gitlab pipeline. Currently gofmt and govet --- .gitlab/.gitlab-ci.yml | 47 ++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 20329e6e0..764ec7804 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -3,10 +3,34 @@ include: - template: Security/License-Scanning.gitlab-ci.yml - template: Security/Dependency-Scanning.gitlab-ci.yml +.go-cache: + variables: + GOPATH: $CI_PROJECT_DIR/.go + cache: + paths: + - .go/pkg/mod/ + stages: + - lint - test - build +before_script: + - echo "before_script" + - git version + - go env -w GOPRIVATE=github.com/molecula + - mkdir -p .go + - go version + - go env -w GO111MODULE=on + +golangci-lint: + image: golangci/golangci-lint:v1.39.0 + stage: lint + extends: .go-cache + allow_failure: false + script: + - golangci-lint run -v + install lattice: stage: test image: node:14 @@ -35,6 +59,7 @@ build lattice: run go tests: stage: test image: golang:1.16.9 + extends: .go-cache script: - echo "Running featurebase unit tests..." - go test -covermode=atomic -coverprofile=coverage.out @@ -64,24 +89,6 @@ generate coverage html: needs: - job: run go tests -install goci: - stage: build - image: golang:1.16.9 - script: - - echo "Installing golangci-lint" - - GO111MODULE=off go get github.com/golangci/golangci-lint/cmd/golangci-lint - artifacts: - paths: - - /go/bin/golangci-lint - -run goci: - stage: test - image: golang:1.16.9 - script: - - echo "Running golangci-lint" - needs: - - job: install goci - upload to sonarcloud: stage: test image: sonarsource/sonar-scanner-cli:4.6 @@ -96,6 +103,7 @@ upload to sonarcloud: build for linux amd64: stage: build image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + extends: .go-cache variables: GOOS: "linux" GOARCH: "amd64" @@ -108,6 +116,7 @@ build for linux amd64: build for linux arm64: stage: build image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + extends: .go-cache variables: GOOS: "linux" GOARCH: "arm64" @@ -120,6 +129,7 @@ build for linux arm64: build for darwin amd64: stage: build image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + extends: .go-cache variables: GOOS: "darwin" GOARCH: "amd64" @@ -132,6 +142,7 @@ build for darwin amd64: build for darwin arm64: stage: build image: registry.gitlab.com/molecula/featurebase/builder:0.0.3 + extends: .go-cache variables: GOOS: "darwin" GOARCH: "arm64" From d1baac62392bf7cef8637a323e3f9a511af5346f Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 09:35:13 -0500 Subject: [PATCH 40/76] only check for new issues --- .golangci.yml | 15 +++++++++------ roaring/roaring.go | 24 ++++++++++++------------ sql2/walk.go | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 1b186c89e..74797538c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,6 @@ run: #skip the protobuf generated files + skip-dirs-use-default: true skip-dirs: - pb - proto @@ -12,6 +13,7 @@ linters-settings: # report about shadowed variables check-shadowing: true + # settings per analyzer settings: printf: # analyzer name, run `go tool vet help` to see all analyzers @@ -30,10 +32,11 @@ linters-settings: - shadow disable-all: false - staticcheck: - # Select the Go version to target. The default is '1.13'. - go: "1.16" - # https://staticcheck.io/docs/options#checks - checks: [ "all" ] - +issues: + max-per-linter: 0 + max-same: 0 + exclude-use-default: false + exclude: + - 'declaration of "(err|ctx)" shadows declaration at' + - 'typecheck' diff --git a/roaring/roaring.go b/roaring/roaring.go index 4fd8a50c0..bc4c57214 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -2402,13 +2402,13 @@ func (b *Bitmap) ImportRoaringRawIterator(itr RoaringIterator, clear bool, log b } err = nil if log && changed > 0 { - op := op{opN: changed, roaring: itr.Data()} + o := op{opN: changed, roaring: itr.Data()} if clear { - op.typ = opTypeRemoveRoaring + o.typ = opTypeRemoveRoaring } else { - op.typ = opTypeAddRoaring + o.typ = opTypeAddRoaring } - err = b.writeOp(&op) + err = b.writeOp(&o) } return changed, rowSet, err } @@ -4559,26 +4559,26 @@ func intersectionCallbackRunRun(a, b *Container, fn func(uint16)) { j++ } else if va.Last > vb.Last && va.Start >= vb.Start { // |--vb-|-|-va--| - for i := int(va.Start); i <= int(vb.Last); i++ { - fn(uint16(i)) + for z := int(va.Start); z <= int(vb.Last); z++ { + fn(uint16(z)) } j++ } else if va.Last > vb.Last && va.Start < vb.Start { // |--va|--vb--|--| - for i := int(vb.Start); i <= int(vb.Last); i++ { - fn(uint16(i)) + for z := int(vb.Start); z <= int(vb.Last); z++ { + fn(uint16(z)) } j++ } else if va.Last <= vb.Last && va.Start >= vb.Start { // |--vb|--va--|--| - for i := int(va.Start); i <= int(va.Last); i++ { - fn(uint16(i)) + for z := int(va.Start); z <= int(va.Last); z++ { + fn(uint16(z)) } i++ } else if va.Last <= vb.Last && va.Start < vb.Start { // |--va-|-|-vb--| - for i := int(vb.Start); i <= int(va.Last); i++ { - fn(uint16(i)) + for z := int(vb.Start); z <= int(va.Last); z++ { + fn(uint16(z)) } i++ } diff --git a/sql2/walk.go b/sql2/walk.go index c04ca76ba..30eafd03c 100644 --- a/sql2/walk.go +++ b/sql2/walk.go @@ -229,7 +229,7 @@ func walk(v Visitor, node Node) (_ Node, err error) { if src, err := walk(v, n.Source); err != nil { return node, err } else if src != nil { - n.Source = n.Source.(Source) + n.Source = n.Source } else { n.Source = nil } From 2b03c97a3805fd80e6409e79413a81d95a1c4ab5 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 10:24:40 -0500 Subject: [PATCH 41/76] only run latest try2 --- .gitlab/.gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 764ec7804..69c263acb 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -29,7 +29,8 @@ golangci-lint: extends: .go-cache allow_failure: false script: - - golangci-lint run -v + - echo "Checking for issues in new code" + - golangci-lint run -v --new-from-rev=HEAD~1 install lattice: stage: test From 113c6bc21a007bb9439bb3b7569a77064076afcf Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 10:42:38 -0500 Subject: [PATCH 42/76] cleanup gofmt --- rbf/cfg/os.go | 1 + shardwidth/20.go | 1 + testhook/cleanup1.14.go | 1 + 3 files changed, 3 insertions(+) diff --git a/rbf/cfg/os.go b/rbf/cfg/os.go index 28605af8b..88bb68891 100644 --- a/rbf/cfg/os.go +++ b/rbf/cfg/os.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !386 // +build !386 package cfg diff --git a/shardwidth/20.go b/shardwidth/20.go index 5505656dd..f5e6ad968 100644 --- a/shardwidth/20.go +++ b/shardwidth/20.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !shardwidth16 && !shardwidth17 && !shardwidth18 && !shardwidth19 && !shardwidth21 && !shardwidth22 && !shardwidth23 && !shardwidth24 && !shardwidth25 && !shardwidth26 && !shardwidth27 && !shardwidth28 && !shardwidth29 && !shardwidth30 && !shardwidth31 && !shardwidth32 // +build !shardwidth16,!shardwidth17,!shardwidth18,!shardwidth19,!shardwidth21,!shardwidth22,!shardwidth23,!shardwidth24,!shardwidth25,!shardwidth26,!shardwidth27,!shardwidth28,!shardwidth29,!shardwidth30,!shardwidth31,!shardwidth32 package shardwidth diff --git a/testhook/cleanup1.14.go b/testhook/cleanup1.14.go index 9ca6024be..a88c91eae 100644 --- a/testhook/cleanup1.14.go +++ b/testhook/cleanup1.14.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build go1.14 // +build go1.14 package testhook From 36fa27e2a2d41d6702eaac9075380c033d6113da Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 10:42:58 -0500 Subject: [PATCH 43/76] limit linters to only specified --- .golangci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 74797538c..9b1b80469 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,11 +1,18 @@ run: #skip the protobuf generated files + timeout: 3m skip-dirs-use-default: true skip-dirs: - pb - proto skip-files: - pql/pql.peg.go +linters: + enable: + - govet + - gofmt + enable-all: false + disable-all: true linters-settings: gofmt: simplify: true @@ -34,9 +41,9 @@ linters-settings: issues: + exclude-use-default: false max-per-linter: 0 max-same: 0 - exclude-use-default: false exclude: - 'declaration of "(err|ctx)" shadows declaration at' - 'typecheck' From 6d6cf655c50bca67e70bd6ed1d76ac8ed7cc61aa Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 10:53:25 -0500 Subject: [PATCH 44/76] clean gofmt --- .golangci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index 9b1b80469..cbeca8cbb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,6 +13,15 @@ linters: - gofmt enable-all: false disable-all: true + +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: tab + # print lines of code with issue, default is true + print-issued-lines: true + # print linter name in the end of issue text, default is true + print-linter-name: true + linters-settings: gofmt: simplify: true @@ -44,6 +53,10 @@ issues: exclude-use-default: false max-per-linter: 0 max-same: 0 + exclude-rules: + - linters: + - gofmt + text: "File is not `gofmt`-ed with `-s`" exclude: - 'declaration of "(err|ctx)" shadows declaration at' - 'typecheck' From ac3445b8a55299115788fc2ade8c7207be34480e Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 11:02:49 -0500 Subject: [PATCH 45/76] disable lint on circleci --- .circleci/config.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 30b2ef5e4..8524a0afd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,14 +70,6 @@ jobs: - checkout-plus - skip-if-root-unchanged - run: make check-license-headers - linter: - executor: - name: golang - steps: - - checkout-plus - - skip-if-root-unchanged - - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/local/bin v1.31.0 - - run: make golangci-lint go-mod-tidy: executor: name: golang @@ -235,10 +227,6 @@ workflows: filters: tags: only: /^v.*/ - - linter: - context: molecula - requires: - - setup - check-license-headers: context: molecula requires: From 21283e123f292ae55417abe55ef44533ef7e6135 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 11:08:51 -0500 Subject: [PATCH 46/76] remove timeout --- .golangci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index cbeca8cbb..7f827841c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,5 @@ run: #skip the protobuf generated files - timeout: 3m skip-dirs-use-default: true skip-dirs: - pb From 396e1e6433d087e90ae5bddc80abbf19546c6929 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 12:16:45 -0500 Subject: [PATCH 47/76] . --- .golangci.yml | 7 +++++-- hack.go | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 7f827841c..ac22cad0b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,7 @@ run: #skip the protobuf generated files + deadline: 5m + timeout: 5m skip-dirs-use-default: true skip-dirs: - pb @@ -57,5 +59,6 @@ issues: - gofmt text: "File is not `gofmt`-ed with `-s`" exclude: - - 'declaration of "(err|ctx)" shadows declaration at' - - 'typecheck' + - 'declaration of "(err|ctx)" shadows declaration at' + - 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' + diff --git a/hack.go b/hack.go index 2dff6139a..21da2e87b 100644 --- a/hack.go +++ b/hack.go @@ -15,7 +15,7 @@ package pilosa import ( - "github.com/gogo/protobuf/proto" +"github.com/gogo/protobuf/proto" "github.com/molecula/featurebase/v2/pb" "github.com/molecula/featurebase/v2/pql" ) From 8af9cfbb7d2633ff7d14a2ee693d8e01c0ebe0e3 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 13:08:24 -0500 Subject: [PATCH 48/76] removed --new check --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 69c263acb..56df3a822 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -30,7 +30,7 @@ golangci-lint: allow_failure: false script: - echo "Checking for issues in new code" - - golangci-lint run -v --new-from-rev=HEAD~1 + - golangci-lint run -v install lattice: stage: test From 2ddcbce8adda400b40eebc8f46c94573eebfa205 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 13:08:49 -0500 Subject: [PATCH 49/76] fix govet and gofmt errors in existing code --- .golangci.yml | 8 +- cache.go | 16 ++-- client/batch.go | 16 ++-- client/client.go | 4 +- client/orm_test.go | 10 +-- cluster.go | 2 +- cmd/roaring-migrate/ctim_linux.go | 1 + const_amd64.go | 1 + ctl/config.go | 2 +- ctl/generate_config.go | 2 +- executor.go | 24 +++--- fragment.go | 4 +- fragment_internal_test.go | 4 +- generation_nodebug.go | 1 + hack.go | 2 +- holder.go | 2 +- http/handler.go | 2 +- rbf.go | 10 +-- rbf/cursor_test.go | 10 ++- rbf/dot.go | 4 +- rbf/rbf_test.go | 4 +- roaring/add_test.go | 1 + roaring/generation_nodebug.go | 1 + roaring/nop_inst.go | 1 + roaring/roaring_internal_test.go | 115 +++++++++++++++-------------- roaring/roaring_nop_paranoia.go | 1 + roaring/roaring_nop_sentinel.go | 1 + roaring/roaring_nop_stats.go | 1 + row.go | 6 +- server/dup.go | 1 + server/grpc.go | 8 +- server/grpc_test.go | 8 +- server/handler_test.go | 4 +- sql/select.go | 8 +- sql2/parser_test.go | 62 ++++++++-------- sql2/walk.go | 4 +- syswrap/mmap.go | 2 +- time.go | 2 +- tracing/opentracing/opentracing.go | 4 +- translator_test.go | 18 ++--- 40 files changed, 192 insertions(+), 185 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ac22cad0b..ca1abeed0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -52,12 +52,8 @@ linters-settings: issues: exclude-use-default: false - max-per-linter: 0 - max-same: 0 - exclude-rules: - - linters: - - gofmt - text: "File is not `gofmt`-ed with `-s`" + max-issues-per-linter: 0 + max-same-issues: 0 exclude: - 'declaration of "(err|ctx)" shadows declaration at' - 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' diff --git a/cache.go b/cache.go index 7991d1aaa..00c70a129 100644 --- a/cache.go +++ b/cache.go @@ -398,8 +398,8 @@ func (p PairField) ToRows(callback func(*pb.RowResponse) error) error { {Name: "count", Datatype: "uint64"}, }, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_StringVal{StringVal: p.Pair.Key}}, - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: p.Pair.Count}}, + {ColumnVal: &pb.ColumnResponse_StringVal{StringVal: p.Pair.Key}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: p.Pair.Count}}, }, }) } else { @@ -409,8 +409,8 @@ func (p PairField) ToRows(callback func(*pb.RowResponse) error) error { {Name: "count", Datatype: "uint64"}, }, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: p.Pair.ID}}, - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: p.Pair.Count}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: p.Pair.ID}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: p.Pair.Count}}, }, }) } @@ -540,8 +540,8 @@ func (p *PairsField) ToRows(callback func(*pb.RowResponse) error) error { if err := callback(&pb.RowResponse{ Headers: ci, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_StringVal{StringVal: pair.Key}}, - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(pair.Count)}}, + {ColumnVal: &pb.ColumnResponse_StringVal{StringVal: pair.Key}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(pair.Count)}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -549,8 +549,8 @@ func (p *PairsField) ToRows(callback func(*pb.RowResponse) error) error { if err := callback(&pb.RowResponse{ Headers: ci, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(pair.ID)}}, - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(pair.Count)}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(pair.ID)}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(pair.Count)}}, }}); err != nil { return errors.Wrap(err, "calling callback") } diff --git a/client/batch.go b/client/batch.go index 19cdc52d9..0b2ae36bb 100644 --- a/client/batch.go +++ b/client/batch.go @@ -672,9 +672,9 @@ func (b *Batch) Import() error { return errors.Wrap(err, "starting transaction") } defer func() { - trns, err := b.client.FinishTransaction(trns.ID) + trnsl, err := b.client.FinishTransaction(trns.ID) if err != nil { - b.log.Errorf("error finishing transaction: %v. trns: %+v", err, trns) + b.log.Errorf("error finishing transaction: %v. trns: %+v", err, trnsl) } b.client.Stats.Timing(MetricBatchImportDurationSeconds, time.Since(start), 1.0) }() @@ -729,9 +729,9 @@ func (b *Batch) Flush() error { return errors.Wrap(err, "starting transaction") } defer func() { - trns, err := b.client.FinishTransaction(trns.ID) + trnsl, err := b.client.FinishTransaction(trns.ID) if err != nil { - b.log.Errorf("error finishing transaction: %v. trns: %+v", err, trns) + b.log.Errorf("error finishing transaction: %v. trns: %+v", err, trnsl) } b.client.Stats.Timing(MetricBatchFlushDurationSeconds, time.Since(start), 1.0) }() @@ -1053,17 +1053,17 @@ func (b *Batch) doImport(frags, clearFrags fragments) error { eg.Go(func() error { clearViewMap := clearFrags.GetViewMap(shard, field) if len(clearViewMap) > 0 { - start := time.Now() + startx := time.Now() err := b.client.ImportRoaringBitmap(b.index.Field(field), shard, clearViewMap, true) if err != nil { return errors.Wrapf(err, "import clearing clearing data for %s", field) } - b.log.Debugf("imp-roar-clr %s,shard:%d,views:%d %v", field, shard, len(clearViewMap), time.Since(start)) + b.log.Debugf("imp-roar-clr %s,shard:%d,views:%d %v", field, shard, len(clearViewMap), time.Since(startx)) } - start := time.Now() + starty := time.Now() err := b.client.ImportRoaringBitmap(b.index.Field(field), shard, viewMap, false) - b.log.Debugf("imp-roar %s,shard:%d,views:%d %v", field, shard, len(clearViewMap), time.Since(start)) + b.log.Debugf("imp-roar %s,shard:%d,views:%d %v", field, shard, len(clearViewMap), time.Since(starty)) return errors.Wrapf(err, "importing data for %s", field) }) } diff --git a/client/client.go b/client/client.go index cac4b9ec3..4076069c2 100644 --- a/client/client.go +++ b/client/client.go @@ -180,8 +180,8 @@ func newClientWithCluster(cluster *Cluster, options *ClientOptions) *Client { func newClientWithURI(uri *pnet.URI, options *ClientOptions) *Client { client := newClientWithOptions(options) if options.manualServerAddress { - fragmentNode := newFragmentNodeFromURI(uri) - client.manualFragmentNode = &fragmentNode + fNode := newFragmentNodeFromURI(uri) + client.manualFragmentNode = &fNode client.manualServerURI = uri client.cluster = NewClusterWithHost() } diff --git a/client/orm_test.go b/client/orm_test.go index cbb7f0c0b..112d7556f 100644 --- a/client/orm_test.go +++ b/client/orm_test.go @@ -26,7 +26,7 @@ import ( "testing" "time" - "github.com/molecula/featurebase/v2" + pilosa "github.com/molecula/featurebase/v2" "github.com/molecula/featurebase/v2/pql" "github.com/pkg/errors" ) @@ -122,15 +122,15 @@ func TestORM(t *testing.T) { }) t.Run("NewIndexOptions", func(t *testing.T) { - schema := NewSchema() + schemal := NewSchema() // test the defaults - index := schema.Index("index-default-options") + index := schemal.Index("index-default-options") target := `{"options":{}}` if target != index.options.String() { t.Fatalf("%s != %s", target, index.options.String()) } - index = schema.Index("index-keys", OptIndexKeys(true)) + index = schemal.Index("index-keys", OptIndexKeys(true)) if true != index.Opts().Keys() { t.Fatalf("index keys %v != %v", true, index.Opts().Keys()) } @@ -139,7 +139,7 @@ func TestORM(t *testing.T) { t.Fatalf("%s != %s", target, index.options.String()) } - index = schema.Index("index-trackexistence", OptIndexTrackExistence(false)) + index = schemal.Index("index-trackexistence", OptIndexTrackExistence(false)) if false != index.Opts().TrackExistence() { t.Fatalf("index trackExistene %v != %v", true, index.Opts().TrackExistence()) } diff --git a/cluster.go b/cluster.go index 2843b732f..9b161ea41 100644 --- a/cluster.go +++ b/cluster.go @@ -1534,7 +1534,7 @@ func (c *cluster) translateFieldListIDs(field *Field, ids []uint64) (keys []stri // TODO: remove this when it is no longer used func (c *cluster) translateIndexKey(ctx context.Context, indexName string, key string, writable bool) (uint64, error) { - keyMap, err := c.translateIndexKeySet(ctx, indexName, map[string]struct{}{key: struct{}{}}, writable) + keyMap, err := c.translateIndexKeySet(ctx, indexName, map[string]struct{}{key: {}}, writable) if err != nil { return 0, err } diff --git a/cmd/roaring-migrate/ctim_linux.go b/cmd/roaring-migrate/ctim_linux.go index b9e5d70e1..09a68435c 100644 --- a/cmd/roaring-migrate/ctim_linux.go +++ b/cmd/roaring-migrate/ctim_linux.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build linux // +build linux package main diff --git a/const_amd64.go b/const_amd64.go index 5298800ff..e0cc22abb 100644 --- a/const_amd64.go +++ b/const_amd64.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build amd64 // +build amd64 package pilosa diff --git a/ctl/config.go b/ctl/config.go index ed89dd481..830e9f526 100644 --- a/ctl/config.go +++ b/ctl/config.go @@ -19,9 +19,9 @@ import ( "fmt" "io" - toml "github.com/pelletier/go-toml" "github.com/molecula/featurebase/v2" "github.com/molecula/featurebase/v2/server" + toml "github.com/pelletier/go-toml" ) // ConfigCommand represents a command for printing a default config. diff --git a/ctl/generate_config.go b/ctl/generate_config.go index f9f28bac9..c35dcc2b6 100644 --- a/ctl/generate_config.go +++ b/ctl/generate_config.go @@ -19,9 +19,9 @@ import ( "fmt" "io" - "github.com/pelletier/go-toml" "github.com/molecula/featurebase/v2" "github.com/molecula/featurebase/v2/server" + "github.com/pelletier/go-toml" "github.com/pkg/errors" ) diff --git a/executor.go b/executor.go index 82f215fef..f99a0f667 100644 --- a/executor.go +++ b/executor.go @@ -2620,7 +2620,7 @@ func (r RowIdentifiers) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_StringVal{StringVal: key}}, + {ColumnVal: &proto.ColumnResponse_StringVal{StringVal: key}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -2632,7 +2632,7 @@ func (r RowIdentifiers) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Uint64Val{Uint64Val: uint64(id)}}, + {ColumnVal: &proto.ColumnResponse_Uint64Val{Uint64Val: uint64(id)}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -7362,7 +7362,7 @@ func (s SignedRow) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: val}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: val}}, }, }); err != nil { return errors.Wrap(err, "calling callback") @@ -7380,7 +7380,7 @@ func (s SignedRow) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: val}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: val}}, }, }); err != nil { return errors.Wrap(err, "calling callback") @@ -7480,8 +7480,8 @@ func (v ValCount) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_DecimalVal{DecimalVal: &proto.Decimal{Value: v.DecimalVal.Value, Scale: v.DecimalVal.Scale}}}, - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, + {ColumnVal: &proto.ColumnResponse_DecimalVal{DecimalVal: &proto.Decimal{Value: v.DecimalVal.Value, Scale: v.DecimalVal.Scale}}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -7493,8 +7493,8 @@ func (v ValCount) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Float64Val{Float64Val: v.FloatVal}}, - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, + {ColumnVal: &proto.ColumnResponse_Float64Val{Float64Val: v.FloatVal}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -7506,8 +7506,8 @@ func (v ValCount) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_StringVal{StringVal: v.TimestampVal.Format(time.RFC3339Nano)}}, - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, + {ColumnVal: &proto.ColumnResponse_StringVal{StringVal: v.TimestampVal.Format(time.RFC3339Nano)}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -7519,8 +7519,8 @@ func (v ValCount) ToRows(callback func(*proto.RowResponse) error) error { if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Val}}, - &proto.ColumnResponse{ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Val}}, + {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, }}); err != nil { return errors.Wrap(err, "calling callback") } diff --git a/fragment.go b/fragment.go index d3c52c7dc..674f2da65 100644 --- a/fragment.go +++ b/fragment.go @@ -961,9 +961,9 @@ func (f *fragment) unprotectedClearBlock(tx Tx, block int) (changed bool, err er err = f.gen.Transaction(wp, func() error { var rowChanged bool for rowID := uint64(firstRow); rowID < firstRow+HashBlockSize; rowID++ { - if changed, err := f.unprotectedClearRow(tx, rowID); err != nil { + if chang, err := f.unprotectedClearRow(tx, rowID); err != nil { return errors.Wrapf(err, "clearing row: %d", rowID) - } else if changed { + } else if chang { rowChanged = true } } diff --git a/fragment_internal_test.go b/fragment_internal_test.go index 23c2063f1..2820ec57b 100644 --- a/fragment_internal_test.go +++ b/fragment_internal_test.go @@ -5375,7 +5375,7 @@ func TestFragmentConcurrentReadWrite(t *testing.T) { eg := &errgroup.Group{} eg.Go(func() error { - tx := idx.holder.txf.NewTx(Txo{Write: writable, Index: idx, Fragment: f, Shard: f.shard}) + ltx := idx.holder.txf.NewTx(Txo{Write: writable, Index: idx, Fragment: f, Shard: f.shard}) for i := uint64(0); i < 1000; i++ { _, err := f.setBit(tx, i%4, i) @@ -5383,7 +5383,7 @@ func TestFragmentConcurrentReadWrite(t *testing.T) { return errors.Wrap(err, "setting bit") } } - PanicOn(tx.Commit()) + PanicOn(ltx.Commit()) return nil }) diff --git a/generation_nodebug.go b/generation_nodebug.go index a2d2dd2f6..03ec5d5e3 100644 --- a/generation_nodebug.go +++ b/generation_nodebug.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !generationdebug // +build !generationdebug package pilosa diff --git a/hack.go b/hack.go index 21da2e87b..2dff6139a 100644 --- a/hack.go +++ b/hack.go @@ -15,7 +15,7 @@ package pilosa import ( -"github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/proto" "github.com/molecula/featurebase/v2/pb" "github.com/molecula/featurebase/v2/pql" ) diff --git a/holder.go b/holder.go index 756d75443..909bd6ecd 100644 --- a/holder.go +++ b/holder.go @@ -2082,7 +2082,7 @@ func (h *Holder) Process(ctx context.Context, op HolderOperator) (err error) { if !recurse { continue } - fragNums := fragNums[:0] + fragNums = fragNums[:0] view.mu.Lock() for fragNum := range view.fragments { fragNums = append(fragNums, fragNum) diff --git a/http/handler.go b/http/handler.go index 59a3c3162..a9c5d2010 100644 --- a/http/handler.go +++ b/http/handler.go @@ -1326,7 +1326,7 @@ func (h *Handler) handlePostField(w http.ResponseWriter, r *http.Request) { fos := fieldOptionsToFunctionalOpts(req.Options) field, err := h.api.CreateField(r.Context(), indexName, fieldName, fos...) - if _, ok := err.(pilosa.BadRequestError); ok { + if _, ok = err.(pilosa.BadRequestError); ok { http.Error(w, err.Error(), http.StatusBadRequest) return } diff --git a/rbf.go b/rbf.go index cee25f35c..910ecce78 100644 --- a/rbf.go +++ b/rbf.go @@ -254,25 +254,25 @@ func (tx *RBFTx) addOrRemove(index, field, view string, shard uint64, remove boo if rc.N() == 0 { return 0, nil } - rc, chng := rc.Remove(lo) + rc1, chng := rc.Remove(lo) if !chng { return 0, nil } - if rc.N() == 0 { + if rc1.N() == 0 { err = tx.tx.RemoveContainer(name, hi) } else { - err = tx.tx.PutContainer(name, hi, rc) + err = tx.tx.PutContainer(name, hi, rc1) } if err != nil { return 0, err } return 1, nil } else { - rc, chng := rc.Add(lo) + rc2, chng := rc.Add(lo) if !chng { return 0, nil } - err = tx.tx.PutContainer(name, hi, rc) + err = tx.tx.PutContainer(name, hi, rc2) if err != nil { return 0, err } diff --git a/rbf/cursor_test.go b/rbf/cursor_test.go index 8c321fcec..9ed2b2209 100644 --- a/rbf/cursor_test.go +++ b/rbf/cursor_test.go @@ -558,10 +558,12 @@ func TestCursor_RLETesting(t *testing.T) { } want := []uint16{0} - if got, want := c.Values(), want; !reflect.DeepEqual(got, want) { + got := c.Values() + if !reflect.DeepEqual(got, want) { t.Fatalf("Values()=%#v, want %#v", got, want) - } else if got, want := c.Key(), uint64(1); !reflect.DeepEqual(got, want) { - t.Fatalf("Key()=%#v, want %#v", got, want) + } + if g, w := c.Key(), uint64(1); !reflect.DeepEqual(g, w) { + t.Fatalf("Key()=%#v, want %#v", g, w) } }) } @@ -1140,7 +1142,7 @@ func TestForEachRange(t *testing.T) { } rb.Put(0, roaring.NewContainerBitmap(n, bits)) - crun := roaring.NewContainerRun([]roaring.Interval16{roaring.Interval16{Start: 0, Last: 1<<16 - 1}}) + crun := roaring.NewContainerRun([]roaring.Interval16{{Start: 0, Last: 1<<16 - 1}}) rb.Put(1, crun) rb.Put(2, roaring.NewContainerArray([]uint16{1, 1024, 1<<16 - 1})) diff --git a/rbf/dot.go b/rbf/dot.go index 74953e6ff..fa815805e 100644 --- a/rbf/dot.go +++ b/rbf/dot.go @@ -78,8 +78,8 @@ func dumpdot(tx *Tx, pgno uint32, parent string, writer io.Writer) { for _, record := range records { root := fmt.Sprintf("root%d", record.Pgno) fmt.Fprintf(writer, "%s[label=\"ROOT(%d)| %s\"]\n%s->%s\n", root, record.Pgno, record.Name, rr, root) - parent := fmt.Sprintf("root%d", record.Pgno) - dumpdot(tx, record.Pgno, parent, writer) + p := fmt.Sprintf("root%d", record.Pgno) + dumpdot(tx, record.Pgno, p, writer) } } diff --git a/rbf/rbf_test.go b/rbf/rbf_test.go index a5baff1e7..5626e67cd 100644 --- a/rbf/rbf_test.go +++ b/rbf/rbf_test.go @@ -39,7 +39,7 @@ func TestReadWriteRootRecord(t *testing.T) { // Write records. if remaining, err := rbf.WriteRootRecord(buf, &rbf.RootRecord{Pgno: 10, Name: "foo"}); err != nil { t.Fatal(err) - } else if remaining, err := rbf.WriteRootRecord(remaining, &rbf.RootRecord{Pgno: 11, Name: "bar"}); err != nil { + } else if remaining, err = rbf.WriteRootRecord(remaining, &rbf.RootRecord{Pgno: 11, Name: "bar"}); err != nil { t.Fatal(err) } else if _, err := rbf.WriteRootRecord(remaining, &rbf.RootRecord{Pgno: 12, Name: "baz"}); err != io.ErrShortBuffer { t.Fatalf("unexpected error: %#v", err) // buffer too short @@ -50,7 +50,7 @@ func TestReadWriteRootRecord(t *testing.T) { t.Fatal(err) } else if got, want := *rec, (rbf.RootRecord{Pgno: 10, Name: "foo"}); got != want { t.Fatalf("ReadRootRecord=%#v, want %#v", got, want) - } else if rec, remaining, err := rbf.ReadRootRecord(remaining); err != nil { + } else if rec, remaining, err = rbf.ReadRootRecord(remaining); err != nil { t.Fatal(err) } else if got, want := *rec, (rbf.RootRecord{Pgno: 11, Name: "bar"}); got != want { t.Fatalf("ReadRootRecord=%#v, want %#v", got, want) diff --git a/roaring/add_test.go b/roaring/add_test.go index 1da117c56..46e1e0d62 100644 --- a/roaring/add_test.go +++ b/roaring/add_test.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !race // +build !race package roaring diff --git a/roaring/generation_nodebug.go b/roaring/generation_nodebug.go index 4ce3f4ab1..7b507dcbe 100644 --- a/roaring/generation_nodebug.go +++ b/roaring/generation_nodebug.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !generationdebug // +build !generationdebug package roaring diff --git a/roaring/nop_inst.go b/roaring/nop_inst.go index ad02831f8..73a8bd99e 100644 --- a/roaring/nop_inst.go +++ b/roaring/nop_inst.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !btreeInstrumentation // +build !btreeInstrumentation package roaring diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index c23adc6f4..b6a6bb262 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -99,9 +99,10 @@ func TestContainerRunAdd(t *testing.T) { {0, []Interval16{{Start: 0, Last: 4}, {Start: 6, Last: 7}, {Start: 10, Last: 10}}}, {8, []Interval16{{Start: 0, Last: 4}, {Start: 6, Last: 8}, {Start: 10, Last: 10}}}, } + var changed bool for _, test := range tests { c.setMapped(true) - c, changed := c.add(test.op) + c, changed = c.add(test.op) if !changed { t.Fatalf("result of adding new bit should be true: %v", c.runs()) } @@ -731,65 +732,65 @@ func TestUnionInterval16InPlace(t *testing.T) { }{ { name: "firstBitUnset lastBitSet", - a: []Interval16{Interval16{1, 10}}, - b: []Interval16{Interval16{10, 10}}, - expected: []Interval16{Interval16{1, 10}}, + a: []Interval16{{1, 10}}, + b: []Interval16{{10, 10}}, + expected: []Interval16{{1, 10}}, expectedN: 10, }, { name: "single overlap", - a: []Interval16{Interval16{1, 10}, Interval16{21, 28}}, - b: []Interval16{Interval16{8, 12}}, - expected: []Interval16{Interval16{1, 12}, Interval16{21, 28}}, + a: []Interval16{{1, 10}, {21, 28}}, + b: []Interval16{{8, 12}}, + expected: []Interval16{{1, 12}, {21, 28}}, expectedN: 20, }, { name: "nested intervals", - a: []Interval16{Interval16{3, 13}, Interval16{17, 20}}, - b: []Interval16{Interval16{1, 4}, Interval16{6, 7}, Interval16{8, 9}, Interval16{10, 11}, Interval16{14, 17}}, - expected: []Interval16{Interval16{1, 20}}, + a: []Interval16{{3, 13}, {17, 20}}, + b: []Interval16{{1, 4}, {6, 7}, {8, 9}, {10, 11}, {14, 17}}, + expected: []Interval16{{1, 20}}, expectedN: 20, }, { name: "no overlap", - a: []Interval16{Interval16{3, 4}, Interval16{7, 8}}, - b: []Interval16{Interval16{1, 2}, Interval16{5, 6}, Interval16{9, 10}}, - expected: []Interval16{Interval16{1, 10}}, + a: []Interval16{{3, 4}, {7, 8}}, + b: []Interval16{{1, 2}, {5, 6}, {9, 10}}, + expected: []Interval16{{1, 10}}, expectedN: 10, }, { name: "b in a", - a: []Interval16{Interval16{1, 10}}, - b: []Interval16{Interval16{5, 7}}, - expected: []Interval16{Interval16{1, 10}}, + a: []Interval16{{1, 10}}, + b: []Interval16{{5, 7}}, + expected: []Interval16{{1, 10}}, expectedN: 10, }, { name: "a eq b", - a: []Interval16{Interval16{1, 10}}, - b: []Interval16{Interval16{1, 10}}, - expected: []Interval16{Interval16{1, 10}}, + a: []Interval16{{1, 10}}, + b: []Interval16{{1, 10}}, + expected: []Interval16{{1, 10}}, expectedN: 10, }, { name: "a in b", - a: []Interval16{Interval16{5, 7}}, - b: []Interval16{Interval16{1, 10}}, - expected: []Interval16{Interval16{1, 10}}, + a: []Interval16{{5, 7}}, + b: []Interval16{{1, 10}}, + expected: []Interval16{{1, 10}}, expectedN: 10, }, { name: "a ahead b", - a: []Interval16{Interval16{1, 2}, Interval16{3, 4}, Interval16{5, 7}}, - b: []Interval16{Interval16{10, 11}, Interval16{12, 13}, Interval16{14, 15}}, - expected: []Interval16{Interval16{1, 7}, Interval16{10, 15}}, + a: []Interval16{{1, 2}, {3, 4}, {5, 7}}, + b: []Interval16{{10, 11}, {12, 13}, {14, 15}}, + expected: []Interval16{{1, 7}, {10, 15}}, expectedN: 13, }, { name: "b ahead a", - a: []Interval16{Interval16{10, 11}, Interval16{12, 13}, Interval16{14, 15}}, - b: []Interval16{Interval16{1, 2}, Interval16{3, 4}, Interval16{5, 7}}, - expected: []Interval16{Interval16{1, 7}, Interval16{10, 15}}, + a: []Interval16{{10, 11}, {12, 13}, {14, 15}}, + b: []Interval16{{1, 2}, {3, 4}, {5, 7}}, + expected: []Interval16{{1, 7}, {10, 15}}, expectedN: 13, }, { @@ -802,78 +803,78 @@ func TestUnionInterval16InPlace(t *testing.T) { { name: "empty a", a: []Interval16{}, - b: []Interval16{Interval16{1, 2}, Interval16{3, 4}, Interval16{5, 7}}, - expected: []Interval16{Interval16{1, 7}}, + b: []Interval16{{1, 2}, {3, 4}, {5, 7}}, + expected: []Interval16{{1, 7}}, expectedN: 7, }, { name: "empty b", - a: []Interval16{Interval16{1, 2}, Interval16{3, 4}, Interval16{5, 7}}, + a: []Interval16{{1, 2}, {3, 4}, {5, 7}}, b: []Interval16{}, - expected: []Interval16{Interval16{1, 7}}, + expected: []Interval16{{1, 7}}, expectedN: 7, }, { name: "single a", - a: []Interval16{Interval16{1, 2}}, + a: []Interval16{{1, 2}}, b: []Interval16{}, - expected: []Interval16{Interval16{1, 2}}, + expected: []Interval16{{1, 2}}, expectedN: 2, }, { name: "single b", a: []Interval16{}, - b: []Interval16{Interval16{1, 2}}, - expected: []Interval16{Interval16{1, 2}}, + b: []Interval16{{1, 2}}, + expected: []Interval16{{1, 2}}, expectedN: 2, }, { name: "single a single b", - a: []Interval16{Interval16{3, 4}}, - b: []Interval16{Interval16{1, 2}}, - expected: []Interval16{Interval16{1, 4}}, + a: []Interval16{{3, 4}}, + b: []Interval16{{1, 2}}, + expected: []Interval16{{1, 4}}, expectedN: 4, }, { name: "oddBitsSet lastBitUnset", - a: []Interval16{Interval16{1, 1}, Interval16{3, 3}, Interval16{5, 5}}, - b: []Interval16{Interval16{0, 4}}, - expected: []Interval16{Interval16{0, 5}}, + a: []Interval16{{1, 1}, {3, 3}, {5, 5}}, + b: []Interval16{{0, 4}}, + expected: []Interval16{{0, 5}}, expectedN: 6, }, { name: "all bits", - a: []Interval16{Interval16{1, 1}, Interval16{3, 3}, Interval16{5, 5}}, - b: []Interval16{Interval16{0, 0}, Interval16{2, 2}, Interval16{4, 4}}, - expected: []Interval16{Interval16{0, 5}}, + a: []Interval16{{1, 1}, {3, 3}, {5, 5}}, + b: []Interval16{{0, 0}, {2, 2}, {4, 4}}, + expected: []Interval16{{0, 5}}, expectedN: 6, }, { name: "short a long b", - a: []Interval16{Interval16{5, 5}, Interval16{7, 7}, Interval16{9, 10}, Interval16{12, 12}, Interval16{15, 17}, Interval16{19, 20}}, - b: []Interval16{Interval16{1, 10}, Interval16{12, 12}, Interval16{14, 18}}, - expected: []Interval16{Interval16{1, 10}, Interval16{12, 12}, Interval16{14, 20}}, + a: []Interval16{{5, 5}, {7, 7}, {9, 10}, {12, 12}, {15, 17}, {19, 20}}, + b: []Interval16{{1, 10}, {12, 12}, {14, 18}}, + expected: []Interval16{{1, 10}, {12, 12}, {14, 20}}, expectedN: 18, }, { name: "common endings", - a: []Interval16{Interval16{1, 5}, Interval16{15, 20}, Interval16{25, 35}}, - b: []Interval16{Interval16{1, 10}, Interval16{15, 20}, Interval16{30, 35}}, - expected: []Interval16{Interval16{1, 10}, Interval16{15, 20}, Interval16{25, 35}}, + a: []Interval16{{1, 5}, {15, 20}, {25, 35}}, + b: []Interval16{{1, 10}, {15, 20}, {30, 35}}, + expected: []Interval16{{1, 10}, {15, 20}, {25, 35}}, expectedN: 27, }, { name: "common endings and overlap", - a: []Interval16{Interval16{1, 5}, Interval16{10, 15}}, - b: []Interval16{Interval16{5, 10}, Interval16{12, 17}}, - expected: []Interval16{Interval16{1, 17}}, + a: []Interval16{{1, 5}, {10, 15}}, + b: []Interval16{{5, 10}, {12, 17}}, + expected: []Interval16{{1, 17}}, expectedN: 17, }, { name: "no common endings and overlap", - a: []Interval16{Interval16{5, 10}, Interval16{12, 17}}, - b: []Interval16{Interval16{0, 11}, Interval16{15, 20}}, - expected: []Interval16{Interval16{0, 20}}, + a: []Interval16{{5, 10}, {12, 17}}, + b: []Interval16{{0, 11}, {15, 20}}, + expected: []Interval16{{0, 20}}, expectedN: 21, }, } diff --git a/roaring/roaring_nop_paranoia.go b/roaring/roaring_nop_paranoia.go index d89ade062..c37e6b9ec 100644 --- a/roaring/roaring_nop_paranoia.go +++ b/roaring/roaring_nop_paranoia.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !roaringparanoia // +build !roaringparanoia package roaring diff --git a/roaring/roaring_nop_sentinel.go b/roaring/roaring_nop_sentinel.go index daa0b266b..cba2ec106 100644 --- a/roaring/roaring_nop_sentinel.go +++ b/roaring/roaring_nop_sentinel.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !roaringsentinel // +build !roaringsentinel package roaring diff --git a/roaring/roaring_nop_stats.go b/roaring/roaring_nop_stats.go index 2b1506953..c25ab553b 100644 --- a/roaring/roaring_nop_stats.go +++ b/roaring/roaring_nop_stats.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !roaringstats // +build !roaringstats package roaring diff --git a/row.go b/row.go index 022ee76f3..89bec66b1 100644 --- a/row.go +++ b/row.go @@ -146,7 +146,7 @@ func (r *Row) ToRows(callback func(*pb.RowResponse) error) error { if err := callback(&pb.RowResponse{ Headers: ci, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_StringVal{StringVal: x}}, + {ColumnVal: &pb.ColumnResponse_StringVal{StringVal: x}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -161,7 +161,7 @@ func (r *Row) ToRows(callback func(*pb.RowResponse) error) error { if err := callback(&pb.RowResponse{ Headers: ci, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: x}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: x}}, }}); err != nil { return errors.Wrap(err, "calling callback") } @@ -308,7 +308,7 @@ func (r *Row) Union(others ...*Row) *Row { } } nextSegs = nextSegs[:0] - toProcess := toProcess[:0] + toProcess = toProcess[:0] for _, segs := range segments { if segs[0].shard == shard { toProcess = append(toProcess, &segs[0]) diff --git a/server/dup.go b/server/dup.go index 55c918758..d6b2fe32c 100644 --- a/server/dup.go +++ b/server/dup.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build darwin || (linux && !arm64) // +build darwin linux,!arm64 package server diff --git a/server/grpc.go b/server/grpc.go index f2f4d3246..270a5dd47 100644 --- a/server/grpc.go +++ b/server/grpc.go @@ -471,7 +471,7 @@ func (r ResultUint64) ToRows(callback func(*pb.RowResponse) error) error { return callback(&pb.RowResponse{ Headers: []*pb.ColumnInfo{{Name: "count", Datatype: "uint64"}}, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(r)}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: uint64(r)}}, }}) } @@ -490,7 +490,7 @@ func (r ResultBool) ToRows(callback func(*pb.RowResponse) error) error { return callback(&pb.RowResponse{ Headers: []*pb.ColumnInfo{{Name: "result", Datatype: "bool"}}, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_BoolVal{BoolVal: bool(r)}}, + {ColumnVal: &pb.ColumnResponse_BoolVal{BoolVal: bool(r)}}, }}) } @@ -714,7 +714,7 @@ func (h *GRPCHandler) Inspect(req *pb.InspectRequest, stream pb.Pilosa_InspectSe rowResp := &pb.RowResponse{ Headers: ci, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: col}}, + {ColumnVal: &pb.ColumnResponse_Uint64Val{Uint64Val: col}}, }, } ci = nil // only include headers with the first row @@ -1029,7 +1029,7 @@ func (h *GRPCHandler) Inspect(req *pb.InspectRequest, stream pb.Pilosa_InspectSe rowResp := &pb.RowResponse{ Headers: ci, Columns: []*pb.ColumnResponse{ - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_StringVal{StringVal: col}}, + {ColumnVal: &pb.ColumnResponse_StringVal{StringVal: col}}, }, } ci = nil // only include headers with the first row diff --git a/server/grpc_test.go b/server/grpc_test.go index 19353838f..5bb39902a 100644 --- a/server/grpc_test.go +++ b/server/grpc_test.go @@ -141,21 +141,21 @@ func TestGRPC(t *testing.T) { // []GroupCount (uint64) { pilosa.NewGroupCounts("", []pilosa.GroupCount{ - pilosa.GroupCount{ + { Group: []pilosa.FieldRow{ {Field: "a", RowID: 10}, {Field: "b", RowID: 11}, }, Count: 123, }, - pilosa.GroupCount{ + { Group: []pilosa.FieldRow{ {Field: "a", RowID: 10}, {Field: "b", RowID: 12}, }, Count: 456, }, - pilosa.GroupCount{ + { Group: []pilosa.FieldRow{ {Field: "va", Value: &va}, {Field: "vb", Value: &vb}, @@ -177,7 +177,7 @@ func TestGRPC(t *testing.T) { // []GroupCount (string) + sum { pilosa.NewGroupCounts("sum", []pilosa.GroupCount{ - pilosa.GroupCount{ + { Group: []pilosa.FieldRow{ {Field: "a", RowKey: "ten"}, {Field: "b", RowKey: "eleven"}, diff --git a/server/handler_test.go b/server/handler_test.go index ea6c74e4f..da64d0004 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -32,7 +32,7 @@ import ( "testing" "time" - "github.com/molecula/featurebase/v2" + pilosa "github.com/molecula/featurebase/v2" "github.com/molecula/featurebase/v2/boltdb" "github.com/molecula/featurebase/v2/encoding/proto" "github.com/molecula/featurebase/v2/http" @@ -1104,7 +1104,7 @@ func TestHandler_Endpoints(t *testing.T) { clus := test.MustRunCluster(t, 1, []server.CommandOption{test.OptAllowedOrigins([]string{"http://test/"})}) defer clus.Close() w = httptest.NewRecorder() - h := clus.GetNode(0).Handler.(*http.Handler).Handler + h = clus.GetNode(0).Handler.(*http.Handler).Handler h.ServeHTTP(w, req) result = w.Result() diff --git a/sql/select.go b/sql/select.go index ca4b22042..1a3a829f9 100644 --- a/sql/select.go +++ b/sql/select.go @@ -19,7 +19,7 @@ import ( "fmt" "strings" - "github.com/molecula/featurebase/v2" + pilosa "github.com/molecula/featurebase/v2" "github.com/molecula/featurebase/v2/pql" pproto "github.com/molecula/featurebase/v2/proto" "github.com/pkg/errors" @@ -56,8 +56,8 @@ func (s *SelectHandler) Handle(ctx context.Context, mapped *MappedSQL) (pproto.T func (s *SelectHandler) mapSelect(ctx context.Context, selectStmt *sqlparser.Select, qm QueryMask) (*MappingResult, error) { // Get the handler for this query mask. - handler := s.router.handler(qm) - if handler == nil { + hndlr := s.router.handler(qm) + if hndlr == nil { return nil, ErrUnsupportedQuery } indexFunc := func(indexName string) *pilosa.Index { @@ -68,7 +68,7 @@ func (s *SelectHandler) mapSelect(ctx context.Context, selectStmt *sqlparser.Sel return idx } - mr, err := handler.Apply(selectStmt, qm, indexFunc) + mr, err := hndlr.Apply(selectStmt, qm, indexFunc) if err != nil { return nil, errors.Wrap(err, "handling") } diff --git a/sql2/parser_test.go b/sql2/parser_test.go index 67927515d..0d371954f 100644 --- a/sql2/parser_test.go +++ b/sql2/parser_test.go @@ -569,7 +569,7 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), }, @@ -586,11 +586,11 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ - &sql.ForeignKeyArg{ + { On: pos(50), OnDelete: pos(53), Set: pos(60), @@ -608,11 +608,11 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ - &sql.ForeignKeyArg{ + { On: pos(50), OnDelete: pos(53), Set: pos(60), @@ -630,11 +630,11 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ - &sql.ForeignKeyArg{ + { On: pos(50), OnDelete: pos(53), Cascade: pos(60), @@ -651,11 +651,11 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ - &sql.ForeignKeyArg{ + { On: pos(50), OnDelete: pos(53), Restrict: pos(60), @@ -672,11 +672,11 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ - &sql.ForeignKeyArg{ + { On: pos(50), OnDelete: pos(53), No: pos(60), @@ -694,16 +694,16 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Args: []*sql.ForeignKeyArg{ - &sql.ForeignKeyArg{ + { On: pos(50), OnDelete: pos(53), Cascade: pos(60), }, - &sql.ForeignKeyArg{ + { On: pos(68), OnUpdate: pos(71), Restrict: pos(78), @@ -720,7 +720,7 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Deferrable: pos(50), @@ -735,7 +735,7 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Not: pos(50), @@ -751,7 +751,7 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Deferrable: pos(50), @@ -768,7 +768,7 @@ func TestParser_ParseStatement(t *testing.T) { ForeignTable: &sql.Ident{Name: "foo", NamePos: pos(39)}, ForeignLparen: pos(43), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "col2", NamePos: pos(44)}, + {Name: "col2", NamePos: pos(44)}, }, ForeignRparen: pos(48), Deferrable: pos(50), @@ -802,8 +802,8 @@ func TestParser_ParseStatement(t *testing.T) { Key: pos(37), Lparen: pos(41), Columns: []*sql.Ident{ - &sql.Ident{Name: "col1", NamePos: pos(42)}, - &sql.Ident{Name: "col2", NamePos: pos(48)}, + {Name: "col1", NamePos: pos(42)}, + {Name: "col2", NamePos: pos(48)}, }, Rparen: pos(52), }, @@ -838,8 +838,8 @@ func TestParser_ParseStatement(t *testing.T) { Unique: pos(45), Lparen: pos(52), Columns: []*sql.Ident{ - &sql.Ident{Name: "col1", NamePos: pos(53)}, - &sql.Ident{Name: "col2", NamePos: pos(59)}, + {Name: "col1", NamePos: pos(53)}, + {Name: "col2", NamePos: pos(59)}, }, Rparen: pos(63), }, @@ -903,16 +903,16 @@ func TestParser_ParseStatement(t *testing.T) { ForeignKey: pos(37), Lparen: pos(41), Columns: []*sql.Ident{ - &sql.Ident{Name: "col1", NamePos: pos(42)}, - &sql.Ident{Name: "col2", NamePos: pos(48)}, + {Name: "col1", NamePos: pos(42)}, + {Name: "col2", NamePos: pos(48)}, }, Rparen: pos(52), References: pos(54), ForeignTable: &sql.Ident{Name: "tbl2", NamePos: pos(65)}, ForeignLparen: pos(70), ForeignColumns: []*sql.Ident{ - &sql.Ident{Name: "x", NamePos: pos(71)}, - &sql.Ident{Name: "y", NamePos: pos(74)}, + {Name: "x", NamePos: pos(71)}, + {Name: "y", NamePos: pos(74)}, }, ForeignRparen: pos(75), }, @@ -963,8 +963,8 @@ func TestParser_ParseStatement(t *testing.T) { Name: &sql.Ident{NamePos: pos(12), Name: "vw"}, Lparen: pos(15), Columns: []*sql.Ident{ - &sql.Ident{NamePos: pos(16), Name: "col1"}, - &sql.Ident{NamePos: pos(22), Name: "col2"}, + {NamePos: pos(16), Name: "col1"}, + {NamePos: pos(22), Name: "col2"}, }, Rparen: pos(26), As: pos(28), @@ -1709,8 +1709,8 @@ func TestParser_ParseStatement(t *testing.T) { Order: pos(9), OrderBy: pos(15), OrderingTerms: []*sql.OrderingTerm{ - &sql.OrderingTerm{X: &sql.Ident{NamePos: pos(18), Name: "foo"}, Asc: pos(22)}, - &sql.OrderingTerm{X: &sql.Ident{NamePos: pos(27), Name: "bar"}, Desc: pos(31)}, + {X: &sql.Ident{NamePos: pos(18), Name: "foo"}, Asc: pos(22)}, + {X: &sql.Ident{NamePos: pos(27), Name: "bar"}, Desc: pos(31)}, }, }) @@ -1757,7 +1757,7 @@ func TestParser_ParseStatement(t *testing.T) { Order: pos(24), OrderBy: pos(30), OrderingTerms: []*sql.OrderingTerm{ - &sql.OrderingTerm{X: &sql.Ident{NamePos: pos(33), Name: "foo"}}, + {X: &sql.Ident{NamePos: pos(33), Name: "foo"}}, }, }) AssertParseStatement(t, `SELECT * UNION ALL SELECT *`, &sql.SelectStatement{ diff --git a/sql2/walk.go b/sql2/walk.go index 30eafd03c..855103987 100644 --- a/sql2/walk.go +++ b/sql2/walk.go @@ -228,9 +228,7 @@ func walk(v Visitor, node Node) (_ Node, err error) { if n.Source != nil { if src, err := walk(v, n.Source); err != nil { return node, err - } else if src != nil { - n.Source = n.Source - } else { + } else if src == nil { n.Source = nil } } diff --git a/syswrap/mmap.go b/syswrap/mmap.go index 2d89ce39b..2858183b2 100644 --- a/syswrap/mmap.go +++ b/syswrap/mmap.go @@ -17,10 +17,10 @@ package syswrap import ( + "strings" "sync" "sync/atomic" "syscall" - "strings" "github.com/pkg/errors" ) diff --git a/time.go b/time.go index 4253a4d1e..403f00da6 100644 --- a/time.go +++ b/time.go @@ -288,7 +288,7 @@ func parseTime(t interface{}) (time.Time, error) { if calcTime, err = time.Parse(TimeFormat, v); err != nil { // if the default parsing fails, check if user tried to // supply partial time eg year and month - calcTime, err := parsePartialTime(v) + calcTime, err = parsePartialTime(v) return calcTime, err } case int64: diff --git a/tracing/opentracing/opentracing.go b/tracing/opentracing/opentracing.go index a0afe8669..ae336a5d0 100644 --- a/tracing/opentracing/opentracing.go +++ b/tracing/opentracing/opentracing.go @@ -18,10 +18,10 @@ import ( "context" "net/http" - "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/ext" "github.com/molecula/featurebase/v2/logger" "github.com/molecula/featurebase/v2/tracing" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" ) // Ensure type implements interface. diff --git a/translator_test.go b/translator_test.go index 2144d2b9a..7d8b09a42 100644 --- a/translator_test.go +++ b/translator_test.go @@ -24,7 +24,7 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/molecula/featurebase/v2" + pilosa "github.com/molecula/featurebase/v2" "github.com/molecula/featurebase/v2/boltdb" "github.com/molecula/featurebase/v2/http" "github.com/molecula/featurebase/v2/mock" @@ -550,9 +550,9 @@ func TestTranslation_Cluster_CreateFind(t *testing.T) { var g errgroup.Group defer g.Wait() //nolint:errcheck for i, n := range c.Nodes { - i, api := i, n.API + x, api := i, n.API g.Go(func() (err error) { - defer func() { err = errors.Wrapf(err, "translating on node %d", i) }() + defer func() { err = errors.Wrapf(err, "translating on node %d", x) }() localTranslations, err := api.FindIndexKeys(ctx, "i", keyList...) if err != nil { return errors.Wrap(err, "finding translations") @@ -567,9 +567,9 @@ func TestTranslation_Cluster_CreateFind(t *testing.T) { // Check that re-invoking create returns the original translations. for i, n := range c.Nodes { - i, api := i, n.API + x, api := i, n.API g.Go(func() (err error) { - defer func() { err = errors.Wrapf(err, "translating on node %d", i) }() + defer func() { err = errors.Wrapf(err, "translating on node %d", x) }() localTranslations, err := api.CreateIndexKeys(ctx, "i", keyList...) if err != nil { return errors.Wrap(err, "finding translations") @@ -636,9 +636,9 @@ func TestTranslation_Cluster_CreateFind(t *testing.T) { var g errgroup.Group defer g.Wait() //nolint:errcheck for i, n := range c.Nodes { - i, api := i, n.API + x, api := i, n.API g.Go(func() (err error) { - defer func() { err = errors.Wrapf(err, "translating on node %d", i) }() + defer func() { err = errors.Wrapf(err, "translating on node %d", x) }() localTranslations, err := api.FindFieldKeys(ctx, "i", "f", keyList...) if err != nil { return errors.Wrap(err, "finding translations") @@ -653,9 +653,9 @@ func TestTranslation_Cluster_CreateFind(t *testing.T) { // Check that re-invoking create returns the original translations. for i, n := range c.Nodes { - i, api := i, n.API + x, api := i, n.API g.Go(func() (err error) { - defer func() { err = errors.Wrapf(err, "translating on node %d", i) }() + defer func() { err = errors.Wrapf(err, "translating on node %d", x) }() localTranslations, err := api.CreateFieldKeys(ctx, "i", "f", keyList...) if err != nil { return errors.Wrap(err, "finding translations") From 7ca714e6d1b9cb4409362c26d8b3b9a7d403787a Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 13:50:41 -0500 Subject: [PATCH 50/76] added the linter back to circle --- .circleci/config.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8524a0afd..30b2ef5e4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,6 +70,14 @@ jobs: - checkout-plus - skip-if-root-unchanged - run: make check-license-headers + linter: + executor: + name: golang + steps: + - checkout-plus + - skip-if-root-unchanged + - run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/local/bin v1.31.0 + - run: make golangci-lint go-mod-tidy: executor: name: golang @@ -227,6 +235,10 @@ workflows: filters: tags: only: /^v.*/ + - linter: + context: molecula + requires: + - setup - check-license-headers: context: molecula requires: From 2d33c40a501ca48a505dc59e12739d9752d28ba4 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 15:08:09 -0500 Subject: [PATCH 51/76] maybe without before? --- .gitlab/.gitlab-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 56df3a822..3345215db 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -15,13 +15,13 @@ stages: - test - build -before_script: - - echo "before_script" - - git version - - go env -w GOPRIVATE=github.com/molecula - - mkdir -p .go - - go version - - go env -w GO111MODULE=on + #before_script: + #- echo "before_script" + #- git version + #- go env -w GOPRIVATE=github.com/molecula + #- mkdir -p .go + #- go version + #- go env -w GO111MODULE=on golangci-lint: image: golangci/golangci-lint:v1.39.0 From bba60eb20d52146d233975ecc1e7d35aff8acad1 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Fri, 29 Oct 2021 17:01:45 -0500 Subject: [PATCH 52/76] add key to cache --- .gitlab/.gitlab-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 3345215db..40c077fcc 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -7,8 +7,9 @@ include: variables: GOPATH: $CI_PROJECT_DIR/.go cache: - paths: - - .go/pkg/mod/ + - key: $CI_COMMIT_REF_SLUG + paths: + - .go/pkg/mod/ stages: - lint From 521ada8e709e866c29beb7c0862f9aa3f988d267 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Sat, 30 Oct 2021 10:44:23 -0500 Subject: [PATCH 53/76] bug with shadow correction --- fragment_internal_test.go | 8 ++++---- main_test.go | 2 +- server/handler_test.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fragment_internal_test.go b/fragment_internal_test.go index 2820ec57b..a878091b7 100644 --- a/fragment_internal_test.go +++ b/fragment_internal_test.go @@ -5378,7 +5378,7 @@ func TestFragmentConcurrentReadWrite(t *testing.T) { ltx := idx.holder.txf.NewTx(Txo{Write: writable, Index: idx, Fragment: f, Shard: f.shard}) for i := uint64(0); i < 1000; i++ { - _, err := f.setBit(tx, i%4, i) + _, err := f.setBit(ltx, i%4, i) if err != nil { return errors.Wrap(err, "setting bit") } @@ -5388,12 +5388,12 @@ func TestFragmentConcurrentReadWrite(t *testing.T) { }) // need read-only Tx so as not to block on the writer finishing above. - tx = idx.holder.txf.NewTx(Txo{Write: !writable, Index: idx, Fragment: f, Shard: f.shard}) - defer tx.Rollback() + tx1 := idx.holder.txf.NewTx(Txo{Write: !writable, Index: idx, Fragment: f, Shard: f.shard}) + defer tx1.Rollback() acc := uint64(0) for i := uint64(0); i < 100; i++ { - r := f.mustRow(tx, i%4) + r := f.mustRow(tx1, i%4) acc += r.Count() } if err := eg.Wait(); err != nil { diff --git a/main_test.go b/main_test.go index 90f3cf214..73254f4e8 100644 --- a/main_test.go +++ b/main_test.go @@ -17,9 +17,9 @@ package pilosa_test import ( "fmt" "net" - "net/http" "testing" + "net/http" _ "net/http/pprof" "github.com/molecula/featurebase/v2/testhook" diff --git a/server/handler_test.go b/server/handler_test.go index da64d0004..25fe90c99 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -1104,8 +1104,8 @@ func TestHandler_Endpoints(t *testing.T) { clus := test.MustRunCluster(t, 1, []server.CommandOption{test.OptAllowedOrigins([]string{"http://test/"})}) defer clus.Close() w = httptest.NewRecorder() - h = clus.GetNode(0).Handler.(*http.Handler).Handler - h.ServeHTTP(w, req) + h1 := clus.GetNode(0).Handler.(*http.Handler).Handler + h1.ServeHTTP(w, req) result = w.Result() if result.StatusCode != 200 { From f6f8db941d0127a72b9a2a2b105fad087f31c6c7 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Sat, 30 Oct 2021 11:52:46 -0500 Subject: [PATCH 54/76] add tes for sonar? --- roaring/roaring_internal_test.go | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index b6a6bb262..e8899a339 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -4737,3 +4737,61 @@ func TestIntersectionCallback(t *testing.T) { } } } +func TestVariousBitmap(t *testing.T) { + b := NewBitmap(1, 2, 3) + + c, e := b.Add(8) + if e != nil { + t.Fatal("add:", e) + } + if c == false { + t.Fatal("add: should have changed") + } + c, _ = b.Add(8) + if c == true { + + t.Fatal("add: should not changed") + } + z, _ := b.AddN(9) + if z != 1 { + t.Fatal("add: should changed 1") + } + z, _ = b.RemoveN(9) + if z != 1 { + t.Fatal("add: should changed 1") + } + if b.Contains(100) { + t.Fatal("should not contain 100") + } + if !b.Any() { + t.Fatal("should have bits ") + } + if b.Size() == 0 { + t.Fatal("should have storage") + } + if b.Count() == 0 { + t.Fatal("should have bits") + } + if b.Max() == 0 { + t.Fatal("should max >0") + } + if m, e := b.Min(); !(m == 1 && e) { + t.Fatal("min should be 1 and containers exist") + } + b = nil + x := b.Clone() + if x != nil { + + t.Fatal("nil clone should be nil") + } + n := b.Freeze() + if n != nil { + + t.Fatal("nil freeze should be nil") + } + r, _ := b.AddN() + if r != 0 { + + t.Fatal("nil AddN should be 0") + } +} From a084ad0753a8cdc1095d8d56f9e2ec4c7dc06d0d Mon Sep 17 00:00:00 2001 From: Samir Patel <48686912+54mir@users.noreply.github.com> Date: Mon, 1 Nov 2021 10:44:01 -0500 Subject: [PATCH 55/76] Up goverversion to 1.16.9 --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index cda5430cc..c159da7db 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -4,7 +4,7 @@ include: - template: Security/Dependency-Scanning.gitlab-ci.yml variables: - GOVERSION: "1.16.3" + GOVERSION: "1.16.9" stages: - test From 9f67866e95cb8892250cc2e86b72bb5b5a410f73 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 1 Nov 2021 11:21:57 -0500 Subject: [PATCH 56/76] include subpackages in coverage --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 40c077fcc..7f51ef456 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -64,7 +64,7 @@ run go tests: extends: .go-cache script: - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverprofile=coverage.out + - go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out ./... artifacts: paths: - coverage.out From 2dab5ac8b28f9eb59438cc66ae9e8a8b5e60cff2 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 1 Nov 2021 11:59:22 -0500 Subject: [PATCH 57/76] coverage fun --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 7f51ef456..269e47a19 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -64,7 +64,7 @@ run go tests: extends: .go-cache script: - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out ./... + - go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out artifacts: paths: - coverage.out From b9b9b0b9cf3ce7b2f569a8e1e75748546f17c120 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 1 Nov 2021 12:17:17 -0500 Subject: [PATCH 58/76] coverage fun part2 --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index d01a2465e..329329a04 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -78,7 +78,7 @@ run go tests: extends: .go-cache script: - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out + - go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out ./... artifacts: paths: - coverage.out From 745c34bbfdb934a1e7449221ed47b237bca4e151 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 1 Nov 2021 12:27:44 -0500 Subject: [PATCH 59/76] remove syntload --- synthload/synthload_test.go | 361 ------------------------------------ synthload/testindex.tar.gz | Bin 234061 -> 0 bytes 2 files changed, 361 deletions(-) delete mode 100644 synthload/synthload_test.go delete mode 100644 synthload/testindex.tar.gz diff --git a/synthload/synthload_test.go b/synthload/synthload_test.go deleted file mode 100644 index f816fd2ff..000000000 --- a/synthload/synthload_test.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2017 Pilosa Corp. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package synthload - -/* // still work in progress, comment out for now, since we - // are getting a hang on go1.13 in CI -import ( - "archive/tar" - "bytes" - "compress/gzip" - "context" - "encoding/json" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/molecula/featurebase/v2" - "github.com/molecula/featurebase/v2/server" - "github.com/molecula/featurebase/v2/test" -) - -func Test_SynthLoad_ImportSchema(t *testing.T) { - c := test.MustRunCluster(t, 1, - []server.CommandOption{ - server.OptCommandServerOptions( - pilosa.OptServerNodeID("node0"), - )}, - ) - defer c.Close() - - m0 := c.GetNode(0) - - tarball := "testindex.tar.gz" - - // get the holder.Path to write to - h := m0.API.Holder() - target := h.Path() - PanicOn(h.Close()) - - PanicOn(unpackTarball(tarball, target)) - - // reopen - PanicOn(h.Open()) - - qs := strings.Split(pql, "\n\n") - //vv("qs = '%#v'", qs) - for _, q := range qs { - req := &pilosa.QueryRequest{ - // Index to execute query against. - Index: "testindex", - - // The query string to parse and execute. - Query: q, - - // // The shards to include in the query execution. - // // If empty, all shards are included. - // Shards []uint64 - - // // Return column attributes, if true. - // ColumnAttrs bool - - // // Do not return row attributes, if true. - // ExcludeRowAttrs bool - - // // Do not return columns, if true. - // ExcludeColumns bool - - // // If true, indicates that query is part of a larger distributed query. - // // If false, this request is on the originating node. - // Remote bool - - // // Should we profile this query? - // Profile bool - - // // Additional data associated with the query, in cases where there's - // // row-style inputs for precomputed values. - // EmbeddedData []*Row - } - - qr, err := m0.API.Query(context.Background(), req) - PanicOn(err) - vv("qr = '%#v'", qr) - } -} - -var _ = applySchema - -func applySchema(m0 *test.Command, schemaStr string) { - // don't need schema now that we import the tarball, it has it all. - schema := &pilosa.Schema{} - err := json.NewDecoder(bytes.NewBufferString(schemaStr)).Decode(schema) - PanicOn(err) - - ctx := context.Background() - remote := false - err = m0.API.ApplySchema(ctx, schema, remote) - PanicOn(err) -} - -func unpackTarball(tarball, target string) error { - - vv("target = '%v'", target) - fd, err := os.Open(tarball) - PanicOn(err) - defer fd.Close() - gz, err := gzip.NewReader(fd) - PanicOn(err) - defer gz.Close() - - tarReader := tar.NewReader(gz) - for { - header, err := tarReader.Next() - if err == io.EOF { - break - } else if err != nil { - panic(err) - } - - path := filepath.Join(target, header.Name) - info := header.FileInfo() - if info.IsDir() { - if err = os.MkdirAll(path, info.Mode()); err != nil { - PanicOn(err) - } - continue - } - - file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode()) - PanicOn(err) - - _, err = io.Copy(file, tarReader) - PanicOn(err) - file.Close() - } - return nil -} - -// mkdir o; bangbang schemator -o o -var pql = ` -Count(All()) - -Rows(field='bools',limit=10) -Count(Row(bools='bool')) -Count(Not(Row(bools='bool'))) -Count(Intersect(Row(bools='bool'))) -Count(Difference(Union(Row(bools='bool')),Intersect(Row(bools='bool')))) -GroupBy(Rows(field='bools'),limit=10) - -Rows(field='bools-exists',limit=10) -Count(Row(bools-exists='bool')) -Count(Not(Row(bools-exists='bool'))) -Count(Intersect(Row(bools-exists='bool'))) -Count(Difference(Union(Row(bools-exists='bool')),Intersect(Row(bools-exists='bool')))) -GroupBy(Rows(field='bools-exists'),limit=10) - -GroupBy(Rows(field='int'),limit=10) -Min(field='int') -Max(field='int') -Sum(field='int') - -Rows(field='mutex',limit=10) -Count(Row(mutex='12')) -Count(Not(Row(mutex='12'))) -Count(Row(mutex='13')) -Count(Not(Row(mutex='13'))) -Count(Row(mutex='0')) -Count(Not(Row(mutex='0'))) -Count(Row(mutex='5')) -Count(Not(Row(mutex='5'))) -Count(Row(mutex='2')) -Count(Not(Row(mutex='2'))) -Count(Row(mutex='14')) -Count(Not(Row(mutex='14'))) -Count(Row(mutex='3')) -Count(Not(Row(mutex='3'))) -Count(Row(mutex='18')) -Count(Not(Row(mutex='18'))) -Count(Row(mutex='7')) -Count(Not(Row(mutex='7'))) -Count(Row(mutex='19')) -Count(Not(Row(mutex='19'))) -Count(Intersect(Row(mutex='12'),Row(mutex='13'),Row(mutex='0'),Row(mutex='5'),Row(mutex='2'),Row(mutex='14'),Row(mutex='3'),Row(mutex='18'),Row(mutex='7'),Row(mutex='19'))) -Count(Difference(Union(Row(mutex='12'),Row(mutex='13'),Row(mutex='0'),Row(mutex='5'),Row(mutex='2'),Row(mutex='14'),Row(mutex='3'),Row(mutex='18'),Row(mutex='7'),Row(mutex='19')),Intersect(Row(mutex='12'),Row(mutex='13'),Row(mutex='0'),Row(mutex='5'),Row(mutex='2'),Row(mutex='14'),Row(mutex='3'),Row(mutex='18'),Row(mutex='7'),Row(mutex='19')))) -GroupBy(Rows(field='mutex'),limit=10) - -Rows(field='set',limit=10) -Count(Row(set='v621')) -Count(Not(Row(set='v621'))) -Count(Row(set='v997')) -Count(Not(Row(set='v997'))) -Count(Row(set='v772')) -Count(Not(Row(set='v772'))) -Count(Row(set='v340')) -Count(Not(Row(set='v340'))) -Count(Row(set='v766')) -Count(Not(Row(set='v766'))) -Count(Row(set='v416')) -Count(Not(Row(set='v416'))) -Count(Row(set='v481')) -Count(Not(Row(set='v481'))) -Count(Row(set='v581')) -Count(Not(Row(set='v581'))) -Count(Row(set='v591')) -Count(Not(Row(set='v591'))) -Count(Row(set='v675')) -Count(Not(Row(set='v675'))) -Count(Intersect(Row(set='v621'),Row(set='v997'),Row(set='v772'),Row(set='v340'),Row(set='v766'),Row(set='v416'),Row(set='v481'),Row(set='v581'),Row(set='v591'),Row(set='v675'))) -Count(Difference(Union(Row(set='v621'),Row(set='v997'),Row(set='v772'),Row(set='v340'),Row(set='v766'),Row(set='v416'),Row(set='v481'),Row(set='v581'),Row(set='v591'),Row(set='v675')),Intersect(Row(set='v621'),Row(set='v997'),Row(set='v772'),Row(set='v340'),Row(set='v766'),Row(set='v416'),Row(set='v481'),Row(set='v581'),Row(set='v591'),Row(set='v675')))) -GroupBy(Rows(field='set'),limit=10) - -Rows(field='string',limit=10) -Count(Row(string='KPFGWOYUGTCF')) -Count(Not(Row(string='KPFGWOYUGTCF'))) -Count(Row(string='VNSHWDTNELDA')) -Count(Not(Row(string='VNSHWDTNELDA'))) -Count(Row(string='LTDOGKKFGZPW')) -Count(Not(Row(string='LTDOGKKFGZPW'))) -Count(Row(string='EETNIIDCDZHB')) -Count(Not(Row(string='EETNIIDCDZHB'))) -Count(Row(string='ATWOMTRASGHP')) -Count(Not(Row(string='ATWOMTRASGHP'))) -Count(Row(string='KOJUBJQVMXZL')) -Count(Not(Row(string='KOJUBJQVMXZL'))) -Count(Row(string='NOXDEYPTGZBH')) -Count(Not(Row(string='NOXDEYPTGZBH'))) -Count(Row(string='DLSRPRADBPKX')) -Count(Not(Row(string='DLSRPRADBPKX'))) -Count(Row(string='WJUEQABAEEUX')) -Count(Not(Row(string='WJUEQABAEEUX'))) -Count(Row(string='PFRGBFDLHHPK')) -Count(Not(Row(string='PFRGBFDLHHPK'))) -Count(Intersect(Row(string='KPFGWOYUGTCF'),Row(string='VNSHWDTNELDA'),Row(string='LTDOGKKFGZPW'),Row(string='EETNIIDCDZHB'),Row(string='ATWOMTRASGHP'),Row(string='KOJUBJQVMXZL'),Row(string='NOXDEYPTGZBH'),Row(string='DLSRPRADBPKX'),Row(string='WJUEQABAEEUX'),Row(string='PFRGBFDLHHPK'))) -Count(Difference(Union(Row(string='KPFGWOYUGTCF'),Row(string='VNSHWDTNELDA'),Row(string='LTDOGKKFGZPW'),Row(string='EETNIIDCDZHB'),Row(string='ATWOMTRASGHP'),Row(string='KOJUBJQVMXZL'),Row(string='NOXDEYPTGZBH'),Row(string='DLSRPRADBPKX'),Row(string='WJUEQABAEEUX'),Row(string='PFRGBFDLHHPK')),Intersect(Row(string='KPFGWOYUGTCF'),Row(string='VNSHWDTNELDA'),Row(string='LTDOGKKFGZPW'),Row(string='EETNIIDCDZHB'),Row(string='ATWOMTRASGHP'),Row(string='KOJUBJQVMXZL'),Row(string='NOXDEYPTGZBH'),Row(string='DLSRPRADBPKX'),Row(string='WJUEQABAEEUX'),Row(string='PFRGBFDLHHPK')))) -GroupBy(Rows(field='string'),limit=10) - -GroupBy(Rows(field='time'),limit=10) -` - -// datagen --source=kitchensink --pilosa.index=testindex --start-from=1 --end-at=1000 --pilosa.batch-size=10000 --pilosa.hosts localhost:10101 -// curl localhost:10101/schema - -var _ = schemaString -var schemaString = ` -{ - "indexes": [ - { - "name": "testindex", - "createdAt": 1599601704641744600, - "options": { - "keys": false, - "trackExistence": true - }, - "fields": [ - { - "name": "bools", - "createdAt": 1599601704647053000, - "options": { - "type": "set", - "cacheType": "ranked", - "cacheSize": 50000, - "keys": true - } - }, - { - "name": "bools-exists", - "createdAt": 1599601704643225900, - "options": { - "type": "set", - "cacheType": "ranked", - "cacheSize": 50000, - "keys": true - } - }, - { - "name": "decimal", - "createdAt": 1599601704642064400, - "options": { - "type": "decimal", - "base": 0, - "scale": 2, - "bitDepth": 63, - "min": -92233720368547760, - "max": 92233720368547760, - "keys": false - } - }, - { - "name": "int", - "createdAt": 1599601704645510100, - "options": { - "type": "int", - "base": 0, - "bitDepth": 63, - "min": -9223372036854776000, - "max": 9223372036854776000, - "keys": false, - "foreignIndex": "" - } - }, - { - "name": "mutex", - "createdAt": 1599601704647187200, - "options": { - "type": "mutex", - "cacheType": "ranked", - "cacheSize": 500, - "keys": true - } - }, - { - "name": "set", - "createdAt": 1599601704643664400, - "options": { - "type": "set", - "cacheType": "lru", - "cacheSize": 1, - "keys": true - } - }, - { - "name": "string", - "createdAt": 1599601704644342000, - "options": { - "type": "set", - "cacheType": "ranked", - "cacheSize": 50000, - "keys": true - } - }, - { - "name": "time", - "createdAt": 1599601704643013400, - "options": { - "type": "time", - "timeQuantum": "YMD", - "keys": true, - "noStandardView": false - } - } - ], - "shardWidth": 1048576 - } - ] -} -` - -*/ diff --git a/synthload/testindex.tar.gz b/synthload/testindex.tar.gz deleted file mode 100644 index 15cdfe2b62f0237bd589572a065f04a2eed4eeb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234061 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%0D?`XTKX)2&oqi!H!LL6cyeq<_iyI?S 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 TzT`ilFYV6%t8l?w*@FKDgpuw+ From ff6a85b72845b2fc57d3eb2169dac9efa4f98018 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 1 Nov 2021 12:33:59 -0500 Subject: [PATCH 60/76] cvrpkg try again --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 329329a04..1d055642c 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -78,7 +78,7 @@ run go tests: extends: .go-cache script: - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out ./... + - go test -covermode=atomic -coverpkg=.../molecula/featurebase/v2/... -coverprofile=coverage.out artifacts: paths: - coverage.out From 2f92fd2a9a0ecb64ee5aaae38a3c3d463f58d357 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 1 Nov 2021 12:49:27 -0500 Subject: [PATCH 61/76] all the things --- .gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 1d055642c..f95be5f49 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -78,7 +78,7 @@ run go tests: extends: .go-cache script: - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverpkg=.../molecula/featurebase/v2/... -coverprofile=coverage.out + - go test -covermode=atomic -coverpkg=all -coverprofile=coverage.out artifacts: paths: - coverage.out From 35d35d9d4de97fa36fc36308e5fd863fb41885a2 Mon Sep 17 00:00:00 2001 From: reesporte Date: Mon, 1 Nov 2021 14:33:57 -0500 Subject: [PATCH 62/76] get rid of coverage html since we dont use it --- .gitlab/.gitlab-ci.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index f95be5f49..8b249480a 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -93,18 +93,6 @@ run go tests with output: paths: - test-report.out -generate coverage html: - stage: test - image: golang:1.16.9 - script: - - echo "Generating coverage HTML.." - - go tool cover -html coverage.out -o coverage.html - artifacts: - paths: - - coverage.html - needs: - - job: run go tests - upload to sonarcloud: stage: test image: sonarsource/sonar-scanner-cli:4.6 From 25d9895a217105bab095fee1efe1e7a8a69c74d1 Mon Sep 17 00:00:00 2001 From: reesporte Date: Mon, 1 Nov 2021 16:49:27 -0500 Subject: [PATCH 63/76] actually get coverage on every single package and subpackage (slow) --- .gitlab/.gitlab-ci.yml | 2 +- cover-everything.sh | 8 ++++++++ go.mod | 3 +-- go.sum | 9 +++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100755 cover-everything.sh diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 8b249480a..528ffb6e9 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -78,7 +78,7 @@ run go tests: extends: .go-cache script: - echo "Running featurebase unit tests..." - - go test -covermode=atomic -coverpkg=all -coverprofile=coverage.out + - ./cover-everything.sh artifacts: paths: - coverage.out diff --git a/cover-everything.sh b/cover-everything.sh new file mode 100755 index 000000000..f34640cfb --- /dev/null +++ b/cover-everything.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# actually get test coverage for every single package and subpackage +# very slow but oh well what are you gonna do, not test things? +echo "mode: atomic" > coverage.out +for pkg in $(go list all | grep featurebase); do + go test -coverprofile=pkgcoverage.out -covermode=atomic $pkg; + tail -n +2 pkgcoverage.out >> coverage.out; done diff --git a/go.mod b/go.mod index 25e8925e5..284a5bfd7 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,8 @@ require ( go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b golang.org/x/exp v0.0.0-20201008143054-e3b2a7f2fdc7 golang.org/x/mod v0.4.2 - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/text v0.3.5 // indirect + golang.org/x/tools v0.1.7 // indirect google.golang.org/grpc v1.28.0 gopkg.in/yaml.v2 v2.3.0 // indirect modernc.org/mathutil v1.0.0 diff --git a/go.sum b/go.sum index 7f90a4524..7b6cbdc7e 100644 --- a/go.sum +++ b/go.sum @@ -339,6 +339,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.1.1 h1:Nbsts7DdKThRHHd+YNlqiGlRqGEF2bE2eXN+xQ1hsEs= @@ -412,6 +413,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -447,6 +450,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201014080544-cc95f250f6bc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -456,6 +461,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= @@ -484,6 +491,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 5d7a39950047d34745c868572f18dc2837544997 Mon Sep 17 00:00:00 2001 From: reesporte Date: Mon, 1 Nov 2021 19:24:18 -0500 Subject: [PATCH 64/76] go mod tidy! --- go.mod | 2 +- go.sum | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 284a5bfd7..e3c2e395e 100644 --- a/go.mod +++ b/go.mod @@ -49,8 +49,8 @@ require ( go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b golang.org/x/exp v0.0.0-20201008143054-e3b2a7f2fdc7 golang.org/x/mod v0.4.2 + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/tools v0.1.7 // indirect google.golang.org/grpc v1.28.0 gopkg.in/yaml.v2 v2.3.0 // indirect modernc.org/mathutil v1.0.0 diff --git a/go.sum b/go.sum index 7b6cbdc7e..38f2e4578 100644 --- a/go.sum +++ b/go.sum @@ -339,7 +339,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.1.1 h1:Nbsts7DdKThRHHd+YNlqiGlRqGEF2bE2eXN+xQ1hsEs= @@ -411,8 +410,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -449,9 +446,7 @@ golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201014080544-cc95f250f6bc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -459,8 +454,6 @@ 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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -491,8 +484,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From f8d4d83e1a40ce9ccb3bbf0d1abc80c49fa42d56 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 2 Nov 2021 10:54:56 -0500 Subject: [PATCH 65/76] check op log --- roaring/roaring_internal_test.go | 45 +++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index e8899a339..a5ff6f5f5 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -34,6 +34,29 @@ func (iv Interval16) String() string { return fmt.Sprintf("[%d, %d]", iv.Start, iv.Last) } +func GetRoaringIter(bitsToSet ...uint64) RoaringIterator { + + b := NewBitmap() + changed := b.DirectAddN(bitsToSet...) + n := len(bitsToSet) + if changed != n { + e := fmt.Sprintf("changed=%v but bitsToSet len = %v", changed, n) + panic(e) + } + buf := bytes.NewBuffer(make([]byte, 0, 100000)) + _, er := b.WriteTo(buf) + if er != nil { + if er != nil { + panic(er) + } + } + itr, err := NewRoaringIterator(buf.Bytes()) + if err != nil { + panic(err) + } + return itr +} + func TestRunAppendInterval(t *testing.T) { a := NewContainerRun(nil) tests := []struct { @@ -4737,8 +4760,28 @@ func TestIntersectionCallback(t *testing.T) { } } } +func TestImportBitmap(t *testing.T) { + b := NewBitmap() + i := GetRoaringIter(1, 3, 5) + + changed, _, err := b.ImportRoaringRawIterator(i, false, true, 16) + if err != nil { + t.Fatal("no error should happen changed") + } + if changed != 3 { + t.Fatal("Should have changed") + } + i = GetRoaringIter(1, 3, 5) + changed, _, err = b.ImportRoaringRawIterator(i, true, true, 16) + if err != nil { + t.Fatal("no error should happen changed") + } + if changed != 3 { + t.Fatalf("Should have changed %v", changed) + } +} func TestVariousBitmap(t *testing.T) { - b := NewBitmap(1, 2, 3) + b := NewBitmap(3) c, e := b.Add(8) if e != nil { From a79bb893af3f8a795be9f7af356a814197916b7f Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 2 Nov 2021 12:14:57 -0500 Subject: [PATCH 66/76] test ToRows --- executor_test.go | 37 ++++++++++++++++++++++++++++++++ roaring/roaring_internal_test.go | 4 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/executor_test.go b/executor_test.go index ba357d724..c42d7eb14 100644 --- a/executor_test.go +++ b/executor_test.go @@ -8184,3 +8184,40 @@ func TestExternalLookup(t *testing.T) { } }) } +func TestToRows(t *testing.T) { + ids := &pilosa.RowIdentifiers{ + Rows: []uint64{1, 2, 3}, + } + c := ids.Clone() + if c == nil { + t.Fatal("Shouldn't be nil ") + } + e := ids.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + + keys := &pilosa.RowIdentifiers{ + Keys: []string{"a", "b"}, + } + c = keys.Clone() + if c == nil { + t.Fatal("Shouldn't be nil ") + } + e = keys.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + v := &pilosa.ValCount{ + TimestampVal: time.Now(), + Count: 1, + } + x := v.Clone() + if x == nil { + t.Fatal("Shouldn't be nil ") + } +} diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index a5ff6f5f5..da65e2306 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -4818,8 +4818,8 @@ func TestVariousBitmap(t *testing.T) { if b.Max() == 0 { t.Fatal("should max >0") } - if m, e := b.Min(); !(m == 1 && e) { - t.Fatal("min should be 1 and containers exist") + if m, e := b.Min(); !(m == 3 && e) { + t.Fatal("min should be 3 and containers exist", m, e) } b = nil x := b.Clone() From ad2f5c5ed4cc005c7fb2f31f2ef46d29506f96b7 Mon Sep 17 00:00:00 2001 From: reesporte Date: Tue, 2 Nov 2021 16:03:43 -0500 Subject: [PATCH 67/76] limit the number of results that we keep also tells the user how many results there were, but they only get to see 1000 of them --- lattice/src/App/Query/QueryContainer.tsx | 85 +++++++------ .../App/Query/QueryResults/QueryResults.tsx | 68 ++++++---- .../GroupByChart/GroupByChart.tsx | 73 +++++------ .../QueryBuilder/QueryBuilderContainer.tsx | 116 +++++++++--------- 4 files changed, 189 insertions(+), 153 deletions(-) diff --git a/lattice/src/App/Query/QueryContainer.tsx b/lattice/src/App/Query/QueryContainer.tsx index 7c102509c..4623cfc3c 100644 --- a/lattice/src/App/Query/QueryContainer.tsx +++ b/lattice/src/App/Query/QueryContainer.tsx @@ -1,37 +1,42 @@ -import React, { FC, useState } from 'react'; -import moment, { Moment } from 'moment'; -import uniqBy from 'lodash/uniqBy'; -import { Query } from './Query'; -import { pilosa } from 'services/eventServices'; -import { useEffectOnce } from 'react-use'; -import { grpc } from '@improbable-eng/grpc-web'; -import { queryPQL, querySQL } from 'services/grpcServices'; -import { ColumnInfo, ColumnResponse, RowResponse } from 'proto/pilosa_pb'; +import React, { FC, useState } from "react"; +import moment, { Moment } from "moment"; +import uniqBy from "lodash/uniqBy"; +import { Query } from "./Query"; +import { pilosa } from "services/eventServices"; +import { useEffectOnce } from "react-use"; +import { grpc } from "@improbable-eng/grpc-web"; +import { queryPQL, querySQL } from "services/grpcServices"; +import { ColumnInfo, ColumnResponse, RowResponse } from "proto/pilosa_pb"; export type ResultType = { query: string; operation: string; - type: 'PQL' | 'SQL'; + type: "PQL" | "SQL"; headers: ColumnInfo.AsObject[]; rows: ColumnResponse.AsObject[][]; duration?: number; roundtrip: number; index?: string; error: string; + totalMessageCount: number; }; let streamingResults: ResultType = { - query: '', - operation: '', - type: 'SQL', + query: "", + operation: "", + type: "SQL", headers: [], rows: [], roundtrip: 0, - error: '' + error: "", + totalMessageCount: 0, }; export const QueryContainer: FC<{}> = () => { let startTime: Moment; + + const MAX_MESSAGES = 1000; // same limit as in query builder + const [indexes, setIndexes] = useState(); const [results, setResults] = useState([]); const [errorResult, setErrorResult] = useState(); @@ -44,12 +49,15 @@ export const QueryContainer: FC<{}> = () => { }); const handleQueryMessages = (message: RowResponse) => { - const response = message.toObject(); - if (response.headersList.length > 0) { - streamingResults.headers = response.headersList; - streamingResults.duration = response.duration; + if (streamingResults.totalMessageCount < MAX_MESSAGES) { + const response = message.toObject(); + if (response.headersList.length > 0) { + streamingResults.headers = response.headersList; + streamingResults.duration = response.duration; + } + streamingResults.rows.push(response.columnsList); } - streamingResults.rows.push(response.columnsList); + streamingResults.totalMessageCount += 1; }; const handleQueryEnd = (status: grpc.Code, statusMessage: string) => { @@ -58,71 +66,72 @@ export const QueryContainer: FC<{}> = () => { setErrorResult(streamingResults); } else { let recentQueries = JSON.parse( - localStorage.getItem('recent-queries') || '[]' + localStorage.getItem("recent-queries") || "[]" ); - const lastQuery = localStorage.getItem('last-query'); + const lastQuery = localStorage.getItem("last-query"); recentQueries.unshift(lastQuery); recentQueries = uniqBy(recentQueries); if (recentQueries.length > 10) { localStorage.setItem( - 'recent-queries', + "recent-queries", JSON.stringify(recentQueries.slice(0, 9)) ); } else { - localStorage.setItem('recent-queries', JSON.stringify(recentQueries)); + localStorage.setItem("recent-queries", JSON.stringify(recentQueries)); } streamingResults.roundtrip = moment .duration(moment().diff(startTime)) - .as('milliseconds'); + .as("milliseconds"); setErrorResult(undefined); setResults([streamingResults, ...results]); } setLoading(false); }; - const onQuery = (query: string, type: 'PQL' | 'SQL', index?: string) => { + const onQuery = (query: string, type: "PQL" | "SQL", index?: string) => { streamingResults = { query, - operation: '', + operation: "", type, headers: [], rows: [], index, roundtrip: 0, - error: '' + error: "", + totalMessageCount: 0, }; startTime = moment(); if (query) { setLoading(true); localStorage.setItem( - 'last-query', - type === 'PQL' ? `[${index}]${query}` : query + "last-query", + type === "PQL" ? `[${index}]${query}` : query ); - if (type === 'PQL') { + if (type === "PQL") { if (index) { queryPQL(index, query, handleQueryMessages, handleQueryEnd); } else { - streamingResults.error = 'missing index'; + streamingResults.error = "missing index"; setErrorResult(streamingResults); setLoading(false); } } else { - let queryArr = query.split(' '); + let queryArr = query.split(" "); queryArr.forEach((word, idx) => { - if (word.includes('-')) { - let wordArr = word.split('.'); + if (word.includes("-")) { + let wordArr = word.split("."); wordArr.forEach((section, idx) => { - if(section.includes('-') && !word.includes('`')) { + if (section.includes("-") && !word.includes("`")) { wordArr[idx] = `\`${wordArr[idx]}\``; } - }) - queryArr[idx] = wordArr.join('.'); + }); + queryArr[idx] = wordArr.join("."); } }); - querySQL(queryArr.join(' '), handleQueryMessages, handleQueryEnd); + querySQL(queryArr.join(" "), handleQueryMessages, handleQueryEnd); } } }; diff --git a/lattice/src/App/Query/QueryResults/QueryResults.tsx b/lattice/src/App/Query/QueryResults/QueryResults.tsx index abab622e0..e940a7ab6 100644 --- a/lattice/src/App/Query/QueryResults/QueryResults.tsx +++ b/lattice/src/App/Query/QueryResults/QueryResults.tsx @@ -1,15 +1,15 @@ -import React, { FC, Fragment, useState } from 'react'; -import CloseIcon from '@material-ui/icons/Close'; -import copy from 'copy-to-clipboard'; -import FileCopySharpIcon from '@material-ui/icons/FileCopySharp'; -import IconButton from '@material-ui/core/IconButton'; -import Tooltip from '@material-ui/core/Tooltip'; -import Typography from '@material-ui/core/Typography'; -import { DataTable } from 'shared/DataTable'; -import { formatDuration } from 'shared/utils/formatDuration'; -import { GroupByChart } from 'App/QueryBuilder/GroupByChart'; -import { ResultType } from '../QueryContainer'; -import css from './QueryResults.module.scss'; +import React, { FC, Fragment, useState } from "react"; +import CloseIcon from "@material-ui/icons/Close"; +import copy from "copy-to-clipboard"; +import FileCopySharpIcon from "@material-ui/icons/FileCopySharp"; +import IconButton from "@material-ui/core/IconButton"; +import Tooltip from "@material-ui/core/Tooltip"; +import Typography from "@material-ui/core/Typography"; +import { DataTable } from "shared/DataTable"; +import { formatDuration } from "shared/utils/formatDuration"; +import { GroupByChart } from "App/QueryBuilder/GroupByChart"; +import { ResultType } from "../QueryContainer"; +import css from "./QueryResults.module.scss"; type QueryResultsProps = { collapsibleQuery?: boolean; @@ -20,12 +20,12 @@ type QueryResultsProps = { export const QueryResults: FC = ({ collapsibleQuery = true, results, - onRemoveResult + onRemoveResult, }) => { const [showQuery, setShowQuery] = useState(false); - const [copyTooltip, setCopyTooltip] = useState('Copy Query'); + const [copyTooltip, setCopyTooltip] = useState("Copy Query"); const queryString = - results.type === 'PQL' + results.type === "PQL" ? `[${results.index}]${results.query}` : results.query; @@ -34,10 +34,10 @@ export const QueryResults: FC = ({ let rowData = {}; row.forEach((col, colIdx) => { const header = headers[colIdx]; - if (header.datatype.includes('[]')) { + if (header.datatype.includes("[]")) { const dataTypeVal = `${header.datatype.slice(2)}arrayval`; - rowData[header.name] = col[dataTypeVal].valsList.join(', '); - } else if (header.datatype === 'decimal') { + rowData[header.name] = col[dataTypeVal].valsList.join(", "); + } else if (header.datatype === "decimal") { const decimalVal = col[`${header.datatype}val`]; if (decimalVal) { const { value, scale } = decimalVal; @@ -55,9 +55,9 @@ export const QueryResults: FC = ({ const onCopyQuery = () => { copy(queryString); - setCopyTooltip('Copied!'); + setCopyTooltip("Copied!"); setTimeout(() => { - setCopyTooltip('Copy Query'); + setCopyTooltip("Copy Query"); }, 1500); }; @@ -75,7 +75,7 @@ export const QueryResults: FC = ({ onClick={() => setShowQuery(!showQuery)} className={css.link} > - {showQuery ? 'Hide' : 'Show'} Query + {showQuery ? "Hide" : "Show"} Query )} @@ -136,10 +136,32 @@ export const QueryResults: FC = ({ {!collapsibleQuery || showQuery ? ( - {queryString} +
+ {queryString} + + {results.totalMessageCount > 1000 ? ( + + + NOTE + + + ) : null} +
) : null} - {results.operation === 'GroupBy' && results.rows.length <= 50 ? ( + {results.operation === "GroupBy" && results.rows.length <= 50 ? ( ) : ( diff --git a/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx b/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx index fb323264e..90973a231 100644 --- a/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx +++ b/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx @@ -1,9 +1,9 @@ -import React, { FC } from 'react'; -import groupBy from 'lodash/groupBy'; -import { ResponsiveBar } from '@nivo/bar'; -import { ResultType } from '../../Query'; -import { schemeTableau10 } from 'd3-scale-chromatic'; -import { useTheme } from '@material-ui/core/styles'; +import React, { FC } from "react"; +import groupBy from "lodash/groupBy"; +import { ResponsiveBar } from "@nivo/bar"; +import { ResultType } from "../../Query"; +import { schemeTableau10 } from "d3-scale-chromatic"; +import { useTheme } from "@material-ui/core/styles"; type GroupByChartType = { results: ResultType; @@ -11,7 +11,7 @@ type GroupByChartType = { export const GroupByChart: FC = ({ results }) => { const theme = useTheme(); - const isDark = theme.palette.type === 'dark'; + const isDark = theme.palette.type === "dark"; const { headers, rows } = results; let uniqueKeys: string[] = []; const grouped = groupBy(rows, (row) => row[0].stringval); @@ -23,7 +23,7 @@ export const GroupByChart: FC = ({ results }) => { groupData = { ...groupData, [headers[0].name]: row[0][`${headers[0].datatype}val`], - [secondaryValue]: row[2][`${headers[2].datatype}val`] + [secondaryValue]: row[2][`${headers[2].datatype}val`], }; if (!uniqueKeys.includes(secondaryValue.toString())) { @@ -33,7 +33,7 @@ export const GroupByChart: FC = ({ results }) => { groupData = { ...groupData, [headers[0].name]: row[0][`${headers[0].datatype}val`], - value: secondaryValue + value: secondaryValue, }; } }); @@ -42,31 +42,34 @@ export const GroupByChart: FC = ({ results }) => { }); return ( -
+
+ This query has {results.totalMessageCount} total results. Due to browser + memory limitations, you will only be able to see {results.rows.length}{" "} + results. 0 ? uniqueKeys : undefined} indexBy={headers[0].name} margin={{ top: 50, right: 130, bottom: 100, left: 60 }} padding={0.3} - valueScale={{ type: 'linear' }} - indexScale={{ type: 'band', round: true }} + valueScale={{ type: "linear" }} + indexScale={{ type: "band", round: true }} colors={schemeTableau10} enableLabel={false} theme={ isDark ? { - textColor: 'var(--text-secondary)', + textColor: "var(--text-secondary)", axis: { - domain: { line: { stroke: 'rgba(255, 255, 255, 0.1)' } } + domain: { line: { stroke: "rgba(255, 255, 255, 0.1)" } }, }, - grid: { line: { stroke: 'rgba(255, 255, 255, 0.1)' } }, - tooltip: { container: { background: '#1c2022' } } + grid: { line: { stroke: "rgba(255, 255, 255, 0.1)" } }, + tooltip: { container: { background: "#1c2022" } }, } : { axis: { - domain: { line: { stroke: '#dddddd' } } - } + domain: { line: { stroke: "#dddddd" } }, + }, } } groupMode="grouped" @@ -75,16 +78,16 @@ export const GroupByChart: FC = ({ results }) => { tickPadding: 5, tickRotation: -40, legend: headers[0].name, - legendPosition: 'middle', - legendOffset: 80 + legendPosition: "middle", + legendOffset: 80, }} axisLeft={{ tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: 'count', - legendPosition: 'middle', - legendOffset: -40 + legend: "count", + legendPosition: "middle", + legendOffset: -40, }} tooltip={({ id, value, color }) => ( @@ -93,31 +96,31 @@ export const GroupByChart: FC = ({ results }) => { )} labelSkipWidth={12} labelSkipHeight={12} - labelTextColor={{ from: 'color', modifiers: [['darker', 1.6]] }} + labelTextColor={{ from: "color", modifiers: [["darker", 1.6]] }} legends={[ { - dataFrom: 'keys', - anchor: 'bottom-right', - direction: 'column', + dataFrom: "keys", + anchor: "bottom-right", + direction: "column", justify: false, translateX: 120, translateY: 0, itemsSpacing: 2, itemWidth: 120, itemHeight: 20, - itemDirection: 'left-to-right', + itemDirection: "left-to-right", itemOpacity: 0.85, - symbolShape: 'circle', + symbolShape: "circle", symbolSize: 20, effects: [ { - on: 'hover', + on: "hover", style: { - itemOpacity: 1 - } - } - ] - } + itemOpacity: 1, + }, + }, + ], + }, ]} animate={true} motionStiffness={90} diff --git a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx index ef9f9062a..a526fd372 100644 --- a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx +++ b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx @@ -1,48 +1,49 @@ -import React, { Fragment, useState } from 'react'; -import Alert from '@material-ui/lab/Alert'; -import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos'; -import Button from '@material-ui/core/Button'; -import classNames from 'classnames'; -import CloseIcon from '@material-ui/icons/Close'; -import IconButton from '@material-ui/core/IconButton'; -import InfoIcon from '@material-ui/icons/Info'; -import moment, { Moment } from 'moment'; -import Paper from '@material-ui/core/Paper'; -import Split from 'react-split'; -import Snackbar from '@material-ui/core/Snackbar'; -import Tooltip from '@material-ui/core/Tooltip'; -import Typography from '@material-ui/core/Typography'; -import { Block } from 'shared/Block'; -import { formatDuration } from 'shared/utils/formatDuration'; -import { grpc } from '@improbable-eng/grpc-web'; -import { pilosa } from 'services/eventServices'; -import { QueryBuilder } from './QueryBuilder'; -import { queryPQL } from 'services/grpcServices'; -import { QueryResults } from 'App/Query/QueryResults'; -import { ResultType } from 'App/Query/QueryContainer'; -import { RowResponse } from 'proto/pilosa_pb'; -import { SavedQueries } from 'App/QueryBuilder/SavedQueries'; -import { useEffectOnce } from 'react-use'; -import css from './QueryBuilderContainer.module.scss'; +import React, { Fragment, useState } from "react"; +import Alert from "@material-ui/lab/Alert"; +import ArrowForwardIosIcon from "@material-ui/icons/ArrowForwardIos"; +import Button from "@material-ui/core/Button"; +import classNames from "classnames"; +import CloseIcon from "@material-ui/icons/Close"; +import IconButton from "@material-ui/core/IconButton"; +import InfoIcon from "@material-ui/icons/Info"; +import moment, { Moment } from "moment"; +import Paper from "@material-ui/core/Paper"; +import Split from "react-split"; +import Snackbar from "@material-ui/core/Snackbar"; +import Tooltip from "@material-ui/core/Tooltip"; +import Typography from "@material-ui/core/Typography"; +import { Block } from "shared/Block"; +import { formatDuration } from "shared/utils/formatDuration"; +import { grpc } from "@improbable-eng/grpc-web"; +import { pilosa } from "services/eventServices"; +import { QueryBuilder } from "./QueryBuilder"; +import { queryPQL } from "services/grpcServices"; +import { QueryResults } from "App/Query/QueryResults"; +import { ResultType } from "App/Query/QueryContainer"; +import { RowResponse } from "proto/pilosa_pb"; +import { SavedQueries } from "App/QueryBuilder/SavedQueries"; +import { useEffectOnce } from "react-use"; +import css from "./QueryBuilderContainer.module.scss"; let streamingResults: ResultType = { - query: '', - operation: '', - type: 'PQL', + query: "", + operation: "", + type: "PQL", headers: [], rows: [], roundtrip: 0, - error: '' + error: "", + totalMessageCount: 0, }; export const QueryBuilderContainer = () => { let startTime: Moment; let exportRows: any[] = []; const colSizes = JSON.parse( - localStorage.getItem('builderColSizes') || '[25, 75]' + localStorage.getItem("builderColSizes") || "[25, 75]" ); const [queriesList, setQueriesList] = useState( - JSON.parse(localStorage.getItem('saved-queries') || '[]') + JSON.parse(localStorage.getItem("saved-queries") || "[]") ); const [tables, setTables] = useState([]); @@ -51,7 +52,7 @@ export const QueryBuilderContainer = () => { const [fullCount, setFullCount] = useState(); const [recordsCount, setRecordsCount] = useState(); const [errorResult, setErrorResult] = useState(); - const [error, setError] = useState(''); + const [error, setError] = useState(""); const [loading, setLoading] = useState(false); const [savedQuery, setSavedQuery] = useState(-1); @@ -77,7 +78,7 @@ export const QueryBuilderContainer = () => { } else { streamingResults.roundtrip = moment .duration(moment().diff(startTime)) - .as('milliseconds'); + .as("milliseconds"); setErrorResult(undefined); setResults(streamingResults); } @@ -89,12 +90,12 @@ export const QueryBuilderContainer = () => { let rowStr: string[] = []; if (exportRows.length === 0) { const headers = response.headersList.map((header) => header.name); - exportRows.push(headers.join('\t')); + exportRows.push(headers.join("\t")); } response.headersList.forEach((header, idx) => rowStr.push(response.columnsList[idx][`${header.datatype}val`]) ); - exportRows.push(rowStr.join('\t')); + exportRows.push(rowStr.join("\t")); }; const handleExternalLookupEnd = ( @@ -104,12 +105,12 @@ export const QueryBuilderContainer = () => { if (status !== grpc.Code.OK) { setError(statusMessage); } else if (exportRows.length === 0) { - setError('No record attributes for current query.'); + setError("No record attributes for current query."); } else { const dateTime = moment().unix(); - const element = document.createElement('a'); - const file = new Blob([exportRows.join('\n')], { - type: 'text/plain;charset=utf-8' + const element = document.createElement("a"); + const file = new Blob([exportRows.join("\n")], { + type: "text/plain;charset=utf-8", }); element.href = URL.createObjectURL(file); element.download = `molecula-${results?.index}-${dateTime}.csv`; @@ -128,17 +129,18 @@ export const QueryBuilderContainer = () => { streamingResults = { query, operation, - type: 'PQL', + type: "PQL", headers: [], rows: [], index: table, roundtrip: 0, - error: '' + error: "", + totalMessageCount: 0, }; startTime = moment(); setLoading(true); - if (operation !== 'Count') { + if (operation !== "Count") { if (countQuery) { pilosa.post.query(table, countQuery).then((res) => { setFullCount(res.data.results[0]); @@ -164,13 +166,13 @@ export const QueryBuilderContainer = () => { const onRemoveQuery = (queryIdx: number) => { let queries = [...queriesList]; queries.splice(queryIdx, 1); - localStorage.setItem('saved-queries', JSON.stringify(queries)); + localStorage.setItem("saved-queries", JSON.stringify(queries)); setQueriesList(queries); }; const onSaveQuery = () => { const updatedList = JSON.parse( - localStorage.getItem('saved-queries') || '[]' + localStorage.getItem("saved-queries") || "[]" ); if (savedQuery < 0) { setSavedQuery(updatedList.length - 1); @@ -181,7 +183,7 @@ export const QueryBuilderContainer = () => { const onExportLogs = () => { if (results) { const columns = results.rows.map((row) => row[0].uint64val); - const table = results.index ? results.index : ''; + const table = results.index ? results.index : ""; onExternalLookup(table, columns); } }; @@ -200,17 +202,17 @@ export const QueryBuilderContainer = () => { cursor="col-resize" minSize={showBuilder ? 350 : 50} onDragEnd={(sizes) => - localStorage.setItem('builderColSizes', JSON.stringify(sizes)) + localStorage.setItem("builderColSizes", JSON.stringify(sizes)) } gutter={(_index, direction) => { - const gutter = document.createElement('div'); + const gutter = document.createElement("div"); gutter.className = `gutter gutter-${direction}`; - const dragbars = document.createElement('div'); - dragbars.className = 'dragBar'; + const dragbars = document.createElement("div"); + dragbars.className = "dragBar"; gutter.appendChild(dragbars); return gutter; }} - className={classNames(css.split, !showBuilder ? 'hide-gutter' : '')} + className={classNames(css.split, !showBuilder ? "hide-gutter" : "")} > {showBuilder ? (
@@ -239,9 +241,9 @@ export const QueryBuilderContainer = () => {
- Results{' '} + Results{" "} - {results?.query.includes('Extract(') ? ( + {results?.query.includes("Extract(") ? (
{
{results.duration ? (
- {recordsCount.toLocaleString()} records scanned in{' '} + {recordsCount.toLocaleString()} records scanned in{" "} {formatDuration(results.duration, true)}.
) : null}
- Showing{' '} - {fullCount > 1000 ? 'first 1,000 rows of' : 'all'}{' '} + Showing{" "} + {fullCount > 1000 ? "first 1,000 rows of" : "all"}{" "} {fullCount.toLocaleString()} results.
@@ -301,7 +303,7 @@ export const QueryBuilderContainer = () => {
- setError('')}> + setError("")}> {error} From e915d75df68ae0c9509e8844b4134937052742b1 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 3 Nov 2021 08:48:57 -0500 Subject: [PATCH 68/76] remove @ from yaml try to fix yml syntax same same same same2 same3 same4 same5 try with shell runner instead of dind remove lattice from dockerfile change path to bin runs after linux arm64 build change dockerfile path same same add dir better test coverage --- .gitlab/.gitlab-ci.yml | 18 ++++++++++ .gitlab/Dockerfile | 23 +++++++++++++ cluster_internal_test.go | 12 +++++++ executor_test.go | 73 ++++++++++++++++++++++++++++++++++++++++ rbf/cursor_test.go | 23 +++++++++++++ rbf/cursorx.go | 2 +- rbf/dot.go | 8 ++--- 7 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 .gitlab/Dockerfile diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index 528ffb6e9..3ebd5d990 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -10,6 +10,8 @@ include: - key: $CI_COMMIT_REF_SLUG paths: - .go/pkg/mod/ +variables: + GOVERSION: "1.16.9" stages: - lint @@ -156,3 +158,19 @@ build for darwin arm64: artifacts: paths: - featurebase_darwin_arm64 + +# Build a FB Docker image with CI/CD and push to the GitLab registry. +build container fb: + image: docker:stable + stage: build + needs: + - "build for linux amd64" + tags: + - shell + before_script: + - echo "${DOCKER_DEPLOY_TOKEN}" | docker login -u ${DOCKER_DEPLOY_USER} --password-stdin ${CI_REGISTRY} + script: + - tag=${CI_REGISTRY_IMAGE}/server:${CI_COMMIT_REF_SLUG} + - docker build --build-arg GO_VERSION=$GOVERSION -t $tag -f .gitlab/Dockerfile . + - docker push $tag + - echo Created docker featurebase image with tag "$tag" \ No newline at end of file diff --git a/.gitlab/Dockerfile b/.gitlab/Dockerfile new file mode 100644 index 000000000..b22392ce1 --- /dev/null +++ b/.gitlab/Dockerfile @@ -0,0 +1,23 @@ +FROM alpine:3.14.2 + +LABEL maintainer "dev@molecula.com" +LABEL org.opencontainers.image.authors="dev@molecula.com" + +WORKDIR /featurebase + +RUN apk add --no-cache curl jq + +COPY LICENSE . +COPY NOTICE . +COPY featurebase_linux_amd64 . + +RUN chmod ugo+x . +EXPOSE 10101 +VOLUME /data + +ENV PILOSA_DATA_DIR /data +ENV PILOSA_BIND 0.0.0.0:10101 +ENV PILOSA_BIND_GRPC 0.0.0.0:20101 + +ENTRYPOINT ["/featurebase"] +CMD ["server"] \ No newline at end of file diff --git a/cluster_internal_test.go b/cluster_internal_test.go index 3d204ae92..5092cba73 100644 --- a/cluster_internal_test.go +++ b/cluster_internal_test.go @@ -15,6 +15,7 @@ package pilosa import ( + "context" "fmt" "math/rand" "reflect" @@ -625,3 +626,14 @@ func TestAE(t *testing.T) { } }) } + +func TestTranslateIndexKey(t *testing.T) { + c := newCluster() + node0 := &topology.Node{ID: "node0"} + c.addNodeBasicSorted(node0) + c.holder = newHolderWithTempPath(t, "rbf") + _, e := c.translateIndexKey(context.Background(), "i", "a", false) + if e == nil { + t.Fatal("expecting error") + } +} diff --git a/executor_test.go b/executor_test.go index c42d7eb14..24395df8c 100644 --- a/executor_test.go +++ b/executor_test.go @@ -8220,4 +8220,77 @@ func TestToRows(t *testing.T) { if x == nil { t.Fatal("Shouldn't be nil ") } + e = v.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + v.DecimalVal = &pql.Decimal{Value: 1, Scale: 1} + e = v.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + v.DecimalVal = nil + v.FloatVal = 3.0 + e = v.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + + pfi := &pilosa.PairField{ + Pair: pilosa.Pair{ID: 1, Count: 1}, + Field: "f", + } + z := pfi.Clone() + if z.Pair.ID != pfi.Pair.ID { + t.Fatal("Should be equal ", z, pfi) + } + e = pfi.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + pfk := &pilosa.PairField{ + Pair: pilosa.Pair{Key: "a", Count: 1}, + Field: "f", + } + o := pfk.Clone() + if o.Pair.Key != pfk.Pair.Key { + t.Fatal("Should be equal ") + } + e = pfk.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + pfs := &pilosa.PairsField{ + Pairs: []pilosa.Pair{{ID: 1, Count: 1}}, + Field: "f", + } + f := pfs.Clone() + if f.Pairs[0].ID != pfs.Pairs[0].ID { + t.Fatal("Should be equal ") + } + e = pfs.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + + r4 := server.ResultUint64(1) + e = r4.ToRows(func(*proto.RowResponse) error { + return nil + }) + if e != nil { + t.Fatal("Shouldn't be err ", e) + } + } diff --git a/rbf/cursor_test.go b/rbf/cursor_test.go index 9ed2b2209..6694cd026 100644 --- a/rbf/cursor_test.go +++ b/rbf/cursor_test.go @@ -18,6 +18,7 @@ import ( "io" "math/bits" "math/rand" + "os" "reflect" "sort" "strings" @@ -847,6 +848,25 @@ func (e *EasyWalker) String() string { return e.path.String() } +func TestDumpDot(t *testing.T) { + db := MustOpenDB(t) + defer MustCloseDB(t, db) + tx := MustBegin(t, db, true) + defer tx.Rollback() + if err := tx.CreateBitmap("x"); err != nil { + t.Fatal(err) + } + c, err := tx.Cursor("x") + if err != nil { + t.Fatal(err) + } + + _, err = c.Add(1) + if err != nil { + t.Fatal(err) + } + rbf.Dumpdot(tx, 0, " ", os.Stdout) +} func TestCursor_UpdateBranchCells(t *testing.T) { db := MustOpenDB(t) defer MustCloseDB(t, db) @@ -964,6 +984,9 @@ func TestCursor_SplitBranchCells(t *testing.T) { t.Fatalf("Expecting RBLL (a branch split) got %v", after.String()) } + // + c, _ := tx.Cursor("x") //added just for dot code coverage + c.Dump("ignore for coverage") } diff --git a/rbf/cursorx.go b/rbf/cursorx.go index 9b2a61111..5fcd2ae7c 100644 --- a/rbf/cursorx.go +++ b/rbf/cursorx.go @@ -106,7 +106,7 @@ func (c *Cursor) Dump(name string) { fmt.Fprintf(bufStdout, "rankdir=\"LR\"\n") fmt.Fprintf(bufStdout, "node [shape=record height=.1]\n") - dumpdot(c.tx, 0, " ", bufStdout) + Dumpdot(c.tx, 0, " ", bufStdout) fmt.Fprintf(bufStdout, "\n}") bufStdout.Flush() } diff --git a/rbf/dot.go b/rbf/dot.go index fa815805e..69ef11961 100644 --- a/rbf/dot.go +++ b/rbf/dot.go @@ -60,8 +60,8 @@ func dotCell(b []byte, parent string, writer io.Writer) { } } -// dumpdot recursively writes the tree representation starting from a given page to STDERR. -func dumpdot(tx *Tx, pgno uint32, parent string, writer io.Writer) { +// Dumpdot recursively writes the tree representation starting from a given page to STDERR. +func Dumpdot(tx *Tx, pgno uint32, parent string, writer io.Writer) { page, _, err := tx.readPage(pgno) if err != nil { panic(err) @@ -79,7 +79,7 @@ func dumpdot(tx *Tx, pgno uint32, parent string, writer io.Writer) { root := fmt.Sprintf("root%d", record.Pgno) fmt.Fprintf(writer, "%s[label=\"ROOT(%d)| %s\"]\n%s->%s\n", root, record.Pgno, record.Name, rr, root) p := fmt.Sprintf("root%d", record.Pgno) - dumpdot(tx, record.Pgno, p, writer) + Dumpdot(tx, record.Pgno, p, writer) } } @@ -96,7 +96,7 @@ func dumpdot(tx *Tx, pgno uint32, parent string, writer io.Writer) { for i, n := 0, readCellN(page); i < n; i++ { cell := readBranchCell(page, i) if cell.Flags&uint32(ContainerTypeBitmap) == 0 { // leaf/branch child page - dumpdot(tx, cell.ChildPgno, p, writer) + Dumpdot(tx, cell.ChildPgno, p, writer) } else { b := fmt.Sprintf("bm%d", cell.ChildPgno) fmt.Fprintf(writer, "%s[label=\"BITMAP(%d) key=%d \"]\n %s -> %s\n", b, cell.ChildPgno, cell.LeftKey, p, b) From 7fa71d954832710edf5de570edd02e4abdcc08f3 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 3 Nov 2021 08:57:24 -0500 Subject: [PATCH 69/76] check selecthandler --- sql/handler_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 sql/handler_test.go diff --git a/sql/handler_test.go b/sql/handler_test.go new file mode 100644 index 000000000..a583f3ba3 --- /dev/null +++ b/sql/handler_test.go @@ -0,0 +1,28 @@ +package sql_test + +import ( + "context" + "testing" + + "github.com/molecula/featurebase/v2/sql" + "github.com/molecula/featurebase/v2/test" +) + +func TestHandler(t *testing.T) { + cluster := test.MustRunCluster(t, 1) + defer cluster.Close() + api := cluster.GetNode(0).API + queryStr := "select * from nowhere" + mapper := sql.NewMapper() + query, err := mapper.MapSQL(queryStr) + if err != nil { + t.Fatal("failed to map SQL") + } + handler := sql.NewSelectHandler(api) + _, err = handler.Handle(context.Background(), query) + if err.Error() != "mapping select: handling: nowhere: index not found" { + //expecting it to fail with index not found + t.Fatal(err) + } + +} From 41ed706d83cd4c7a420330631d326671cb6f1dcb Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 3 Nov 2021 09:02:47 -0500 Subject: [PATCH 70/76] add license --- sql/handler_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sql/handler_test.go b/sql/handler_test.go index a583f3ba3..bb82b7e9f 100644 --- a/sql/handler_test.go +++ b/sql/handler_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 Pilosa Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package sql_test import ( From 2e086b866adedc4554054dfda49937a896fc59cf Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 3 Nov 2021 09:37:52 -0500 Subject: [PATCH 71/76] . --- sql/handler_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/handler_test.go b/sql/handler_test.go index bb82b7e9f..e07b67f1e 100644 --- a/sql/handler_test.go +++ b/sql/handler_test.go @@ -35,6 +35,7 @@ func TestHandler(t *testing.T) { _, err = handler.Handle(context.Background(), query) if err.Error() != "mapping select: handling: nowhere: index not found" { //expecting it to fail with index not found + //can be more elaborate later t.Fatal(err) } From f9c78091ab023a55ee47b0250c5b55250b78043b Mon Sep 17 00:00:00 2001 From: reesporte Date: Wed, 3 Nov 2021 13:05:47 -0500 Subject: [PATCH 72/76] move warning label to results count --- .../App/Query/QueryResults/QueryResults.tsx | 33 ++++-------- .../GroupByChart/GroupByChart.tsx | 3 -- lattice/src/shared/DataTable/DataTable.tsx | 54 +++++++++---------- lattice/src/shared/Pager/Pager.tsx | 39 ++++++++++---- 4 files changed, 65 insertions(+), 64 deletions(-) diff --git a/lattice/src/App/Query/QueryResults/QueryResults.tsx b/lattice/src/App/Query/QueryResults/QueryResults.tsx index e940a7ab6..02ac554bb 100644 --- a/lattice/src/App/Query/QueryResults/QueryResults.tsx +++ b/lattice/src/App/Query/QueryResults/QueryResults.tsx @@ -136,35 +136,20 @@ export const QueryResults: FC = ({
{!collapsibleQuery || showQuery ? ( -
- {queryString} - - {results.totalMessageCount > 1000 ? ( - - - NOTE - - - ) : null} -
+ + {queryString} + ) : null}
{results.operation === "GroupBy" && results.rows.length <= 50 ? ( ) : ( - + )} ); diff --git a/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx b/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx index 90973a231..81af794f7 100644 --- a/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx +++ b/lattice/src/App/QueryBuilder/GroupByChart/GroupByChart.tsx @@ -43,9 +43,6 @@ export const GroupByChart: FC = ({ results }) => { return (
- This query has {results.totalMessageCount} total results. Due to browser - memory limitations, you will only be able to see {results.rows.length}{" "} - results. 0 ? uniqueKeys : undefined} diff --git a/lattice/src/shared/DataTable/DataTable.tsx b/lattice/src/shared/DataTable/DataTable.tsx index 94510f86d..342511d3f 100644 --- a/lattice/src/shared/DataTable/DataTable.tsx +++ b/lattice/src/shared/DataTable/DataTable.tsx @@ -1,35 +1,37 @@ -import React, { FC, Fragment, useEffect, useRef, useState } from 'react'; -import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; -import classNames from 'classnames'; -import OrderBy from 'lodash/orderBy'; -import Table from '@material-ui/core/Table'; -import TableBody from '@material-ui/core/TableBody'; -import TableCell from '@material-ui/core/TableCell'; -import TableHead from '@material-ui/core/TableHead'; +import React, { FC, Fragment, useEffect, useRef, useState } from "react"; +import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; +import classNames from "classnames"; +import OrderBy from "lodash/orderBy"; +import Table from "@material-ui/core/Table"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; +import TableHead from "@material-ui/core/TableHead"; // import TablePagination from '@material-ui/core/TablePagination'; -import TableRow from '@material-ui/core/TableRow'; -import Typography from '@material-ui/core/Typography'; -import { ColumnInfo } from 'proto/pilosa_pb'; -import { Pager } from 'shared/Pager'; -import { formatTableCell } from 'shared/utils/formatTableCell'; -import css from './DataTable.module.scss'; +import TableRow from "@material-ui/core/TableRow"; +import Typography from "@material-ui/core/Typography"; +import { ColumnInfo } from "proto/pilosa_pb"; +import { Pager } from "shared/Pager"; +import { formatTableCell } from "shared/utils/formatTableCell"; +import css from "./DataTable.module.scss"; type TableProps = { headers: ColumnInfo.AsObject[]; data: any[]; loading?: boolean; autoWidth?: boolean; + totalResultsCount: number; }; export const DataTable: FC = ({ headers, data, loading = false, - autoWidth = false + autoWidth = false, + totalResultsCount, }) => { const [sortedData, setSortedData] = useState(data); const [sort, setSort] = useState(headers[0]?.name); - const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc'); + const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); const [page, setPage] = useState(1); const [rowsPerPage, setRowsPerPage] = useState(10); const resultsRef = useRef(null); @@ -54,8 +56,8 @@ export const DataTable: FC = ({ setTimeout(() => { if (resultsRef.current) { resultsRef.current.scrollIntoView({ - behavior: 'smooth', - block: 'start' + behavior: "smooth", + block: "start", }); } }, 0); @@ -63,15 +65,13 @@ export const DataTable: FC = ({ const onSortClick = (name: string) => { if (sort === name) { - setSortDir(sortDir === 'desc' ? 'asc' : 'desc'); + setSortDir(sortDir === "desc" ? "asc" : "desc"); } else { setSort(name); - setSortDir('asc'); + setSortDir("asc"); } }; - - return (
@@ -86,14 +86,14 @@ export const DataTable: FC = ({ > onSortClick(col.name)} > {col.name} @@ -112,7 +112,7 @@ export const DataTable: FC = ({ .map((row, rowIdx) => ( {headers.map((col, colIdx) => ( = ({ className={css.tableCell} > {formatTableCell(row, col)} - + ))} {autoWidth ? : null} @@ -148,7 +148,6 @@ export const DataTable: FC = ({
- = ({ showTotal={true} onChangePage={onChangePage} onChangePerPage={onChangePerPage} + totalResultsCount={totalResultsCount} />
); diff --git a/lattice/src/shared/Pager/Pager.tsx b/lattice/src/shared/Pager/Pager.tsx index d9babbcd6..2f8cfc817 100644 --- a/lattice/src/shared/Pager/Pager.tsx +++ b/lattice/src/shared/Pager/Pager.tsx @@ -1,9 +1,11 @@ -import React, { FC } from 'react'; -import classNames from 'classnames'; -import Pagination from '@material-ui/lab/Pagination'; -import Pluralize from 'react-pluralize'; -import { Select } from 'shared/Select'; -import css from './Pager.module.scss'; +import React, { FC } from "react"; +import ErrorOutlineIcon from "@material-ui/icons/ErrorOutline"; +import Tooltip from "@material-ui/core/Tooltip"; +import classNames from "classnames"; +import Pagination from "@material-ui/lab/Pagination"; +import Pluralize from "react-pluralize"; +import { Select } from "shared/Select"; +import css from "./Pager.module.scss"; type PagerProps = { page: number; @@ -13,6 +15,7 @@ type PagerProps = { className?: any; onChangePage: (page: number) => void; onChangePerPage?: (rowsPerPage: number) => void; + totalResultsCount?: number; }; export const Pager: FC = ({ @@ -22,7 +25,8 @@ export const Pager: FC = ({ showTotal = true, className, onChangePage, - onChangePerPage + onChangePerPage, + totalResultsCount = 0, }) => { const numPages = Math.ceil(totalItems / rowsPerPage); const startResults = (page - 1) * rowsPerPage + 1; @@ -43,9 +47,9 @@ export const Pager: FC = ({ label="Per Page" value={rowsPerPage} options={[ - { label: '10', value: '10' }, - { label: '25', value: '25' }, - { label: '50', value: '50' } + { label: "10", value: "10" }, + { label: "25", value: "25" }, + { label: "50", value: "50" }, ]} onChange={(value) => onChangePerPage(Number(value))} fullWidth @@ -59,6 +63,21 @@ export const Pager: FC = ({
)} + {totalResultsCount > 1000 && ( + + + + + + )}
); }; From 1585313a0cf07617e32b2541e1e3b21d56fe247d Mon Sep 17 00:00:00 2001 From: reesporte Date: Wed, 3 Nov 2021 13:14:28 -0500 Subject: [PATCH 73/76] better styling --- lattice/src/shared/Pager/Pager.tsx | 36 +++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/lattice/src/shared/Pager/Pager.tsx b/lattice/src/shared/Pager/Pager.tsx index 2f8cfc817..b3f198f90 100644 --- a/lattice/src/shared/Pager/Pager.tsx +++ b/lattice/src/shared/Pager/Pager.tsx @@ -59,25 +59,31 @@ export const Pager: FC = ({ {showTotal && (
+ {totalResultsCount > 1000 && ( + + + + + + )} Showing {startResults} - {endResults} of{` `}
)} - {totalResultsCount > 1000 && ( - - - - - - )} ); }; From e11ce9a248034d9b66a79456c9544841653b2ace Mon Sep 17 00:00:00 2001 From: reesporte Date: Wed, 3 Nov 2021 13:18:41 -0500 Subject: [PATCH 74/76] clearer wording --- lattice/src/shared/Pager/Pager.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lattice/src/shared/Pager/Pager.tsx b/lattice/src/shared/Pager/Pager.tsx index b3f198f90..c483ef2c9 100644 --- a/lattice/src/shared/Pager/Pager.tsx +++ b/lattice/src/shared/Pager/Pager.tsx @@ -71,9 +71,7 @@ export const Pager: FC = ({ title={ "This query has " + totalResultsCount + - " results. Due to browser memory limitations, you will only be able to view " + - 1000 + - " of them." + " results. Due to browser memory limitations, you will only be able to view the first 1000." } > From 7b3dee5653ed389b2d336cbe8ebb8951e068f933 Mon Sep 17 00:00:00 2001 From: reesporte Date: Thu, 4 Nov 2021 11:55:32 -0500 Subject: [PATCH 75/76] single quotify --- lattice/src/App/Query/QueryContainer.tsx | 64 +++++----- .../App/Query/QueryResults/QueryResults.tsx | 44 +++---- .../GroupByChart/GroupByChart.tsx | 50 ++++---- .../QueryBuilder/QueryBuilderContainer.tsx | 114 +++++++++--------- lattice/src/shared/DataTable/DataTable.tsx | 42 +++---- lattice/src/shared/Pager/Pager.tsx | 32 ++--- 6 files changed, 173 insertions(+), 173 deletions(-) diff --git a/lattice/src/App/Query/QueryContainer.tsx b/lattice/src/App/Query/QueryContainer.tsx index 4623cfc3c..a26190eb0 100644 --- a/lattice/src/App/Query/QueryContainer.tsx +++ b/lattice/src/App/Query/QueryContainer.tsx @@ -1,17 +1,17 @@ -import React, { FC, useState } from "react"; -import moment, { Moment } from "moment"; -import uniqBy from "lodash/uniqBy"; -import { Query } from "./Query"; -import { pilosa } from "services/eventServices"; -import { useEffectOnce } from "react-use"; -import { grpc } from "@improbable-eng/grpc-web"; -import { queryPQL, querySQL } from "services/grpcServices"; -import { ColumnInfo, ColumnResponse, RowResponse } from "proto/pilosa_pb"; +import React, { FC, useState } from 'react'; +import moment, { Moment } from 'moment'; +import uniqBy from 'lodash/uniqBy'; +import { Query } from './Query'; +import { pilosa } from 'services/eventServices'; +import { useEffectOnce } from 'react-use'; +import { grpc } from '@improbable-eng/grpc-web'; +import { queryPQL, querySQL } from 'services/grpcServices'; +import { ColumnInfo, ColumnResponse, RowResponse } from 'proto/pilosa_pb'; export type ResultType = { query: string; operation: string; - type: "PQL" | "SQL"; + type: 'PQL' | 'SQL'; headers: ColumnInfo.AsObject[]; rows: ColumnResponse.AsObject[][]; duration?: number; @@ -22,13 +22,13 @@ export type ResultType = { }; let streamingResults: ResultType = { - query: "", - operation: "", - type: "SQL", + query: '', + operation: '', + type: 'SQL', headers: [], rows: [], roundtrip: 0, - error: "", + error: '', totalMessageCount: 0, }; @@ -66,72 +66,72 @@ export const QueryContainer: FC<{}> = () => { setErrorResult(streamingResults); } else { let recentQueries = JSON.parse( - localStorage.getItem("recent-queries") || "[]" + localStorage.getItem('recent-queries') || '[]' ); - const lastQuery = localStorage.getItem("last-query"); + const lastQuery = localStorage.getItem('last-query'); recentQueries.unshift(lastQuery); recentQueries = uniqBy(recentQueries); if (recentQueries.length > 10) { localStorage.setItem( - "recent-queries", + 'recent-queries', JSON.stringify(recentQueries.slice(0, 9)) ); } else { - localStorage.setItem("recent-queries", JSON.stringify(recentQueries)); + localStorage.setItem('recent-queries', JSON.stringify(recentQueries)); } streamingResults.roundtrip = moment .duration(moment().diff(startTime)) - .as("milliseconds"); + .as('milliseconds'); setErrorResult(undefined); setResults([streamingResults, ...results]); } setLoading(false); }; - const onQuery = (query: string, type: "PQL" | "SQL", index?: string) => { + const onQuery = (query: string, type: 'PQL' | 'SQL', index?: string) => { streamingResults = { query, - operation: "", + operation: '', type, headers: [], rows: [], index, roundtrip: 0, - error: "", + error: '', totalMessageCount: 0, }; startTime = moment(); if (query) { setLoading(true); localStorage.setItem( - "last-query", - type === "PQL" ? `[${index}]${query}` : query + 'last-query', + type === 'PQL' ? `[${index}]${query}` : query ); - if (type === "PQL") { + if (type === 'PQL') { if (index) { queryPQL(index, query, handleQueryMessages, handleQueryEnd); } else { - streamingResults.error = "missing index"; + streamingResults.error = 'missing index'; setErrorResult(streamingResults); setLoading(false); } } else { - let queryArr = query.split(" "); + let queryArr = query.split(' '); queryArr.forEach((word, idx) => { - if (word.includes("-")) { - let wordArr = word.split("."); + if (word.includes('-')) { + let wordArr = word.split('.'); wordArr.forEach((section, idx) => { - if (section.includes("-") && !word.includes("`")) { + if (section.includes('-') && !word.includes('`')) { wordArr[idx] = `\`${wordArr[idx]}\``; } }); - queryArr[idx] = wordArr.join("."); + queryArr[idx] = wordArr.join('.'); } }); - querySQL(queryArr.join(" "), handleQueryMessages, handleQueryEnd); + querySQL(queryArr.join(' '), handleQueryMessages, handleQueryEnd); } } }; diff --git a/lattice/src/App/Query/QueryResults/QueryResults.tsx b/lattice/src/App/Query/QueryResults/QueryResults.tsx index 02ac554bb..782ad95e0 100644 --- a/lattice/src/App/Query/QueryResults/QueryResults.tsx +++ b/lattice/src/App/Query/QueryResults/QueryResults.tsx @@ -1,15 +1,15 @@ -import React, { FC, Fragment, useState } from "react"; -import CloseIcon from "@material-ui/icons/Close"; -import copy from "copy-to-clipboard"; -import FileCopySharpIcon from "@material-ui/icons/FileCopySharp"; -import IconButton from "@material-ui/core/IconButton"; -import Tooltip from "@material-ui/core/Tooltip"; -import Typography from "@material-ui/core/Typography"; -import { DataTable } from "shared/DataTable"; -import { formatDuration } from "shared/utils/formatDuration"; -import { GroupByChart } from "App/QueryBuilder/GroupByChart"; -import { ResultType } from "../QueryContainer"; -import css from "./QueryResults.module.scss"; +import React, { FC, Fragment, useState } from 'react'; +import CloseIcon from '@material-ui/icons/Close'; +import copy from 'copy-to-clipboard'; +import FileCopySharpIcon from '@material-ui/icons/FileCopySharp'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; +import { DataTable } from 'shared/DataTable'; +import { formatDuration } from 'shared/utils/formatDuration'; +import { GroupByChart } from 'App/QueryBuilder/GroupByChart'; +import { ResultType } from '../QueryContainer'; +import css from './QueryResults.module.scss'; type QueryResultsProps = { collapsibleQuery?: boolean; @@ -23,9 +23,9 @@ export const QueryResults: FC = ({ onRemoveResult, }) => { const [showQuery, setShowQuery] = useState(false); - const [copyTooltip, setCopyTooltip] = useState("Copy Query"); + const [copyTooltip, setCopyTooltip] = useState('Copy Query'); const queryString = - results.type === "PQL" + results.type === 'PQL' ? `[${results.index}]${results.query}` : results.query; @@ -34,10 +34,10 @@ export const QueryResults: FC = ({ let rowData = {}; row.forEach((col, colIdx) => { const header = headers[colIdx]; - if (header.datatype.includes("[]")) { + if (header.datatype.includes('[]')) { const dataTypeVal = `${header.datatype.slice(2)}arrayval`; - rowData[header.name] = col[dataTypeVal].valsList.join(", "); - } else if (header.datatype === "decimal") { + rowData[header.name] = col[dataTypeVal].valsList.join(', '); + } else if (header.datatype === 'decimal') { const decimalVal = col[`${header.datatype}val`]; if (decimalVal) { const { value, scale } = decimalVal; @@ -55,9 +55,9 @@ export const QueryResults: FC = ({ const onCopyQuery = () => { copy(queryString); - setCopyTooltip("Copied!"); + setCopyTooltip('Copied!'); setTimeout(() => { - setCopyTooltip("Copy Query"); + setCopyTooltip('Copy Query'); }, 1500); }; @@ -75,7 +75,7 @@ export const QueryResults: FC = ({ onClick={() => setShowQuery(!showQuery)} className={css.link} > - {showQuery ? "Hide" : "Show"} Query + {showQuery ? 'Hide' : 'Show'} Query )} @@ -136,12 +136,12 @@ export const QueryResults: FC = ({ {!collapsibleQuery || showQuery ? ( - + {queryString} ) : null} - {results.operation === "GroupBy" && results.rows.length <= 50 ? ( + {results.operation === 'GroupBy' && results.rows.length <= 50 ? ( ) : ( = ({ results }) => { const theme = useTheme(); - const isDark = theme.palette.type === "dark"; + const isDark = theme.palette.type === 'dark'; const { headers, rows } = results; let uniqueKeys: string[] = []; const grouped = groupBy(rows, (row) => row[0].stringval); @@ -42,30 +42,30 @@ export const GroupByChart: FC = ({ results }) => { }); return ( -
+
0 ? uniqueKeys : undefined} indexBy={headers[0].name} margin={{ top: 50, right: 130, bottom: 100, left: 60 }} padding={0.3} - valueScale={{ type: "linear" }} - indexScale={{ type: "band", round: true }} + valueScale={{ type: 'linear' }} + indexScale={{ type: 'band', round: true }} colors={schemeTableau10} enableLabel={false} theme={ isDark ? { - textColor: "var(--text-secondary)", + textColor: 'var(--text-secondary)', axis: { - domain: { line: { stroke: "rgba(255, 255, 255, 0.1)" } }, + domain: { line: { stroke: 'rgba(255, 255, 255, 0.1)' } }, }, - grid: { line: { stroke: "rgba(255, 255, 255, 0.1)" } }, - tooltip: { container: { background: "#1c2022" } }, + grid: { line: { stroke: 'rgba(255, 255, 255, 0.1)' } }, + tooltip: { container: { background: '#1c2022' } }, } : { axis: { - domain: { line: { stroke: "#dddddd" } }, + domain: { line: { stroke: '#dddddd' } }, }, } } @@ -75,15 +75,15 @@ export const GroupByChart: FC = ({ results }) => { tickPadding: 5, tickRotation: -40, legend: headers[0].name, - legendPosition: "middle", + legendPosition: 'middle', legendOffset: 80, }} axisLeft={{ tickSize: 5, tickPadding: 5, tickRotation: 0, - legend: "count", - legendPosition: "middle", + legend: 'count', + legendPosition: 'middle', legendOffset: -40, }} tooltip={({ id, value, color }) => ( @@ -93,25 +93,25 @@ export const GroupByChart: FC = ({ results }) => { )} labelSkipWidth={12} labelSkipHeight={12} - labelTextColor={{ from: "color", modifiers: [["darker", 1.6]] }} + labelTextColor={{ from: 'color', modifiers: [['darker', 1.6]] }} legends={[ { - dataFrom: "keys", - anchor: "bottom-right", - direction: "column", + dataFrom: 'keys', + anchor: 'bottom-right', + direction: 'column', justify: false, translateX: 120, translateY: 0, itemsSpacing: 2, itemWidth: 120, itemHeight: 20, - itemDirection: "left-to-right", + itemDirection: 'left-to-right', itemOpacity: 0.85, - symbolShape: "circle", + symbolShape: 'circle', symbolSize: 20, effects: [ { - on: "hover", + on: 'hover', style: { itemOpacity: 1, }, diff --git a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx index a526fd372..da27904de 100644 --- a/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx +++ b/lattice/src/App/QueryBuilder/QueryBuilderContainer.tsx @@ -1,38 +1,38 @@ -import React, { Fragment, useState } from "react"; -import Alert from "@material-ui/lab/Alert"; -import ArrowForwardIosIcon from "@material-ui/icons/ArrowForwardIos"; -import Button from "@material-ui/core/Button"; -import classNames from "classnames"; -import CloseIcon from "@material-ui/icons/Close"; -import IconButton from "@material-ui/core/IconButton"; -import InfoIcon from "@material-ui/icons/Info"; -import moment, { Moment } from "moment"; -import Paper from "@material-ui/core/Paper"; -import Split from "react-split"; -import Snackbar from "@material-ui/core/Snackbar"; -import Tooltip from "@material-ui/core/Tooltip"; -import Typography from "@material-ui/core/Typography"; -import { Block } from "shared/Block"; -import { formatDuration } from "shared/utils/formatDuration"; -import { grpc } from "@improbable-eng/grpc-web"; -import { pilosa } from "services/eventServices"; -import { QueryBuilder } from "./QueryBuilder"; -import { queryPQL } from "services/grpcServices"; -import { QueryResults } from "App/Query/QueryResults"; -import { ResultType } from "App/Query/QueryContainer"; -import { RowResponse } from "proto/pilosa_pb"; -import { SavedQueries } from "App/QueryBuilder/SavedQueries"; -import { useEffectOnce } from "react-use"; -import css from "./QueryBuilderContainer.module.scss"; +import React, { Fragment, useState } from 'react'; +import Alert from '@material-ui/lab/Alert'; +import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos'; +import Button from '@material-ui/core/Button'; +import classNames from 'classnames'; +import CloseIcon from '@material-ui/icons/Close'; +import IconButton from '@material-ui/core/IconButton'; +import InfoIcon from '@material-ui/icons/Info'; +import moment, { Moment } from 'moment'; +import Paper from '@material-ui/core/Paper'; +import Split from 'react-split'; +import Snackbar from '@material-ui/core/Snackbar'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; +import { Block } from 'shared/Block'; +import { formatDuration } from 'shared/utils/formatDuration'; +import { grpc } from '@improbable-eng/grpc-web'; +import { pilosa } from 'services/eventServices'; +import { QueryBuilder } from './QueryBuilder'; +import { queryPQL } from 'services/grpcServices'; +import { QueryResults } from 'App/Query/QueryResults'; +import { ResultType } from 'App/Query/QueryContainer'; +import { RowResponse } from 'proto/pilosa_pb'; +import { SavedQueries } from 'App/QueryBuilder/SavedQueries'; +import { useEffectOnce } from 'react-use'; +import css from './QueryBuilderContainer.module.scss'; let streamingResults: ResultType = { - query: "", - operation: "", - type: "PQL", + query: '', + operation: '', + type: 'PQL', headers: [], rows: [], roundtrip: 0, - error: "", + error: '', totalMessageCount: 0, }; @@ -40,10 +40,10 @@ export const QueryBuilderContainer = () => { let startTime: Moment; let exportRows: any[] = []; const colSizes = JSON.parse( - localStorage.getItem("builderColSizes") || "[25, 75]" + localStorage.getItem('builderColSizes') || '[25, 75]' ); const [queriesList, setQueriesList] = useState( - JSON.parse(localStorage.getItem("saved-queries") || "[]") + JSON.parse(localStorage.getItem('saved-queries') || '[]') ); const [tables, setTables] = useState([]); @@ -52,7 +52,7 @@ export const QueryBuilderContainer = () => { const [fullCount, setFullCount] = useState(); const [recordsCount, setRecordsCount] = useState(); const [errorResult, setErrorResult] = useState(); - const [error, setError] = useState(""); + const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const [savedQuery, setSavedQuery] = useState(-1); @@ -78,7 +78,7 @@ export const QueryBuilderContainer = () => { } else { streamingResults.roundtrip = moment .duration(moment().diff(startTime)) - .as("milliseconds"); + .as('milliseconds'); setErrorResult(undefined); setResults(streamingResults); } @@ -90,12 +90,12 @@ export const QueryBuilderContainer = () => { let rowStr: string[] = []; if (exportRows.length === 0) { const headers = response.headersList.map((header) => header.name); - exportRows.push(headers.join("\t")); + exportRows.push(headers.join('\t')); } response.headersList.forEach((header, idx) => rowStr.push(response.columnsList[idx][`${header.datatype}val`]) ); - exportRows.push(rowStr.join("\t")); + exportRows.push(rowStr.join('\t')); }; const handleExternalLookupEnd = ( @@ -105,12 +105,12 @@ export const QueryBuilderContainer = () => { if (status !== grpc.Code.OK) { setError(statusMessage); } else if (exportRows.length === 0) { - setError("No record attributes for current query."); + setError('No record attributes for current query.'); } else { const dateTime = moment().unix(); - const element = document.createElement("a"); - const file = new Blob([exportRows.join("\n")], { - type: "text/plain;charset=utf-8", + const element = document.createElement('a'); + const file = new Blob([exportRows.join('\n')], { + type: 'text/plain;charset=utf-8', }); element.href = URL.createObjectURL(file); element.download = `molecula-${results?.index}-${dateTime}.csv`; @@ -129,18 +129,18 @@ export const QueryBuilderContainer = () => { streamingResults = { query, operation, - type: "PQL", + type: 'PQL', headers: [], rows: [], index: table, roundtrip: 0, - error: "", + error: '', totalMessageCount: 0, }; startTime = moment(); setLoading(true); - if (operation !== "Count") { + if (operation !== 'Count') { if (countQuery) { pilosa.post.query(table, countQuery).then((res) => { setFullCount(res.data.results[0]); @@ -166,13 +166,13 @@ export const QueryBuilderContainer = () => { const onRemoveQuery = (queryIdx: number) => { let queries = [...queriesList]; queries.splice(queryIdx, 1); - localStorage.setItem("saved-queries", JSON.stringify(queries)); + localStorage.setItem('saved-queries', JSON.stringify(queries)); setQueriesList(queries); }; const onSaveQuery = () => { const updatedList = JSON.parse( - localStorage.getItem("saved-queries") || "[]" + localStorage.getItem('saved-queries') || '[]' ); if (savedQuery < 0) { setSavedQuery(updatedList.length - 1); @@ -183,7 +183,7 @@ export const QueryBuilderContainer = () => { const onExportLogs = () => { if (results) { const columns = results.rows.map((row) => row[0].uint64val); - const table = results.index ? results.index : ""; + const table = results.index ? results.index : ''; onExternalLookup(table, columns); } }; @@ -202,17 +202,17 @@ export const QueryBuilderContainer = () => { cursor="col-resize" minSize={showBuilder ? 350 : 50} onDragEnd={(sizes) => - localStorage.setItem("builderColSizes", JSON.stringify(sizes)) + localStorage.setItem('builderColSizes', JSON.stringify(sizes)) } gutter={(_index, direction) => { - const gutter = document.createElement("div"); + const gutter = document.createElement('div'); gutter.className = `gutter gutter-${direction}`; - const dragbars = document.createElement("div"); - dragbars.className = "dragBar"; + const dragbars = document.createElement('div'); + dragbars.className = 'dragBar'; gutter.appendChild(dragbars); return gutter; }} - className={classNames(css.split, !showBuilder ? "hide-gutter" : "")} + className={classNames(css.split, !showBuilder ? 'hide-gutter' : '')} > {showBuilder ? (
@@ -241,9 +241,9 @@ export const QueryBuilderContainer = () => {
- Results{" "} + Results{' '} - {results?.query.includes("Extract(") ? ( + {results?.query.includes('Extract(') ? (
{
{results.duration ? (
- {recordsCount.toLocaleString()} records scanned in{" "} + {recordsCount.toLocaleString()} records scanned in{' '} {formatDuration(results.duration, true)}.
) : null}
- Showing{" "} - {fullCount > 1000 ? "first 1,000 rows of" : "all"}{" "} + Showing{' '} + {fullCount > 1000 ? 'first 1,000 rows of' : 'all'}{' '} {fullCount.toLocaleString()} results.
@@ -303,7 +303,7 @@ export const QueryBuilderContainer = () => {
- setError("")}> + setError('')}> {error} diff --git a/lattice/src/shared/DataTable/DataTable.tsx b/lattice/src/shared/DataTable/DataTable.tsx index 342511d3f..10cf19865 100644 --- a/lattice/src/shared/DataTable/DataTable.tsx +++ b/lattice/src/shared/DataTable/DataTable.tsx @@ -1,18 +1,18 @@ -import React, { FC, Fragment, useEffect, useRef, useState } from "react"; -import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown"; -import classNames from "classnames"; -import OrderBy from "lodash/orderBy"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; -import TableCell from "@material-ui/core/TableCell"; -import TableHead from "@material-ui/core/TableHead"; +import React, { FC, Fragment, useEffect, useRef, useState } from 'react'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; +import classNames from 'classnames'; +import OrderBy from 'lodash/orderBy'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; // import TablePagination from '@material-ui/core/TablePagination'; -import TableRow from "@material-ui/core/TableRow"; -import Typography from "@material-ui/core/Typography"; -import { ColumnInfo } from "proto/pilosa_pb"; -import { Pager } from "shared/Pager"; -import { formatTableCell } from "shared/utils/formatTableCell"; -import css from "./DataTable.module.scss"; +import TableRow from '@material-ui/core/TableRow'; +import Typography from '@material-ui/core/Typography'; +import { ColumnInfo } from 'proto/pilosa_pb'; +import { Pager } from 'shared/Pager'; +import { formatTableCell } from 'shared/utils/formatTableCell'; +import css from './DataTable.module.scss'; type TableProps = { headers: ColumnInfo.AsObject[]; @@ -31,7 +31,7 @@ export const DataTable: FC = ({ }) => { const [sortedData, setSortedData] = useState(data); const [sort, setSort] = useState(headers[0]?.name); - const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); + const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc'); const [page, setPage] = useState(1); const [rowsPerPage, setRowsPerPage] = useState(10); const resultsRef = useRef(null); @@ -56,8 +56,8 @@ export const DataTable: FC = ({ setTimeout(() => { if (resultsRef.current) { resultsRef.current.scrollIntoView({ - behavior: "smooth", - block: "start", + behavior: 'smooth', + block: 'start', }); } }, 0); @@ -65,10 +65,10 @@ export const DataTable: FC = ({ const onSortClick = (name: string) => { if (sort === name) { - setSortDir(sortDir === "desc" ? "asc" : "desc"); + setSortDir(sortDir === 'desc' ? 'asc' : 'desc'); } else { setSort(name); - setSortDir("asc"); + setSortDir('asc'); } }; @@ -93,7 +93,7 @@ export const DataTable: FC = ({ {col.name} @@ -112,7 +112,7 @@ export const DataTable: FC = ({ .map((row, rowIdx) => ( {headers.map((col, colIdx) => ( = ({ label="Per Page" value={rowsPerPage} options={[ - { label: "10", value: "10" }, - { label: "25", value: "25" }, - { label: "50", value: "50" }, + { label: '10', value: '10' }, + { label: '25', value: '25' }, + { label: '50', value: '50' }, ]} onChange={(value) => onChangePerPage(Number(value))} fullWidth @@ -62,16 +62,16 @@ export const Pager: FC = ({ {totalResultsCount > 1000 && ( From f70fdfb59e465cb78c5e8ab3d5418c662b8ceab9 Mon Sep 17 00:00:00 2001 From: reesporte Date: Thu, 4 Nov 2021 12:14:01 -0500 Subject: [PATCH 76/76] remove inline styling --- .../src/App/Query/QueryResults/QueryResults.module.scss | 1 - lattice/src/App/Query/QueryResults/QueryResults.tsx | 4 +--- lattice/src/shared/Pager/Pager.module.scss | 5 +++++ lattice/src/shared/Pager/Pager.tsx | 8 +------- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lattice/src/App/Query/QueryResults/QueryResults.module.scss b/lattice/src/App/Query/QueryResults/QueryResults.module.scss index cd83bf011..92762c813 100644 --- a/lattice/src/App/Query/QueryResults/QueryResults.module.scss +++ b/lattice/src/App/Query/QueryResults/QueryResults.module.scss @@ -30,7 +30,6 @@ .queryString { white-space: pre-wrap; - display: block; font-size: 12px; } diff --git a/lattice/src/App/Query/QueryResults/QueryResults.tsx b/lattice/src/App/Query/QueryResults/QueryResults.tsx index 782ad95e0..33c016f09 100644 --- a/lattice/src/App/Query/QueryResults/QueryResults.tsx +++ b/lattice/src/App/Query/QueryResults/QueryResults.tsx @@ -136,9 +136,7 @@ export const QueryResults: FC = ({
{!collapsibleQuery || showQuery ? ( - - {queryString} - + {queryString} ) : null}
{results.operation === 'GroupBy' && results.rows.length <= 50 ? ( diff --git a/lattice/src/shared/Pager/Pager.module.scss b/lattice/src/shared/Pager/Pager.module.scss index c161d396c..26d9840da 100644 --- a/lattice/src/shared/Pager/Pager.module.scss +++ b/lattice/src/shared/Pager/Pager.module.scss @@ -40,3 +40,8 @@ font-size: 0.75rem; color: rgba(var(--contrast-rgb), 0.54); } + +.tooMany { + vertical-align: middle; + margin-right: .5em; +} diff --git a/lattice/src/shared/Pager/Pager.tsx b/lattice/src/shared/Pager/Pager.tsx index 0a58b5836..16590129b 100644 --- a/lattice/src/shared/Pager/Pager.tsx +++ b/lattice/src/shared/Pager/Pager.tsx @@ -60,13 +60,7 @@ export const Pager: FC = ({ {showTotal && (
{totalResultsCount > 1000 && ( - +