Skip to content

Commit

Permalink
Fix: Exporter not initiating connection to mongo when Unauthorized
Browse files Browse the repository at this point in the history
  • Loading branch information
Dudi Dolev committed Jan 22, 2025
1 parent 111a29d commit 4943042
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 9 deletions.
89 changes: 82 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]

WORKDIR /opt/bitnami/mongodb-exporter
USER 1001
ENTRYPOINT [ "mongodb_exporter" ]
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 \
Expand Down
8 changes: 8 additions & 0 deletions exporter/currentop_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package exporter

import (
"context"
"os"
"strconv"
"time"

Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions exporter/dbstats_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package exporter

import (
"context"
"os"

"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions exporter/diagnostic_data_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package exporter

import (
"context"
"os"

"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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")
}

Expand Down
7 changes: 7 additions & 0 deletions exporter/feature_compatibility_version_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package exporter
import (
"context"
"fmt"
"os"
"strconv"

"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -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
}

Expand Down
6 changes: 6 additions & 0 deletions exporter/replset_status_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package exporter

import (
"context"
"os"

"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
Expand All @@ -27,6 +28,7 @@ import (
const (
replicationNotEnabled = 76
replicationNotYetInitialized = 94
Unauthorized = 13
)

type replSetGetStatusCollector struct {
Expand Down Expand Up @@ -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)

Expand Down
7 changes: 7 additions & 0 deletions exporter/top_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package exporter
import (
"context"
"fmt"
"os"

"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -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
}
Expand Down
13 changes: 13 additions & 0 deletions exporter/topology_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package exporter
import (
"context"
"fmt"
"os"
"sync"

"github.com/pkg/errors"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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")
}

Expand Down
19 changes: 19 additions & 0 deletions exporter/v1_compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"context"
"fmt"
"math"
"os"
"strings"
"time"

Expand Down Expand Up @@ -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")
}

Expand Down Expand Up @@ -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
}

Expand All @@ -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" {
Expand Down
21 changes: 21 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package util

import (
"context"
"fmt"
"os"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
Expand All @@ -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.
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand Down

0 comments on commit 4943042

Please sign in to comment.