From 4943042f78552da1212b89ca963c57aa5fb8d26c Mon Sep 17 00:00:00 2001 From: Dudi Dolev Date: Sun, 19 Jan 2025 18:20:10 +0200 Subject: [PATCH 1/2] Fix: Exporter not initiating connection to mongo when Unauthorized --- Dockerfile | 89 +++++++++++++++++-- Makefile | 7 +- exporter/currentop_collector.go | 8 ++ exporter/dbstats_collector.go | 7 ++ exporter/diagnostic_data_collector.go | 13 +++ ...feature_compatibility_version_collector.go | 7 ++ exporter/replset_status_collector.go | 6 ++ exporter/top_collector.go | 7 ++ exporter/topology_info.go | 13 +++ exporter/v1_compatibility.go | 19 ++++ internal/util/util.go | 21 +++++ 11 files changed, 188 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3ed6f8ffb..c84f1cbb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,84 @@ -FROM alpine AS builder -RUN apk add --no-cache ca-certificates +# #FROM alpine AS builder +# FROM docker.io/bitnami/minideb:bookworm +# #RUN apk add --no-cache ca-certificates +# RUN install_packages ca-certificates curl procps + +# RUN apt-get autoremove --purge -y curl && \ +# apt-get update && apt-get upgrade -y && \ +# apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# #FROM scratch AS final +# USER 1001:1001 +# #COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +# COPY ./mongodb_exporter /bin/mongodb_exporter +# #RUN chmod g+rwX /bin/mongodb_exporter +# RUN chmod 777 /bin/mongodb_exporter + +# EXPOSE 9216 +# ENTRYPOINT ["/mongodb_exporter"] + + + +# Copyright Broadcom, Inc. All Rights Reserved. +# SPDX-License-Identifier: APACHE-2.0 + +FROM docker.io/bitnami/minideb:bookworm + +ARG DOWNLOADS_URL="downloads.bitnami.com/files/stacksmith" +ARG TARGETARCH + +LABEL com.vmware.cp.artifact.flavor="sha256:c50c90cfd9d12b445b011e6ad529f1ad3daea45c26d20b00732fae3cd71f6a83" \ + org.opencontainers.image.base.name="docker.io/bitnami/minideb:bookworm" \ + org.opencontainers.image.created="2025-01-17T03:23:22Z" \ + org.opencontainers.image.description="Application packaged by Broadcom, Inc." \ + org.opencontainers.image.documentation="https://github.com/bitnami/containers/tree/main/bitnami/mongodb-exporter/README.md" \ + org.opencontainers.image.licenses="Apache-2.0" \ + org.opencontainers.image.ref.name="0.43.1-debian-12-r2" \ + org.opencontainers.image.source="https://github.com/bitnami/containers/tree/main/bitnami/mongodb-exporter" \ + org.opencontainers.image.title="mongodb-exporter" \ + org.opencontainers.image.vendor="Broadcom, Inc." \ + org.opencontainers.image.version="0.43.1" + +ENV HOME="/" \ + OS_ARCH="${TARGETARCH:-amd64}" \ + OS_FLAVOUR="debian-12" \ + OS_NAME="linux" + +# COPY prebuildfs / +# SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] +# Install required system packages and dependencies +RUN install_packages ca-certificates curl procps +# RUN mkdir -p /tmp/bitnami/pkg/cache/ ; cd /tmp/bitnami/pkg/cache/ ; \ +# COMPONENTS=( \ +# "mongodb-exporter-0.43.1-1-linux-${OS_ARCH}-debian-12" \ +# ) ; \ +# for COMPONENT in "${COMPONENTS[@]}"; do \ +# if [ ! -f "${COMPONENT}.tar.gz" ]; then \ +# curl -SsLf "https://${DOWNLOADS_URL}/${COMPONENT}.tar.gz" -O ; \ +# curl -SsLf "https://${DOWNLOADS_URL}/${COMPONENT}.tar.gz.sha256" -O ; \ +# fi ; \ +# sha256sum -c "${COMPONENT}.tar.gz.sha256" ; \ +# tar -zxf "${COMPONENT}.tar.gz" -C /opt/bitnami --strip-components=2 --no-same-owner --wildcards '*/files' ; \ +# rm -rf "${COMPONENT}".tar.gz{,.sha256} ; \ +# done + +RUN mkdir -p /opt/bitnami/mongodb-exporter/bin + +COPY ./mongodb_exporter /opt/bitnami/mongodb-exporter/bin/mongodb_exporter + +RUN apt-get autoremove --purge -y curl && \ + apt-get update && apt-get upgrade -y && \ + apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt/archives +RUN chmod g+rwX /opt/bitnami +RUN find / -perm /6000 -type f -exec chmod a-s {} \; || true +RUN ln -sf /opt/bitnami/mongodb-exporter/bin/mongodb_exporter /bin/mongodb_exporter + +ENV APP_VERSION="0.43.1" \ + BITNAMI_APP_NAME="mongodb-exporter" \ + PATH="/opt/bitnami/mongodb-exporter/bin:$PATH" -FROM scratch AS final -USER 65535:65535 -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY ./mongodb_exporter / EXPOSE 9216 -ENTRYPOINT ["/mongodb_exporter"] \ No newline at end of file + +WORKDIR /opt/bitnami/mongodb-exporter +USER 1001 +ENTRYPOINT [ "mongodb_exporter" ] \ No newline at end of file diff --git a/Makefile b/Makefile index a2eed500b..515ad8fef 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ GO_TEST_PATH ?= ./... GO_TEST_EXTRA ?= GO_TEST_COVER_PROFILE ?= cover.out GO_TEST_CODECOV ?= +GOARCH = amd64 +CGO_ENABLED = 0 +GOOS = linux BUILD ?= $(shell date +%FT%T%z) GOVERSION ?= $(shell go version | cut -d " " -f3) @@ -68,10 +71,10 @@ env: @echo $(TEST_ENV) | tr ' ' '\n' >.env init: ## Install linters. - cd tools && go generate -x -tags=tools + cd tools && GOARCH=amd64 CGO_ENABLED=0 GOOS=linux go generate -x -tags=tools build: ## Compile using plain go build - go build -ldflags="$(GO_BUILD_LDFLAGS)" -o $(PMM_RELEASE_PATH)/mongodb_exporter + GOARCH=amd64 CGO_ENABLED=0 GOOS=linux go build -ldflags="$(GO_BUILD_LDFLAGS)" -o $(PMM_RELEASE_PATH)/mongodb_exporter release: ## Build the binaries using goreleaser docker run --rm --privileged \ diff --git a/exporter/currentop_collector.go b/exporter/currentop_collector.go index b93316e96..2a2b03336 100644 --- a/exporter/currentop_collector.go +++ b/exporter/currentop_collector.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "os" "strconv" "time" @@ -90,6 +91,13 @@ func (d *currentopCollector) collect(ch chan<- prometheus.Metric) { var r primitive.M if err := res.Decode(&r); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + logger.Errorf("unauthorized to run currtop: %s", err) + os.Exit(1) + } + } + logger.Errorf("Failed to decode currentOp response: %s", err) ch <- prometheus.NewInvalidMetric(prometheus.NewInvalidDesc(err), err) return diff --git a/exporter/dbstats_collector.go b/exporter/dbstats_collector.go index b43141456..846e5616e 100644 --- a/exporter/dbstats_collector.go +++ b/exporter/dbstats_collector.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "os" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -84,6 +85,12 @@ func (d *dbstatsCollector) collect(ch chan<- prometheus.Metric) { r := client.Database(db).RunCommand(d.ctx, cmd) err := r.Decode(&dbStats) if err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + logger.Errorf("unauthorized to run replSetGetStatus: %s", err) + os.Exit(1) + } + } logger.Errorf("Failed to get $dbstats for database %s: %s", db, err) continue diff --git a/exporter/diagnostic_data_collector.go b/exporter/diagnostic_data_collector.go index 3fb322bdc..750eff78c 100644 --- a/exporter/diagnostic_data_collector.go +++ b/exporter/diagnostic_data_collector.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "os" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" @@ -94,6 +95,12 @@ func (d *diagnosticDataCollector) collect(ch chan<- prometheus.Metric) { } } else { if err := res.Decode(&m); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + logger.Errorf("unauthorized to run getDiagnosticData: %s", err) + os.Exit(1) + } + } logger.Errorf("cannot run getDiagnosticData: %s", err) return } @@ -163,6 +170,12 @@ func (d *diagnosticDataCollector) getSecurityMetricFromLineOptions(client *mongo return nil, errors.Wrap(resCmdLineOptions.Err(), "cannot execute getCmdLineOpts command") } if err := resCmdLineOptions.Decode(&cmdLineOpionsBson); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + errors.New("unauthorized to run getCmdLineOpts") + os.Exit(1) + } + } return nil, errors.Wrap(err, "cannot parse response of the getCmdLineOpts command") } diff --git a/exporter/feature_compatibility_version_collector.go b/exporter/feature_compatibility_version_collector.go index 871544c6c..45550f71f 100644 --- a/exporter/feature_compatibility_version_collector.go +++ b/exporter/feature_compatibility_version_collector.go @@ -18,6 +18,7 @@ package exporter import ( "context" "fmt" + "os" "strconv" "github.com/prometheus/client_golang/prometheus" @@ -61,6 +62,12 @@ func (d *featureCompatibilityCollector) collect(ch chan<- prometheus.Metric) { if err := res.Decode(&m); err != nil { d.base.logger.Errorf("Failed to decode featureCompatibilityVersion: %v", err) ch <- prometheus.NewInvalidMetric(prometheus.NewInvalidDesc(err), err) + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + d.base.logger.Errorf("Failed to decode featureCompatibilityVersion: %v", err) + os.Exit(1) + } + } return } diff --git a/exporter/replset_status_collector.go b/exporter/replset_status_collector.go index afbbd6d39..988133ce1 100644 --- a/exporter/replset_status_collector.go +++ b/exporter/replset_status_collector.go @@ -17,6 +17,7 @@ package exporter import ( "context" + "os" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -27,6 +28,7 @@ import ( const ( replicationNotEnabled = 76 replicationNotYetInitialized = 94 + Unauthorized = 13 ) type replSetGetStatusCollector struct { @@ -72,6 +74,10 @@ func (d *replSetGetStatusCollector) collect(ch chan<- prometheus.Metric) { if e.Code == replicationNotYetInitialized || e.Code == replicationNotEnabled { return } + if e.Code == Unauthorized { + logger.Errorf("unauthorized to run replSetGetStatus: %s", err) + os.Exit(1) + } } logger.Errorf("cannot get replSetGetStatus: %s", err) diff --git a/exporter/top_collector.go b/exporter/top_collector.go index 562a28ec6..0b6b7ae99 100644 --- a/exporter/top_collector.go +++ b/exporter/top_collector.go @@ -18,6 +18,7 @@ package exporter import ( "context" "fmt" + "os" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -66,6 +67,12 @@ func (d *topCollector) collect(ch chan<- prometheus.Metric) { var m primitive.M if err := res.Decode(&m); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + logger.Errorf("unauthorized to run top command") + os.Exit(1) + } + } ch <- prometheus.NewInvalidMetric(prometheus.NewInvalidDesc(err), err) return } diff --git a/exporter/topology_info.go b/exporter/topology_info.go index 08d05d841..e18c3fe03 100644 --- a/exporter/topology_info.go +++ b/exporter/topology_info.go @@ -18,6 +18,7 @@ package exporter import ( "context" "fmt" + "os" "sync" "github.com/pkg/errors" @@ -143,6 +144,12 @@ func getNodeType(ctx context.Context, client *mongo.Client) (mongoDBNodeType, er } md := proto.MasterDoc{} if err := client.Database("admin").RunCommand(ctx, primitive.M{"isMaster": 1}).Decode(&md); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + errors.New("unauthorized to getisMaster") + os.Exit(1) + } + } return "", err } @@ -171,6 +178,12 @@ func getClusterRole(ctx context.Context, client *mongo.Client) (string, error) { } if err := res.Decode(&cmdOpts); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + errors.New("unauthorized to getCmdLineOpts") + os.Exit(1) + } + } return "", errors.Wrap(err, "cannot decode getCmdLineOpts response") } diff --git a/exporter/v1_compatibility.go b/exporter/v1_compatibility.go index 3a3d49895..202a5aafa 100644 --- a/exporter/v1_compatibility.go +++ b/exporter/v1_compatibility.go @@ -19,6 +19,7 @@ import ( "context" "fmt" "math" + "os" "strings" "time" @@ -835,6 +836,12 @@ func retrieveMongoDBBuildInfo(ctx context.Context, client *mongo.Client, l *logr var buildInfoDoc bson.M err := res.Decode(&buildInfoDoc) if err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + errors.Wrap(err, "unauthorized to run command buildInfo") + os.Exit(1) + } + } return buildInfo{}, errors.Wrap(err, "Failed to run buildInfo command") } @@ -1176,6 +1183,12 @@ func chunksBalancerRunning(ctx context.Context, client *mongo.Client) (prometheu res := client.Database("admin").RunCommand(ctx, cmd) if err := res.Decode(&m); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + errors.Wrap(err, "unauthorized to run command balancerStatus") + os.Exit(1) + } + } return nil, err } @@ -1201,6 +1214,12 @@ func balancerEnabled(ctx context.Context, client *mongo.Client) (prometheus.Metr cmd := bson.D{{Key: "balancerStatus", Value: "1"}} err := client.Database("admin").RunCommand(ctx, cmd).Decode(&bs) if err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == Unauthorized { + errors.Wrap(err, "unauthorized to run command balancerStatus") + os.Exit(1) + } + } return nil, err } if bs.Mode == "full" { diff --git a/internal/util/util.go b/internal/util/util.go index bebf5ee18..c05c60a9e 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -17,6 +17,8 @@ package util import ( "context" + "fmt" + "os" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -29,6 +31,7 @@ const ( ErrNotYetInitialized = int32(94) ErrNoReplicationEnabled = int32(76) ErrNotPrimaryOrSecondary = int32(13436) + ErrNotUnauthorized = int32(13) ) // MyState returns the replica set and the instance's state if available. @@ -37,6 +40,12 @@ func MyState(ctx context.Context, client *mongo.Client) (string, int, error) { err := client.Database("admin").RunCommand(ctx, bson.M{"replSetGetStatus": 1}).Decode(&status) if err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == ErrNotUnauthorized { + fmt.Fprintf(os.Stderr, "unauthorized to run command replSetGetStatus: %v\n", err) + os.Exit(1) + } + } return "", 0, err } @@ -48,6 +57,12 @@ func MyRole(ctx context.Context, client *mongo.Client) (*proto.HelloResponse, er var role proto.HelloResponse err := client.Database("admin").RunCommand(ctx, bson.M{"isMaster": 1}).Decode(&role) if err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == ErrNotUnauthorized { + fmt.Fprintf(os.Stderr, "unauthorized to run command isMaster: %v\n", err) + os.Exit(1) + } + } return nil, err } @@ -57,6 +72,12 @@ func MyRole(ctx context.Context, client *mongo.Client) (*proto.HelloResponse, er func ReplicasetConfig(ctx context.Context, client *mongo.Client) (*proto.ReplicasetConfig, error) { var rs proto.ReplicasetConfig if err := client.Database("admin").RunCommand(ctx, bson.M{"replSetGetConfig": 1}).Decode(&rs); err != nil { + if e, ok := err.(mongo.CommandError); ok { + if e.Code == ErrNotUnauthorized { + fmt.Fprintf(os.Stderr, "unauthorized to run command replSetGetConfig: %v\n", err) + os.Exit(1) + } + } return nil, err } From ec904955685447b692b311e183d5bd7a1e794119 Mon Sep 17 00:00:00 2001 From: Dudi Dolev Date: Sun, 26 Jan 2025 14:34:42 +0200 Subject: [PATCH 2/2] Revert the change on Dockerfile and Makefile --- Dockerfile | 89 +++++------------------------------------------------- Makefile | 7 ++--- 2 files changed, 9 insertions(+), 87 deletions(-) diff --git a/Dockerfile b/Dockerfile index c84f1cbb9..3ed6f8ffb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,84 +1,9 @@ -# #FROM alpine AS builder -# FROM docker.io/bitnami/minideb:bookworm -# #RUN apk add --no-cache ca-certificates -# RUN install_packages ca-certificates curl procps - -# RUN apt-get autoremove --purge -y curl && \ -# apt-get update && apt-get upgrade -y && \ -# apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt/archives - -# #FROM scratch AS final -# USER 1001:1001 -# #COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -# COPY ./mongodb_exporter /bin/mongodb_exporter -# #RUN chmod g+rwX /bin/mongodb_exporter -# RUN chmod 777 /bin/mongodb_exporter - -# EXPOSE 9216 -# ENTRYPOINT ["/mongodb_exporter"] - - - -# Copyright Broadcom, Inc. All Rights Reserved. -# SPDX-License-Identifier: APACHE-2.0 - -FROM docker.io/bitnami/minideb:bookworm - -ARG DOWNLOADS_URL="downloads.bitnami.com/files/stacksmith" -ARG TARGETARCH - -LABEL com.vmware.cp.artifact.flavor="sha256:c50c90cfd9d12b445b011e6ad529f1ad3daea45c26d20b00732fae3cd71f6a83" \ - org.opencontainers.image.base.name="docker.io/bitnami/minideb:bookworm" \ - org.opencontainers.image.created="2025-01-17T03:23:22Z" \ - org.opencontainers.image.description="Application packaged by Broadcom, Inc." \ - org.opencontainers.image.documentation="https://github.com/bitnami/containers/tree/main/bitnami/mongodb-exporter/README.md" \ - org.opencontainers.image.licenses="Apache-2.0" \ - org.opencontainers.image.ref.name="0.43.1-debian-12-r2" \ - org.opencontainers.image.source="https://github.com/bitnami/containers/tree/main/bitnami/mongodb-exporter" \ - org.opencontainers.image.title="mongodb-exporter" \ - org.opencontainers.image.vendor="Broadcom, Inc." \ - org.opencontainers.image.version="0.43.1" - -ENV HOME="/" \ - OS_ARCH="${TARGETARCH:-amd64}" \ - OS_FLAVOUR="debian-12" \ - OS_NAME="linux" - -# COPY prebuildfs / -# SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -# Install required system packages and dependencies -RUN install_packages ca-certificates curl procps -# RUN mkdir -p /tmp/bitnami/pkg/cache/ ; cd /tmp/bitnami/pkg/cache/ ; \ -# COMPONENTS=( \ -# "mongodb-exporter-0.43.1-1-linux-${OS_ARCH}-debian-12" \ -# ) ; \ -# for COMPONENT in "${COMPONENTS[@]}"; do \ -# if [ ! -f "${COMPONENT}.tar.gz" ]; then \ -# curl -SsLf "https://${DOWNLOADS_URL}/${COMPONENT}.tar.gz" -O ; \ -# curl -SsLf "https://${DOWNLOADS_URL}/${COMPONENT}.tar.gz.sha256" -O ; \ -# fi ; \ -# sha256sum -c "${COMPONENT}.tar.gz.sha256" ; \ -# tar -zxf "${COMPONENT}.tar.gz" -C /opt/bitnami --strip-components=2 --no-same-owner --wildcards '*/files' ; \ -# rm -rf "${COMPONENT}".tar.gz{,.sha256} ; \ -# done - -RUN mkdir -p /opt/bitnami/mongodb-exporter/bin - -COPY ./mongodb_exporter /opt/bitnami/mongodb-exporter/bin/mongodb_exporter - -RUN apt-get autoremove --purge -y curl && \ - apt-get update && apt-get upgrade -y && \ - apt-get clean && rm -rf /var/lib/apt/lists /var/cache/apt/archives -RUN chmod g+rwX /opt/bitnami -RUN find / -perm /6000 -type f -exec chmod a-s {} \; || true -RUN ln -sf /opt/bitnami/mongodb-exporter/bin/mongodb_exporter /bin/mongodb_exporter - -ENV APP_VERSION="0.43.1" \ - BITNAMI_APP_NAME="mongodb-exporter" \ - PATH="/opt/bitnami/mongodb-exporter/bin:$PATH" +FROM alpine AS builder +RUN apk add --no-cache ca-certificates +FROM scratch AS final +USER 65535:65535 +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY ./mongodb_exporter / EXPOSE 9216 - -WORKDIR /opt/bitnami/mongodb-exporter -USER 1001 -ENTRYPOINT [ "mongodb_exporter" ] \ No newline at end of file +ENTRYPOINT ["/mongodb_exporter"] \ No newline at end of file diff --git a/Makefile b/Makefile index 515ad8fef..a2eed500b 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,6 @@ GO_TEST_PATH ?= ./... GO_TEST_EXTRA ?= GO_TEST_COVER_PROFILE ?= cover.out GO_TEST_CODECOV ?= -GOARCH = amd64 -CGO_ENABLED = 0 -GOOS = linux BUILD ?= $(shell date +%FT%T%z) GOVERSION ?= $(shell go version | cut -d " " -f3) @@ -71,10 +68,10 @@ env: @echo $(TEST_ENV) | tr ' ' '\n' >.env init: ## Install linters. - cd tools && GOARCH=amd64 CGO_ENABLED=0 GOOS=linux go generate -x -tags=tools + cd tools && go generate -x -tags=tools build: ## Compile using plain go build - GOARCH=amd64 CGO_ENABLED=0 GOOS=linux go build -ldflags="$(GO_BUILD_LDFLAGS)" -o $(PMM_RELEASE_PATH)/mongodb_exporter + go build -ldflags="$(GO_BUILD_LDFLAGS)" -o $(PMM_RELEASE_PATH)/mongodb_exporter release: ## Build the binaries using goreleaser docker run --rm --privileged \