From d1c66445c1d335d67df7256f3b1845ef65fb6f4c Mon Sep 17 00:00:00 2001 From: Devdutt Shenoi Date: Tue, 10 Dec 2024 18:20:47 +0530 Subject: [PATCH 1/5] ci: lint, test and coverage report (#1028) Signed-off-by: Devdutt Shenoi --- .github/workflows/coverage.yaml | 57 ++++++++++++++++++++++++++++++ .github/workflows/lint.yaml | 61 --------------------------------- 2 files changed, 57 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/coverage.yaml delete mode 100644 .github/workflows/lint.yaml diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml new file mode 100644 index 000000000..eb5a46da4 --- /dev/null +++ b/.github/workflows/coverage.yaml @@ -0,0 +1,57 @@ +on: + pull_request: + paths-ignore: + - "docs/**" + - "helm/**" + - "assets/**" + - "**.md" + push: + branches: + - main + +name: Lint, Test and Coverage Report +jobs: + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - uses: Swatinem/rust-cache@v2 + with: + shared-key: ${{ runner.os }}-cargo + + - uses: taiki-e/install-action@v2 + with: + tool: cargo-hack, cargo-llvm-cov, nextest + + - name: Check with clippy + run: cargo hack clippy --verbose --each-feature --no-dev-deps -- -D warnings + + - name: Check docs + run: cargo hack doc --verbose --no-deps --each-feature --no-dev-deps + + - name: Doctests + run: cargo hack --each-feature test --doc + + - name: Tests + run: cargo hack --each-feature llvm-cov --no-report nextest + + - name: Genrate coverage report + run: cargo llvm-cov report --lcov --output-path coverage.lcov + + - name: Upload Coverage Report + uses: coverallsapp/github-action@v2.2.3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + file: ./coverage.lcov + parallel: true + flag-name: run-${{ matrix.os }}-cargo + + - name: Finish Coverage Report + uses: coverallsapp/github-action@v2.2.3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel-finished: true diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index 4f2578cf5..000000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: Linter - -on: - pull_request: - paths-ignore: - - 'docs/**' - - 'helm/**' - - 'assets/**' - - '**.md' - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - - unit-tests: - name: Unit tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - - fmt: - name: Rust fmt check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - clippy: - name: Cargo Clippy check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add clippy - - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings From 71450ebb39f42f711e2e094192117e91186d4bca Mon Sep 17 00:00:00 2001 From: AdheipSingh <34169002+AdheipSingh@users.noreply.github.com> Date: Wed, 11 Dec 2024 03:41:04 +0530 Subject: [PATCH 2/5] chore: add enabled field in secrets for better handling (#1029) --- helm/templates/standalone-deployment.yaml | 15 +++-- helm/values.yaml | 79 +++++++++++------------ 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/helm/templates/standalone-deployment.yaml b/helm/templates/standalone-deployment.yaml index dc0c8dbfc..abb615884 100644 --- a/helm/templates/standalone-deployment.yaml +++ b/helm/templates/standalone-deployment.yaml @@ -47,8 +47,8 @@ spec: - name: {{ $key }} value: {{ tpl $value $ | quote }} {{- end }} - {{- if .Values.parseable.localModeSecret }} - {{- range $secret := .Values.parseable.localModeSecret }} + {{- if and .Values.parseable.localModeSecret .Values.parseable.localModeSecret.enabled }} + {{- range $secret := .Values.parseable.localModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -59,8 +59,10 @@ spec: key: {{ $key }} {{- end }} {{- end }} - {{- else }} - {{- range $secret := .Values.parseable.s3ModeSecret }} + {{- end }} + + {{- if and .Values.parseable.s3ModeSecret .Values.parseable.s3ModeSecret.enabled }} + {{- range $secret := .Values.parseable.s3ModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -72,7 +74,9 @@ spec: {{- end }} {{- end }} {{- end }} - {{- range $secret := .Values.parseable.blobModeSecret }} + + {{- if and .Values.parseable.blobModeSecret .Values.parseable.blobModeSecret.enabled }} + {{- range $secret := .Values.parseable.blobModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -83,6 +87,7 @@ spec: key: {{ $key }} {{- end }} {{- end }} + {{- end }} ports: - containerPort: 8000 {{- with .Values.readinessProbe }} diff --git a/helm/values.yaml b/helm/values.yaml index 949ae2365..83aa6d251 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -77,46 +77,45 @@ parseable: ## blobModeSecret if store is set to blob-store ## s3ModeSecret if store is set to s3-store localModeSecret: - - type: env - name: parseable-env-secret - prefix: P_ - keys: - - addr - - username - - password - - staging.dir - - fs.dir - # blobModeSecret: - # - type: env - # name: parseable-env-secret - # prefix: p_ - # keys: - # - addr - # - username - # - password - # - azr.access_key - # - azr.account - # - azr.container - # - azr.url - # s3ModeSecret: - # - type: env - # name: parseable-env-secret - # prefix: P_ - # keys: - # ## Comment / uncomment the following lines as required - # # - tls.cert.path - # # - tls.key.path - # # - storage.upload.interval - # - addr - # - username - # - password - # - staging.dir - # - fs.dir - # - s3.url - # - s3.access.key - # - s3.secret.key - # - s3.bucket - # - s3.region + enabled: false + secrets: + - name: parseable-env-secret + prefix: P_ + keys: + - addr + - username + - password + - staging.dir + - fs.dir + blobModeSecret: + enabled: false + secrets: + - name: parseable-env-secret + prefix: P_ + keys: + - addr + - username + - password + - azr.access_key + - azr.account + - azr.container + - azr.url + s3ModeSecret: + enabled: false + secrets: + - name: parseable-env-secret + prefix: P_ + keys: + - addr + - username + - password + - staging.dir + - fs.dir + - s3.url + - s3.access.key + - s3.secret.key + - s3.bucket + - s3.region serviceAccount: create: true name: "parseable" From f6ac00e383d30a3b92291ec0cc9b6e3365e0d7ba Mon Sep 17 00:00:00 2001 From: Nitish Tiwari Date: Tue, 10 Dec 2024 17:21:24 -0500 Subject: [PATCH 3/5] point fix release for helm chart (#1030) --- helm-releases/parseable-1.6.4.tgz | Bin 0 -> 50130 bytes helm/Chart.yaml | 3 +- index.yaml | 121 ++++++++++++++++++------------ 3 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 helm-releases/parseable-1.6.4.tgz diff --git a/helm-releases/parseable-1.6.4.tgz b/helm-releases/parseable-1.6.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..18cc77e6eb1bc34970b0245653344bc17fcb0c51 GIT binary patch literal 50130 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%0A%6=~h2&c^aiRdvZl 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 literal 0 HcmV?d00001 diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 9860f3d8b..9a10ec40b 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: parseable description: Helm chart for Parseable Server type: application -version: 1.6.3 +version: 1.6.4 appVersion: "v1.6.3" maintainers: - name: Parseable Team @@ -18,3 +18,4 @@ dependencies: version: 0.48.0 repository: https://fluent.github.io/helm-charts condition: fluent-bit.enabled + \ No newline at end of file diff --git a/index.yaml b/index.yaml index cfe471e3e..91b3cc796 100644 --- a/index.yaml +++ b/index.yaml @@ -34,7 +34,30 @@ entries: parseable: - apiVersion: v2 appVersion: v1.6.3 - created: "2024-11-29T00:08:45.06534311+05:30" + created: "2024-12-10T17:20:03.444421031-05:00" + dependencies: + - condition: vector.enabled + name: vector + repository: https://helm.vector.dev + version: 0.20.1 + - condition: fluent-bit.enabled + name: fluent-bit + repository: https://fluent.github.io/helm-charts + version: 0.48.0 + description: Helm chart for Parseable Server + digest: 3baedde745eb646edb5caab571c30af496f9379fb07785914e6343480c168b06 + maintainers: + - email: hi@parseable.com + name: Parseable Team + url: https://parseable.com + name: parseable + type: application + urls: + - https://charts.parseable.com/helm-releases/parseable-1.6.4.tgz + version: 1.6.4 + - apiVersion: v2 + appVersion: v1.6.3 + created: "2024-12-10T17:20:03.43821683-05:00" dependencies: - condition: vector.enabled name: vector @@ -57,7 +80,7 @@ entries: version: 1.6.3 - apiVersion: v2 appVersion: v1.6.2 - created: "2024-11-29T00:08:45.059250262+05:30" + created: "2024-12-10T17:20:03.431215144-05:00" dependencies: - condition: vector.enabled name: vector @@ -80,7 +103,7 @@ entries: version: 1.6.2 - apiVersion: v2 appVersion: v1.6.1 - created: "2024-11-29T00:08:45.05077333+05:30" + created: "2024-12-10T17:20:03.423805201-05:00" dependencies: - condition: vector.enabled name: vector @@ -103,7 +126,7 @@ entries: version: 1.6.1 - apiVersion: v2 appVersion: v1.6.0 - created: "2024-11-29T00:08:45.043008895+05:30" + created: "2024-12-10T17:20:03.413613355-05:00" dependencies: - condition: vector.enabled name: vector @@ -126,7 +149,7 @@ entries: version: 1.6.0 - apiVersion: v2 appVersion: v1.5.5 - created: "2024-11-29T00:08:45.034231702+05:30" + created: "2024-12-10T17:20:03.405348908-05:00" dependencies: - condition: vector.enabled name: vector @@ -149,7 +172,7 @@ entries: version: 1.5.5 - apiVersion: v2 appVersion: v1.5.4 - created: "2024-11-29T00:08:45.026777254+05:30" + created: "2024-12-10T17:20:03.398094402-05:00" dependencies: - condition: vector.enabled name: vector @@ -172,7 +195,7 @@ entries: version: 1.5.4 - apiVersion: v2 appVersion: v1.5.3 - created: "2024-11-29T00:08:45.018990474+05:30" + created: "2024-12-10T17:20:03.388977285-05:00" dependencies: - condition: vector.enabled name: vector @@ -195,7 +218,7 @@ entries: version: 1.5.3 - apiVersion: v2 appVersion: v1.5.2 - created: "2024-11-29T00:08:45.011693608+05:30" + created: "2024-12-10T17:20:03.381112937-05:00" dependencies: - condition: vector.enabled name: vector @@ -218,7 +241,7 @@ entries: version: 1.5.2 - apiVersion: v2 appVersion: v1.5.1 - created: "2024-11-29T00:08:45.004156481+05:30" + created: "2024-12-10T17:20:03.37177663-05:00" dependencies: - condition: vector.enabled name: vector @@ -241,7 +264,7 @@ entries: version: 1.5.1 - apiVersion: v2 appVersion: v1.5.0 - created: "2024-11-29T00:08:44.996027644+05:30" + created: "2024-12-10T17:20:03.36164978-05:00" dependencies: - condition: vector.enabled name: vector @@ -264,7 +287,7 @@ entries: version: 1.5.0 - apiVersion: v2 appVersion: v1.4.0 - created: "2024-11-29T00:08:44.989389457+05:30" + created: "2024-12-10T17:20:03.35310657-05:00" dependencies: - condition: vector.enabled name: vector @@ -287,7 +310,7 @@ entries: version: 1.4.1 - apiVersion: v2 appVersion: v1.4.0 - created: "2024-11-29T00:08:44.981911848+05:30" + created: "2024-12-10T17:20:03.343252346-05:00" dependencies: - condition: vector.enabled name: vector @@ -310,7 +333,7 @@ entries: version: 1.4.0 - apiVersion: v2 appVersion: v1.3.0 - created: "2024-11-29T00:08:44.973430432+05:30" + created: "2024-12-10T17:20:03.333842856-05:00" dependencies: - condition: vector.enabled name: vector @@ -333,7 +356,7 @@ entries: version: 1.3.1 - apiVersion: v2 appVersion: v1.3.0 - created: "2024-11-29T00:08:44.965107642+05:30" + created: "2024-12-10T17:20:03.327182676-05:00" dependencies: - condition: vector.enabled name: vector @@ -356,7 +379,7 @@ entries: version: 1.3.0 - apiVersion: v2 appVersion: v1.2.0 - created: "2024-11-29T00:08:44.957765008+05:30" + created: "2024-12-10T17:20:03.319091016-05:00" dependencies: - condition: vector.enabled name: vector @@ -379,7 +402,7 @@ entries: version: 1.2.0 - apiVersion: v2 appVersion: v1.1.0 - created: "2024-11-29T00:08:44.950742995+05:30" + created: "2024-12-10T17:20:03.31333821-05:00" dependencies: - condition: vector.enabled name: vector @@ -402,7 +425,7 @@ entries: version: 1.1.0 - apiVersion: v2 appVersion: v1.0.0 - created: "2024-11-29T00:08:44.945545633+05:30" + created: "2024-12-10T17:20:03.307173302-05:00" dependencies: - condition: vector.enabled name: vector @@ -425,7 +448,7 @@ entries: version: 1.0.0 - apiVersion: v2 appVersion: v0.9.0 - created: "2024-11-29T00:08:44.939512651+05:30" + created: "2024-12-10T17:20:03.300297326-05:00" dependencies: - condition: vector.enabled name: vector @@ -448,7 +471,7 @@ entries: version: 0.9.0 - apiVersion: v2 appVersion: v0.8.1 - created: "2024-11-29T00:08:44.933448679+05:30" + created: "2024-12-10T17:20:03.293880211-05:00" dependencies: - condition: vector.enabled name: vector @@ -471,7 +494,7 @@ entries: version: 0.8.1 - apiVersion: v2 appVersion: v0.8.0 - created: "2024-11-29T00:08:44.926692308+05:30" + created: "2024-12-10T17:20:03.286818525-05:00" dependencies: - condition: vector.enabled name: vector @@ -494,7 +517,7 @@ entries: version: 0.8.0 - apiVersion: v2 appVersion: v0.7.3 - created: "2024-11-29T00:08:44.918803911+05:30" + created: "2024-12-10T17:20:03.279285163-05:00" dependencies: - condition: vector.enabled name: vector @@ -517,7 +540,7 @@ entries: version: 0.7.3 - apiVersion: v2 appVersion: v0.7.2 - created: "2024-11-29T00:08:44.911976979+05:30" + created: "2024-12-10T17:20:03.273058112-05:00" dependencies: - condition: vector.enabled name: vector @@ -540,7 +563,7 @@ entries: version: 0.7.2 - apiVersion: v2 appVersion: v0.7.1 - created: "2024-11-29T00:08:44.904921107+05:30" + created: "2024-12-10T17:20:03.266578799-05:00" dependencies: - condition: vector.enabled name: vector @@ -563,7 +586,7 @@ entries: version: 0.7.1 - apiVersion: v2 appVersion: v0.7.0 - created: "2024-11-29T00:08:44.898821973+05:30" + created: "2024-12-10T17:20:03.260042386-05:00" dependencies: - condition: vector.enabled name: vector @@ -586,7 +609,7 @@ entries: version: 0.7.0 - apiVersion: v2 appVersion: v0.6.2 - created: "2024-11-29T00:08:44.890991713+05:30" + created: "2024-12-10T17:20:03.255083174-05:00" dependencies: - condition: vector.enabled name: vector @@ -609,7 +632,7 @@ entries: version: 0.6.2 - apiVersion: v2 appVersion: v0.6.1 - created: "2024-11-29T00:08:44.883903131+05:30" + created: "2024-12-10T17:20:03.249083724-05:00" dependencies: - condition: vector.enabled name: vector @@ -632,7 +655,7 @@ entries: version: 0.6.1 - apiVersion: v2 appVersion: v0.6.0 - created: "2024-11-29T00:08:44.875874141+05:30" + created: "2024-12-10T17:20:03.240792003-05:00" dependencies: - condition: vector.enabled name: vector @@ -655,7 +678,7 @@ entries: version: 0.6.0 - apiVersion: v2 appVersion: v0.5.1 - created: "2024-11-29T00:08:44.868676404+05:30" + created: "2024-12-10T17:20:03.23396611-05:00" dependencies: - condition: vector.enabled name: vector @@ -678,7 +701,7 @@ entries: version: 0.5.1 - apiVersion: v2 appVersion: v0.5.0 - created: "2024-11-29T00:08:44.861730197+05:30" + created: "2024-12-10T17:20:03.226693392-05:00" dependencies: - condition: vector.enabled name: vector @@ -701,7 +724,7 @@ entries: version: 0.5.0 - apiVersion: v2 appVersion: v0.4.4 - created: "2024-11-29T00:08:44.854653371+05:30" + created: "2024-12-10T17:20:03.218278363-05:00" dependencies: - condition: vector.enabled name: vector @@ -724,7 +747,7 @@ entries: version: 0.4.5 - apiVersion: v2 appVersion: v0.4.3 - created: "2024-11-29T00:08:44.847726652+05:30" + created: "2024-12-10T17:20:03.2118222-05:00" dependencies: - condition: vector.enabled name: vector @@ -747,7 +770,7 @@ entries: version: 0.4.4 - apiVersion: v2 appVersion: v0.4.2 - created: "2024-11-29T00:08:44.84085073+05:30" + created: "2024-12-10T17:20:03.205428913-05:00" dependencies: - condition: vector.enabled name: vector @@ -770,7 +793,7 @@ entries: version: 0.4.3 - apiVersion: v2 appVersion: v0.4.1 - created: "2024-11-29T00:08:44.832709942+05:30" + created: "2024-12-10T17:20:03.198395504-05:00" dependencies: - condition: vector.enabled name: vector @@ -793,7 +816,7 @@ entries: version: 0.4.2 - apiVersion: v2 appVersion: v0.4.0 - created: "2024-11-29T00:08:44.825857924+05:30" + created: "2024-12-10T17:20:03.193193538-05:00" dependencies: - condition: vector.enabled name: vector @@ -816,7 +839,7 @@ entries: version: 0.4.1 - apiVersion: v2 appVersion: v0.4.0 - created: "2024-11-29T00:08:44.818783068+05:30" + created: "2024-12-10T17:20:03.186780127-05:00" dependencies: - condition: vector.enabled name: vector @@ -839,7 +862,7 @@ entries: version: 0.4.0 - apiVersion: v2 appVersion: v0.3.1 - created: "2024-11-29T00:08:44.811794892+05:30" + created: "2024-12-10T17:20:03.1792561-05:00" dependencies: - condition: vector.enabled name: vector @@ -862,7 +885,7 @@ entries: version: 0.3.1 - apiVersion: v2 appVersion: v0.3.0 - created: "2024-11-29T00:08:44.806984292+05:30" + created: "2024-12-10T17:20:03.172715307-05:00" description: Helm chart for Parseable Server digest: ff30739229b727dc637f62fd4481c886a6080ce4556bae10cafe7642ddcfd937 name: parseable @@ -872,7 +895,7 @@ entries: version: 0.3.0 - apiVersion: v2 appVersion: v0.2.2 - created: "2024-11-29T00:08:44.806549892+05:30" + created: "2024-12-10T17:20:03.171609995-05:00" description: Helm chart for Parseable Server digest: 477d0dc2f0c07d4f4c32e105d4bdd70c71113add5c2a75ac5f1cb42aa0276db7 name: parseable @@ -882,7 +905,7 @@ entries: version: 0.2.2 - apiVersion: v2 appVersion: v0.2.1 - created: "2024-11-29T00:08:44.80613879+05:30" + created: "2024-12-10T17:20:03.170694187-05:00" description: Helm chart for Parseable Server digest: 84826fcd1b4c579f301569f43b0309c07e8082bad76f5cdd25f86e86ca2e8192 name: parseable @@ -892,7 +915,7 @@ entries: version: 0.2.1 - apiVersion: v2 appVersion: v0.2.0 - created: "2024-11-29T00:08:44.805774371+05:30" + created: "2024-12-10T17:20:03.169740636-05:00" description: Helm chart for Parseable Server digest: 7a759f7f9809f3935cba685e904c021a0b645f217f4e45b9be185900c467edff name: parseable @@ -902,7 +925,7 @@ entries: version: 0.2.0 - apiVersion: v2 appVersion: v0.1.1 - created: "2024-11-29T00:08:44.805373257+05:30" + created: "2024-12-10T17:20:03.168898001-05:00" description: Helm chart for Parseable Server digest: 37993cf392f662ec7b1fbfc9a2ba00ec906d98723e38f3c91ff1daca97c3d0b3 name: parseable @@ -912,7 +935,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: v0.1.0 - created: "2024-11-29T00:08:44.804981967+05:30" + created: "2024-12-10T17:20:03.168067728-05:00" description: Helm chart for Parseable Server digest: 1d580d072af8d6b1ebcbfee31c2e16c907d08db754780f913b5f0032b403789b name: parseable @@ -922,7 +945,7 @@ entries: version: 0.1.0 - apiVersion: v2 appVersion: v0.0.8 - created: "2024-11-29T00:08:44.80455305+05:30" + created: "2024-12-10T17:20:03.16722528-05:00" description: Helm chart for Parseable Server digest: c805254ffa634f96ecec448bcfff9973339aa9487dd8199b21b17b79a4de9345 name: parseable @@ -932,7 +955,7 @@ entries: version: 0.0.8 - apiVersion: v2 appVersion: v0.0.7 - created: "2024-11-29T00:08:44.804020344+05:30" + created: "2024-12-10T17:20:03.166440181-05:00" description: Helm chart for Parseable Server digest: c591f617ed1fe820bb2c72a4c976a78126f1d1095d552daa07c4700f46c4708a name: parseable @@ -942,7 +965,7 @@ entries: version: 0.0.7 - apiVersion: v2 appVersion: v0.0.6 - created: "2024-11-29T00:08:44.803336035+05:30" + created: "2024-12-10T17:20:03.165555386-05:00" description: Helm chart for Parseable Server digest: f9ae56a6fcd6a59e7bee0436200ddbedeb74ade6073deb435b8fcbaf08dda795 name: parseable @@ -952,7 +975,7 @@ entries: version: 0.0.6 - apiVersion: v2 appVersion: v0.0.5 - created: "2024-11-29T00:08:44.802638218+05:30" + created: "2024-12-10T17:20:03.164686292-05:00" description: Helm chart for Parseable Server digest: 4d6b08a064fba36e16feeb820b77e1e8e60fb6de48dbf7ec8410d03d10c26ad0 name: parseable @@ -962,7 +985,7 @@ entries: version: 0.0.5 - apiVersion: v2 appVersion: v0.0.2 - created: "2024-11-29T00:08:44.800852375+05:30" + created: "2024-12-10T17:20:03.163857934-05:00" description: Helm chart for Parseable Server digest: 38a0a3e4c498afbbcc76ebfcb9cb598fa2ca843a53cc93b3cb4f135b85c10844 name: parseable @@ -972,7 +995,7 @@ entries: version: 0.0.2 - apiVersion: v2 appVersion: v0.0.1 - created: "2024-11-29T00:08:44.800073576+05:30" + created: "2024-12-10T17:20:03.162050391-05:00" description: Helm chart for Parseable Server digest: 1f1142db092b9620ee38bb2294ccbb1c17f807b33bf56da43816af7fe89f301e name: parseable @@ -1001,4 +1024,4 @@ entries: urls: - https://charts.parseable.io/helm-releases/parseable-operator-0.0.1.tgz version: 0.0.1 -generated: "2024-11-29T00:08:44.799027282+05:30" +generated: "2024-12-10T17:20:03.161183816-05:00" From 6d3632a7f5bc9e29e35ae8ad1e165c3a0797195e Mon Sep 17 00:00:00 2001 From: Nikhil Sinha <131262146+nikhilsinhaparseable@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:25:28 -0500 Subject: [PATCH 4/5] fix: convert all number data types to float (#1027) server checks if event has any number (all ints and floats) then update the data type of all numbers to Float64 This is useful to allow users to dynamically switch between an int or a float in their events. --------- Signed-off-by: Nitish Tiwari Co-authored-by: Nitish Tiwari Co-authored-by: Devdutt Shenoi --- src/catalog/column.rs | 8 +++++ src/event/format/json.rs | 4 ++- src/event/format/mod.rs | 22 +++++++++++++ src/handlers/http/ingest.rs | 61 ++++++++++++++++------------------- src/metadata.rs | 14 ++++++++ src/storage/object_storage.rs | 18 +++++++++-- 6 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/catalog/column.rs b/src/catalog/column.rs index d5db2950d..ef4b5858b 100644 --- a/src/catalog/column.rs +++ b/src/catalog/column.rs @@ -66,6 +66,14 @@ impl TypedStatistics { max: max(this.max, other.max), }) } + + // Ints are casted to Float if self is Float and other in Int + (TypedStatistics::Float(this), TypedStatistics::Int(other)) => { + TypedStatistics::Float(Float64Type { + min: this.min.min(other.min as f64), + max: this.max.max(other.max as f64), + }) + } (TypedStatistics::Float(this), TypedStatistics::Float(other)) => { TypedStatistics::Float(Float64Type { min: this.min.min(other.min), diff --git a/src/event/format/json.rs b/src/event/format/json.rs index 487cb58a6..0a137fdb2 100644 --- a/src/event/format/json.rs +++ b/src/event/format/json.rs @@ -185,7 +185,9 @@ fn valid_type(data_type: &DataType, value: &Value) -> bool { DataType::Boolean => value.is_boolean(), DataType::Int8 | DataType::Int16 | DataType::Int32 | DataType::Int64 => value.is_i64(), DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64 => value.is_u64(), - DataType::Float16 | DataType::Float32 | DataType::Float64 => value.is_f64(), + DataType::Float16 | DataType::Float32 => value.is_f64(), + // NOTE: All numbers can be ingested as Float64 + DataType::Float64 => value.is_number(), DataType::Utf8 => value.is_string(), DataType::List(field) => { let data_type = field.data_type(); diff --git a/src/event/format/mod.rs b/src/event/format/mod.rs index e637eb4e6..e0bb00daf 100644 --- a/src/event/format/mod.rs +++ b/src/event/format/mod.rs @@ -204,6 +204,24 @@ pub fn override_timestamp_fields( Arc::new(Schema::new(updated_fields)) } +/// All number fields from inferred schema are forced into Float64 +pub fn override_num_fields_from_schema(schema: Vec>) -> Vec> { + schema + .iter() + .map(|field| { + if field.data_type().is_numeric() { + Arc::new(Field::new( + field.name(), + DataType::Float64, + field.is_nullable(), + )) + } else { + field.clone() + } + }) + .collect::>>() +} + pub fn update_field_type_in_schema( inferred_schema: Arc, existing_schema: Option<&HashMap>>, @@ -212,6 +230,10 @@ pub fn update_field_type_in_schema( ) -> Arc { let mut updated_schema = inferred_schema.clone(); + // All number fields from inferred schema are forced into Float64 + updated_schema = Arc::new(Schema::new(override_num_fields_from_schema( + updated_schema.fields().to_vec(), + ))); if let Some(existing_schema) = existing_schema { let existing_fields = get_existing_fields(inferred_schema.clone(), Some(existing_schema)); let existing_timestamp_fields = get_existing_timestamp_fields(existing_schema); diff --git a/src/handlers/http/ingest.rs b/src/handlers/http/ingest.rs index 305bfecb0..303f591c3 100644 --- a/src/handlers/http/ingest.rs +++ b/src/handlers/http/ingest.rs @@ -277,7 +277,7 @@ mod tests { use std::{collections::HashMap, sync::Arc}; use actix_web::test::TestRequest; - use arrow_array::{ArrayRef, Float64Array, Int64Array, StringArray}; + use arrow_array::{ArrayRef, Float64Array, StringArray}; use arrow_schema::{DataType, Field}; use serde_json::json; @@ -287,16 +287,11 @@ mod tests { }; trait TestExt { - fn as_int64_arr(&self) -> &Int64Array; fn as_float64_arr(&self) -> &Float64Array; fn as_utf8_arr(&self) -> &StringArray; } impl TestExt for ArrayRef { - fn as_int64_arr(&self) -> &Int64Array { - self.as_any().downcast_ref().unwrap() - } - fn as_float64_arr(&self) -> &Float64Array { self.as_any().downcast_ref().unwrap() } @@ -328,8 +323,8 @@ mod tests { assert_eq!(rb.num_rows(), 1); assert_eq!(rb.num_columns(), 6); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from_iter([1]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from_iter([1.0]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), @@ -368,8 +363,8 @@ mod tests { assert_eq!(rb.num_rows(), 1); assert_eq!(rb.num_columns(), 5); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from_iter([1]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from_iter([1.0]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), @@ -386,7 +381,7 @@ mod tests { let schema = fields_to_map( [ - Field::new("a", DataType::Int64, true), + Field::new("a", DataType::Float64, true), Field::new("b", DataType::Utf8, true), Field::new("c", DataType::Float64, true), ] @@ -400,8 +395,8 @@ mod tests { assert_eq!(rb.num_rows(), 1); assert_eq!(rb.num_columns(), 5); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from_iter([1]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from_iter([1.0]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), @@ -418,7 +413,7 @@ mod tests { let schema = fields_to_map( [ - Field::new("a", DataType::Int64, true), + Field::new("a", DataType::Float64, true), Field::new("b", DataType::Utf8, true), Field::new("c", DataType::Float64, true), ] @@ -488,21 +483,21 @@ mod tests { let schema = rb.schema(); let fields = &schema.fields; - assert_eq!(&*fields[1], &Field::new("a", DataType::Int64, true)); + assert_eq!(&*fields[1], &Field::new("a", DataType::Float64, true)); assert_eq!(&*fields[2], &Field::new("b", DataType::Utf8, true)); - assert_eq!(&*fields[3], &Field::new("c", DataType::Int64, true)); + assert_eq!(&*fields[3], &Field::new("c", DataType::Float64, true)); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from(vec![None, Some(1), Some(1)]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from(vec![None, Some(1.0), Some(1.0)]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), &StringArray::from(vec![Some("hello"), Some("hello"), Some("hello"),]) ); assert_eq!( - rb.column_by_name("c").unwrap().as_int64_arr(), - &Int64Array::from(vec![None, Some(1), None]) + rb.column_by_name("c").unwrap().as_float64_arr(), + &Float64Array::from(vec![None, Some(1.0), None]) ); } @@ -533,8 +528,8 @@ mod tests { assert_eq!(rb.num_rows(), 3); assert_eq!(rb.num_columns(), 6); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from(vec![None, Some(1), Some(1)]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from(vec![None, Some(1.0), Some(1.0)]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), @@ -568,7 +563,7 @@ mod tests { let schema = fields_to_map( [ - Field::new("a", DataType::Int64, true), + Field::new("a", DataType::Float64, true), Field::new("b", DataType::Utf8, true), Field::new("c", DataType::Float64, true), ] @@ -581,8 +576,8 @@ mod tests { assert_eq!(rb.num_rows(), 3); assert_eq!(rb.num_columns(), 6); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from(vec![None, Some(1), Some(1)]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from(vec![None, Some(1.0), Some(1.0)]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), @@ -608,7 +603,7 @@ mod tests { "c": 1 }, { - "a": 1, + "a": "1", "b": "hello", "c": null }, @@ -618,7 +613,7 @@ mod tests { let schema = fields_to_map( [ - Field::new("a", DataType::Int64, true), + Field::new("a", DataType::Float64, true), Field::new("b", DataType::Utf8, true), Field::new("c", DataType::Float64, true), ] @@ -658,8 +653,8 @@ mod tests { assert_eq!(rb.num_rows(), 4); assert_eq!(rb.num_columns(), 7); assert_eq!( - rb.column_by_name("a").unwrap().as_int64_arr(), - &Int64Array::from(vec![Some(1), Some(1), Some(1), Some(1)]) + rb.column_by_name("a").unwrap().as_float64_arr(), + &Float64Array::from(vec![Some(1.0), Some(1.0), Some(1.0), Some(1.0)]) ); assert_eq!( rb.column_by_name("b").unwrap().as_utf8_arr(), @@ -672,13 +667,13 @@ mod tests { ); assert_eq!( - rb.column_by_name("c_a").unwrap().as_int64_arr(), - &Int64Array::from(vec![None, None, Some(1), Some(1)]) + rb.column_by_name("c_a").unwrap().as_float64_arr(), + &Float64Array::from(vec![None, None, Some(1.0), Some(1.0)]) ); assert_eq!( - rb.column_by_name("c_b").unwrap().as_int64_arr(), - &Int64Array::from(vec![None, None, None, Some(2)]) + rb.column_by_name("c_b").unwrap().as_float64_arr(), + &Float64Array::from(vec![None, None, None, Some(2.0)]) ); } } diff --git a/src/metadata.rs b/src/metadata.rs index f768a4e88..5447ea796 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -164,6 +164,20 @@ impl StreamInfo { Ok(Arc::new(schema)) } + /// update the schema in the metadata + pub fn set_schema( + &self, + stream_name: &str, + schema: HashMap>, + ) -> Result<(), MetadataError> { + let mut map = self.write().expect(LOCK_EXPECT); + map.get_mut(stream_name) + .ok_or(MetadataError::StreamMetaNotFound(stream_name.to_string())) + .map(|metadata| { + metadata.schema = schema; + }) + } + pub fn set_alert(&self, stream_name: &str, alerts: Alerts) -> Result<(), MetadataError> { let mut map = self.write().expect(LOCK_EXPECT); map.get_mut(stream_name) diff --git a/src/storage/object_storage.rs b/src/storage/object_storage.rs index e70c326bd..e9ee32f18 100644 --- a/src/storage/object_storage.rs +++ b/src/storage/object_storage.rs @@ -25,6 +25,7 @@ use super::{ SCHEMA_FILE_NAME, STREAM_METADATA_FILE_NAME, STREAM_ROOT_DIRECTORY, }; +use crate::event::format::override_num_fields_from_schema; use crate::handlers::http::modal::ingest_server::INGESTOR_META; use crate::handlers::http::users::{DASHBOARDS_DIR, FILTER_DIR, USERS_ROOT_DIR}; use crate::metrics::{EVENTS_STORAGE_SIZE_DATE, LIFETIME_EVENTS_STORAGE_SIZE}; @@ -40,7 +41,7 @@ use crate::{ }; use actix_web_prometheus::PrometheusMetrics; -use arrow_schema::Schema; +use arrow_schema::{Field, Schema}; use async_trait::async_trait; use bytes::Bytes; use chrono::Local; @@ -667,8 +668,21 @@ pub async fn commit_schema_to_storage( schema: Schema, ) -> Result<(), ObjectStorageError> { let storage = CONFIG.storage().get_object_store(); - let stream_schema = storage.get_schema(stream_name).await?; + let mut stream_schema = storage.get_schema(stream_name).await?; + // override the data type of all numeric fields to Float64 + //if data type is not Float64 already + stream_schema = Schema::new(override_num_fields_from_schema( + stream_schema.fields().iter().cloned().collect(), + )); let new_schema = Schema::try_merge(vec![schema, stream_schema]).unwrap(); + + //update the merged schema in the metadata and storage + let schema_map: HashMap> = new_schema + .fields() + .iter() + .map(|field| (field.name().clone(), Arc::clone(field))) + .collect(); + let _ = STREAM_INFO.set_schema(stream_name, schema_map); storage.put_schema(stream_name, &new_schema).await } From 80a3ff4afe4299975fd16af2b155129973059d81 Mon Sep 17 00:00:00 2001 From: AdheipSingh <34169002+AdheipSingh@users.noreply.github.com> Date: Sat, 14 Dec 2024 13:31:34 +0530 Subject: [PATCH 5/5] helm fixes for distributed mode (#1031) * fixes for distributed mode * add placeholder for fluent bit values --- helm/templates/ingestor-statefulset.yaml | 10 ++++++++-- helm/templates/querier-statefulset.yaml | 16 +++++++++------- helm/values.yaml | 5 +++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/helm/templates/ingestor-statefulset.yaml b/helm/templates/ingestor-statefulset.yaml index 91ca793c0..50324cb7e 100644 --- a/helm/templates/ingestor-statefulset.yaml +++ b/helm/templates/ingestor-statefulset.yaml @@ -54,7 +54,9 @@ spec: apiVersion: v1 fieldPath: metadata.name {{- end }} - {{- range $secret := .Values.parseable.s3ModeSecret }} + + {{- if and .Values.parseable.s3ModeSecret .Values.parseable.s3ModeSecret.enabled }} + {{- range $secret := .Values.parseable.s3ModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -65,7 +67,10 @@ spec: key: {{ $key }} {{- end }} {{- end }} - {{- range $secret := .Values.parseable.blobModeSecret }} + {{- end }} + + {{- if and .Values.parseable.blobModeSecret .Values.parseable.blobModeSecret.enabled }} + {{- range $secret := .Values.parseable.blobModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -76,6 +81,7 @@ spec: key: {{ $key }} {{- end }} {{- end }} + {{- end }} - name: P_MODE value: "ingest" ports: diff --git a/helm/templates/querier-statefulset.yaml b/helm/templates/querier-statefulset.yaml index d48ce1035..98845faaa 100644 --- a/helm/templates/querier-statefulset.yaml +++ b/helm/templates/querier-statefulset.yaml @@ -52,11 +52,15 @@ spec: fieldRef: apiVersion: v1 fieldPath: metadata.name + - name: P_MODE + value: "query" {{- range $key, $value := .Values.parseable.env }} - name: {{ $key }} value: {{ tpl $value $ | quote }} {{- end }} - {{- range $secret := .Values.parseable.s3ModeSecret }} + + {{- if and .Values.parseable.s3ModeSecret .Values.parseable.s3ModeSecret.enabled }} + {{- range $secret := .Values.parseable.s3ModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -67,13 +71,10 @@ spec: key: {{ $key }} {{- end }} {{- end }} - - name: P_MODE - value: "query" - {{- if .Values.parseable.persistence.querier.enabled }} - - name: P_HOT_TIER_DIR - value: "/parseable/hot-tier" {{- end }} - {{- range $secret := .Values.parseable.blobModeSecret }} + + {{- if and .Values.parseable.blobModeSecret .Values.parseable.blobModeSecret.enabled }} + {{- range $secret := .Values.parseable.blobModeSecret.secrets }} {{- range $key := $secret.keys }} {{- $envPrefix := $secret.prefix | default "" | upper }} {{- $envKey := $key | upper | replace "." "_" | replace "-" "_" }} @@ -84,6 +85,7 @@ spec: key: {{ $key }} {{- end }} {{- end }} + {{- end }} ports: - containerPort: 8000 {{- with .Values.readinessProbe }} diff --git a/helm/values.yaml b/helm/values.yaml index 83aa6d251..49f6759ad 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -297,6 +297,7 @@ vector: fluent-bit: enabled: true kind: DaemonSet + serverHost: parseable.parseable.svc.cluster.local replicaCount: 1 image: repository: parseable/fluent-bit @@ -382,7 +383,7 @@ fluent-bit: [OUTPUT] Name parseable Match kube.* - Server_Host parseable.parseable.svc.cluster.local + Server_Host {{ .Values.serverHost }} Username admin Password admin Server_Port 80 @@ -392,7 +393,7 @@ fluent-bit: [OUTPUT] Name parseable Match k8s_events - Server_Host parseable.parseable.svc.cluster.local + Server_Host {{ .Values.serverHost }} Server_Port 80 Username admin Password admin