Skip to content

Commit

Permalink
Merge pull request #1 from castai/create-scraper
Browse files Browse the repository at this point in the history
Initialize project
  • Loading branch information
Ivaka authored Mar 8, 2024
2 parents 084440b + 811de8d commit 1ad0de3
Show file tree
Hide file tree
Showing 26 changed files with 2,256 additions and 0 deletions.
92 changes: 92 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Build

on:
push:
branches:
- main
release:
types:
- published
pull_request:
branches:
- main

jobs:
build:
name: Build
runs-on: ubuntu-20.04
steps:

- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22


- name: Get release tag
if: github.event_name == 'release'
run: echo "RELEASE_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV

- name: Build Go binary amd64
run: go build -ldflags "-s -w -X main.GitCommit=$GITHUB_SHA -X main.GitRef=$GITHUB_REF -X main.Version=${RELEASE_TAG:-commit-$GITHUB_SHA}" -o bin/gpu-metrics-exporter-amd64 ./cmd/main.go
env:
GOOS: linux
GOARCH: amd64
CGO_ENABLED: 0

- name: Build Go binary arm64
run: go build -ldflags "-s -w -X main.GitCommit=$GITHUB_SHA -X main.GitRef=$GITHUB_REF -X main.Version=${RELEASE_TAG:-commit-$GITHUB_SHA}" -o bin/gpu-metrics-exporter-arm64 ./cmd/main.go
env:
GOOS: linux
GOARCH: arm64
CGO_ENABLED: 0

- name: Test
run: go test -race ./...

# - name: Set up QEMU
# uses: docker/setup-qemu-action@v3

# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v3

# - name: Login to GitHub Container Registry
# uses: docker/login-action@v2
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}

# - name: Build and push PR
# if: ${{ github.event_name == 'pull_request' }}
# uses: docker/build-push-action@v5
# with:
# context: .
# platforms: linux/arm64,linux/amd64
# file: ./Dockerfile
# push: true
# tags: ghcr.io/castai/gpu-metrics-exporter/gpu-metrics-exporter:${{ github.sha }}

# - name: Build and push main
# if: ${{ github.event_name != 'pull_request' && github.event_name != 'release' }}
# uses: docker/build-push-action@v5
# with:
# context: .
# platforms: linux/arm64,linux/amd64
# file: ./Dockerfile
# push: true
# tags: ghcr.io/castai/egressd/egressd:${{ github.sha }}

# - name: Build and push release (egressd collector)
# uses: docker/build-push-action@v5
# with:
# context: .
# push: true
# platforms: linux/arm64,linux/amd64
# file: ./Dockerfile
# tags: |
# ghcr.io/castai/gpu-metrics-exporter/gpu-metrics-exporter:${{ env.RELEASE_TAG }}
# ghcr.io/castai/gpu-metrics-exporter/gpu-metrics-exporter:latest
18 changes: 18 additions & 0 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: golangci-lint
on:
pull_request:
permissions:
contents: read
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.22.0
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
args: --timeout=5m
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bin
coverage.txt
.vscode
87 changes: 87 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
linters-settings:
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: [argument,case,condition,return]
govet:
# shadow is marked as experimental feature, skip it for now.
check-shadowing: false
settings:
printf:
funcs:
- (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
lll:
line-length: 200
maligned:
suggest-new: true
misspell:
locale: US
revive:
rules:
- name: redefines-builtin-id
disabled: true
- name: nested-structs
disabled: true
gci:
sections:
- standard
- default
- prefix(github.com/castai)
linters:
disable-all: true
enable:
- dogsled
- errcheck
- gofmt
- revive
- goprintffuncname
- gosimple
- gosec
- govet
- ineffassign
- lll
- misspell
- nakedret
- exportloopref
- staticcheck
- stylecheck
- typecheck
- unconvert
- unused
- whitespace
- errname
- dupword
- gci
- containedctx
- durationcheck
- errorlint
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd
- bodyclose
- gosec
- linters:
- gosec
# Ignored gosec G107 rule because of many false positives. It states that `http.Get(url)` must not contain a variable.
text: G107
- linters:
- unparam
# ignoring error where function always receives same value - mostly in tests
text: "always receives"
- linters:
- revive
# tolerate code where errors are returned as if-return
text: "redundant if ...; err != nil check, just return error instead."
run:
skip-dirs:
- gen
- e2e
9 changes: 9 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
with-expecter: true
dir: 'mock/{{replace .InterfaceDirRelative "internal" "" 1}}'
packages:
"github.com/castai/gpu-metrics-exporter/internal/exporter":
interfaces:
Exporter:
Scraper:
MetricMapper:
HttpClient:
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM gcr.io/distroless/static-debian11:nonroot
ARG TARGETARCH
COPY bin/gpu-metrics-exporter-$TARGETARCH /usr/local/bin/gpu-metrics-exporter
CMD ["gpu-metrics-exporter"]
38 changes: 38 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.PHONY: lint
lint: check-lint-dependencies
golangci-lint run ./...

.PHONY: fix-lint-lint
fix-lint: check-lint-dependencies
golangci-lint run ./... --fix

.PHONY: build
build:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/gpu-metrics-exporter-amd64 ./cmd/main.go
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/gpu-metrics-exporter-arm64 ./cmd/main.go
docker buildx build --push --platform=linux/amd64,linux/arm64 -t $(TAG) .

.PHONY: generate
generate:
go generate ./...

SHELL := /bin/bash
.PHONY: run
run:
go run ./cmd/main.go

.PHONY: test
test:
go test ./... -race -coverprofile=coverage.txt -covermode=atomic

.PHONY: gen-proto
gen-proto: check-proto-dependencies
protoc pb/metrics.proto --go_out=paths=source_relative:.

.PHONY: check-lint-dependencies
check-lint-dependencies:
@which golangci-lint > /dev/null || (echo "golangci-lint not found, please install it" && exit 1)

.PHONY: check-proto-dependencies
check-proto-dependencies:
@which protoc > /dev/null || (echo "protoc not found, please install it" && exit 1)
128 changes: 128 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/flowcontrol"

"github.com/castai/gpu-metrics-exporter/internal/config"
"github.com/castai/gpu-metrics-exporter/internal/exporter"
"github.com/castai/gpu-metrics-exporter/internal/server"
)

func main() {
log := logrus.New()

cfg, err := config.GetFromEnvironment()
if err != nil {
log.Fatal(err)
}

logLevel, err := logrus.ParseLevel(cfg.LogLevel)
if err != nil {
log.Fatal(err)
}
log.SetLevel(logLevel)

if err := run(cfg, log); err != nil && !errors.Is(err, context.Canceled) {
log.Fatal(err)
}
}

func run(cfg *config.Config, log logrus.FieldLogger) error {
mux := server.NewServerMux(log)

srv := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.HTTPListenPort),
Handler: mux,
ReadHeaderTimeout: 3 * time.Second,
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func() {
stopper := make(chan os.Signal, 1)
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
<-stopper

ctx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownCancel()
if err := srv.Shutdown(ctx); err != nil {
log.Errorf("http server shutdown: %v", err)
}

cancel()
}()

clientset, err := newKubernetesClientset(cfg)
if err != nil {
log.Fatal(err)
}

labelSelector, err := selectorFromMap(cfg.DCGMLabels)
if err != nil {
log.Fatal(err)
}

scraper := exporter.NewScraper(&http.Client{}, log)
mapper := exporter.NewMapper()
ex := exporter.NewExporter(exporter.Config{
ExportInterval: cfg.ExportInterval,
Selector: labelSelector.String(),
DCGMExporterPort: cfg.DCGMPort,
DCGMExporterPath: cfg.DCGMMetricsEndpoint,
Enabled: true,
}, clientset, log, scraper, mapper)

go func() {
if err := ex.Start(ctx); err != nil && !errors.Is(err, context.Canceled) {
log.Errorf("exporter stopped with error %v", err)
cancel()
}
}()

return srv.ListenAndServe()
}

func newKubernetesClientset(cfg *config.Config) (*kubernetes.Clientset, error) {
config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath)
if err != nil {
return nil, err
}
config.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(float32(10), 25)

clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}

return clientset, nil
}

func selectorFromMap(labelMap map[string]string) (labels.Selector, error) {
selector := labels.NewSelector()
var requirements labels.Requirements

for label, value := range labelMap {
req, err := labels.NewRequirement(label, selection.Equals, []string{value})
if err != nil {
return nil, err
}
requirements = append(requirements, *req)
}

return selector.Add(requirements...), nil
}
2 changes: 2 additions & 0 deletions gen_mockery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//go:generate go run github.com/vektra/mockery/[email protected] --all
package mockery
Loading

0 comments on commit 1ad0de3

Please sign in to comment.