Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a CLI for uploading and downloading blocks #760

Merged
merged 11 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .promu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ build:
path: ./tools/scaler
- name: tools/load-generator
path: ./tools/load-generator
- name: tools/block-sync
path: ./tools/block-sync
flags: -a -tags netgo
crossbuild:
platforms:
Expand Down
47 changes: 46 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,50 @@ require (
sigs.k8s.io/kind v0.24.0
)

require (
cloud.google.com/go v0.115.1 // indirect
cloud.google.com/go/iam v1.2.0 // indirect
cloud.google.com/go/storage v1.43.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible // indirect
github.com/aws/aws-sdk-go-v2 v1.16.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.15.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.11.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.1 // indirect
github.com/aws/smithy-go v1.11.1 // indirect
github.com/baidubce/bce-sdk-go v0.9.111 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/efficientgo/core v1.0.0-rc.0.0.20221201130417-ba593f67d2a4 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.3+incompatible // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.72 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/ncw/swift v1.0.53 // indirect
github.com/oracle/oci-go-sdk/v65 v65.41.1 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/tencentyun/cos-go-sdk-v5 v0.7.40 // indirect
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

require (
cloud.google.com/go/auth v0.9.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
Expand All @@ -42,7 +86,7 @@ require (
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-kit/log v0.2.1
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand Down Expand Up @@ -102,6 +146,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/thanos-io/objstore v0.0.0-20240913165201-fd105025a2e5
github.com/x448/float16 v0.8.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
Expand Down
112 changes: 112 additions & 0 deletions go.sum

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions prombench/manifests/prombench/benchmark/2a_prometheus-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
name: s3-secret
namespace: prombench-{{ .PR_NUMBER }} # Replace with your actual namespace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you put the Secret in the namespace for the PR, but I see the same bucket being used across many runs.

type: Opaque
stringData:
object-config.yml: |
type: S3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to be configured differently for each installation - s3 is good for local installs using Minio, but when run at Google Cloud it needs to say gcs.
I suggest you take this Secret out of the PR and instead put instructions in the docs, e.g. here, for the person who installs Prombench to set it up.

config:
bucket: {{ .BUCKET_KEY }}
endpoint: minio:9000
access_key: {{ .ACCESS_KEY }}
secret_key: {{ .SECRET_KEY }}
insecure: true

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ spec:
runAsUser: 0
initContainers:
- name: prometheus-builder
image: docker.io/prominfra/prometheus-builder:master
image: kushalshukla/builder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guessing this one is built from the change in this PR.

imagePullPolicy: Always
env:
- name: PR_NUMBER
Expand All @@ -44,10 +44,35 @@ spec:
- name: GITHUB_ORG
value: "{{ .GITHUB_ORG }}"
- name: GITHUB_REPO
value: "{{ .GITHUB_REPO }}"
value: "{{ .GITHUB_REPO }}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious change

volumeMounts:
- name: prometheus-executable
mountPath: /prometheus-builder
- name: key
mountPath: /config
- name: data-downloader
image: kushalshukla/writer
imagePullPolicy: Always
env:
- name: ACCESS_KEY
value: "{{ .ACCESS_KEY }}"
- name: SECRET_KEY
value: "{{ .SECRET_KEY }}"
- name: BUCKET_KEY
value: "{{ .BUCKET_KEY }}"
volumeMounts:
- name: instance-ssd
mountPath: /data
- name: s3-config
mountPath: /config
- name: key
mountPath: /key
args: [
"download",
"--tsdb-path=/data",
"--objstore.config-file=/config/object-config.yml",
"--key=/key/key.yml"
]
containers:
- name: prometheus
image: quay.io/prometheus/busybox:latest
Expand Down Expand Up @@ -88,6 +113,11 @@ spec:
path: /mnt/disks/ssd0 #gke ssds
- name: prometheus-executable
emptyDir: {}
- name: s3-config # Define the Secret volume
secret:
secretName: s3-secret
- name: key
emptyDir: {}
terminationGracePeriodSeconds: 300
nodeSelector:
node-name: prometheus-{{ .PR_NUMBER }}
Expand Down Expand Up @@ -144,6 +174,46 @@ spec:
- prometheus
securityContext:
runAsUser: 0
initContainers:
- name: git-fetcher
image: alpine/git
command:
- /bin/sh
- -c
- |
apk add --no-cache bash && \
git clone --depth 1 https://github.com/{{ .GITHUB_ORG }}/{{ .GITHUB_REPO }}.git /repo1 && \
cd /repo1 && \
git fetch origin pull/{{ .PR_NUMBER }}/head:pr-branch && \
git checkout pr-branch && \
cp key.yml /config/key.yml && \
rm -rf /repo1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This multi-line command is getting unwieldly. Pull it out to a .sh file.

volumeMounts:
- name: key
mountPath: /config
- name: data-downloader
image: kushalshukla/writer
imagePullPolicy: Always
env:
- name: ACCESS_KEY
value: "{{ .ACCESS_KEY }}"
- name: SECRET_KEY
value: "{{ .SECRET_KEY }}"
- name: BUCKET_KEY
value: "{{ .BUCKET_KEY }}"
volumeMounts:
- name: instance-ssd
mountPath: /data
- name: s3-config
mountPath: /config
- name: key
mountPath: /key
args: [
"download",
"--tsdb-path=/data",
"--objstore.config-file=/config/object-config.yml",
"--key=/key/key.yml"
]
containers:
- name: prometheus
image: quay.io/prometheus/prometheus:{{ .RELEASE }}
Expand Down Expand Up @@ -172,6 +242,14 @@ spec:
# /mnt is where GKE keeps it's SSD
# don't change this if you want Prometheus to take advantage of these local SSDs
path: /mnt/disks/ssd0
- name: config
hostPath:
path: /object-config
- name: s3-config # Define the Secret volume
secret:
secretName: s3-secret
- name: key
emptyDir: {}
terminationGracePeriodSeconds: 300
nodeSelector:
node-name: prometheus-{{ .PR_NUMBER }}
Expand Down
10 changes: 10 additions & 0 deletions tools/block-sync/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM alpine:latest
LABEL maintainer="The Prometheus Authors <[email protected]>"

RUN apk --no-cache add libc6-compat

COPY ./block-sync /bin/block-sync

RUN chmod +x /bin/block-sync

ENTRYPOINT ["/bin/block-sync"]
53 changes: 53 additions & 0 deletions tools/block-sync/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

# block-sync - TSDB Data Synchronization Tool


The `block-sync` command is a CLI tool designed to synchronize TSDB data with an object storage system.

## Table of Contents

1. [Upload](#upload)
2. [Download](#download)

## Command Flags

- ``` -h , --help```:Displays context-sensitive help
- ``` - tsdb-path```: Path for The TSDB data in prometheus
- ```- objstore.config-file```: Path for The Config file
- ```- Key```: Path for the Key where to store block data , i.e a Directory.

## Upload

The `upload` command allows you to upload TSDB data from a specified path to an object storage bucket. This command is essential for backing up your TSDB data or migrating it to an object storage solution for future use.

### Usage

```bash
./block-sync upload --tsdb-path=<path-to-tsdb> --objstore.config-file=<path-to-config> --key=<object-key>


```
## Download

The `download` command allows you to retrieve TSDB data from an object storage bucket to a specified local path. This command is essential for restoring your TSDB data or retrieving it for local analysis and processing.

### Usage

```bash
./block-sync download --tsdb-path=<path-to-tsdb> --objstore.config-file=<path-to-config> --key=<object-key>
```
## Config File

The configuration file is essential for connecting to your object storage solution. Below are basic templates for different object storage systems.

```yaml
type: s3, GCS , AZURE , etc.
config:
bucket: your-bucket-name
endpoint: https://your-endpoint
access_key: your-access-key
secret_key: your-secret-key
insecure: false # Set to true if using HTTP instead of HTTPS
```
You can customize the config file , follow this link [Storage.md](https://thanos.io/tip/thanos/storage.md/)

103 changes: 103 additions & 0 deletions tools/block-sync/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2024 The Prometheus Authors
// 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 main

import (
"context"
"flag"
"fmt"
"log/slog"
"os"
)

func main() {
var (
tsdbPath string
objectConfig string
objectKey string
)
uploadCmd := flag.NewFlagSet("upload", flag.ExitOnError)
downloadCmd := flag.NewFlagSet("download", flag.ExitOnError)

uploadCmd.StringVar(&tsdbPath, "tsdb-path", "", "Uploading data to objstore")
uploadCmd.StringVar(&objectConfig, "objstore.config-file", "", "Path for The Config file")
uploadCmd.StringVar(&objectKey, "key", "", "Path for the Key where to store block data")

downloadCmd.StringVar(&tsdbPath, "tsdb-path", "", "Downloading data to objstore")
downloadCmd.StringVar(&objectConfig, "objstore.config-file", "", "Path for The Config file")
downloadCmd.StringVar(&objectKey, "key", "", "Path from the Key where to download the block data")
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Println(" upload Uploads data to the object store")
fmt.Println(" download Downloads data from the object store")
fmt.Println("Flags:")
fmt.Println(" --tsdb-path Path to TSDB data")
fmt.Println(" --objstore.config-file Path to the object store config file")
fmt.Println(" --key Key path for storing or downloading data")
fmt.Println()
fmt.Println("Use 'block-sync [command] --help' for more information about a command.")
}

if len(os.Args) < 2 {
logger.Error("Expected 'upload' or 'download' subcommands")
flag.Usage()
os.Exit(1)
}

switch os.Args[1] {
case "upload":
if err := uploadCmd.Parse(os.Args[2:]); err != nil {
fmt.Println("Error parsing upload command:", err)
os.Exit(1)
}
case "download":
if err := downloadCmd.Parse(os.Args[2:]); err != nil {
fmt.Println("Error parsing download command:", err)
os.Exit(1)
}
default:
logger.Error("Expected 'upload' or 'download' subcommands")
flag.Usage()
os.Exit(1)
}

if tsdbPath == "" || objectConfig == "" || objectKey == "" {
fmt.Println("error: all flags --tsdb-path, --objstore.config-file, and --key are required.")
os.Exit(1)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
store, err := newStore(tsdbPath, objectConfig, objectKey, logger)
if err != nil {
logger.Error("Failed to create store", "error", err)
os.Exit(1)
}

switch os.Args[1] {
case "upload":
err = store.upload(ctx)
if err != nil {
logger.Error("Failed to upload data", "Error", err)
os.Exit(1)
}
case "download":
err = store.download(ctx)
if err != nil {
logger.Error("Failed to download data", "error", err)
os.Exit(1)
}
}
}
Loading