├── .dockerignore ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ └── ci.yaml ├── .gitignore ├── tools.go ├── run.sh ├── main.go ├── example └── k8s │ ├── configmap.yaml │ └── deployment.yaml ├── pkg ├── utils │ └── utils.go ├── prom │ ├── server.go │ └── model.go ├── config │ └── config.go └── retriever │ ├── cost.go │ └── api.go ├── Dockerfile ├── LICENSE ├── README.md ├── cmd └── root.go ├── .editorconfig ├── go.mod └── go.sum /.dockerignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /vendor 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @rebuy-de/prp-cost-exporter 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /cost-exporter* 3 | /dist 4 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | 3 | package main 4 | 5 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 6 | import ( 7 | _ "github.com/rebuy-de/rebuy-go-sdk/v7/cmd/buildutil" 8 | ) 9 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | # This makes sure we don't spend a lot of money on API calls when the 6 | # process starts crash looping for whatever reason: 7 | ./usr/local/bin/cost-exporter "$@" & 8 | 9 | while true; do sleep 365d; done 10 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/rebuy-de/rebuy-go-sdk/v7/pkg/cmdutil" 5 | "github.com/sirupsen/logrus" 6 | 7 | "github.com/rebuy-de/cost-exporter/cmd" 8 | ) 9 | 10 | func main() { 11 | defer cmdutil.HandleExit() 12 | if err := cmd.NewRootCommand().Execute(); err != nil { 13 | logrus.Fatal(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/k8s/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: cost-exporter 5 | labels: 6 | app: cost-exporter 7 | data: 8 | config.yaml: |- 9 | accounts: 10 | - name: staging 11 | id: ABCDEFGHIJKLMNOPQRST 12 | secret: 1234567890123456789012345678901234567890 13 | settings: 14 | costCron: "0 0 19 * * *" 15 | coresInterval: 300 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "gomod" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | day: "tuesday" 10 | time: "10:00" 11 | timezone: "Europe/Berlin" 12 | groups: 13 | golang: 14 | patterns: 15 | - "*" 16 | -------------------------------------------------------------------------------- /pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/aws/aws-sdk-go-v2/aws" 7 | "github.com/aws/aws-sdk-go-v2/service/costexplorer/types" 8 | ) 9 | 10 | func GetIntervalForPastDay(daysAgo int) *types.DateInterval { 11 | now := time.Now() 12 | start := now.AddDate(0, 0, -daysAgo) 13 | end := now.AddDate(0, 0, (-daysAgo)+1) 14 | dateRange := types.DateInterval{ 15 | Start: aws.String(start.Format("2006-01-02")), 16 | End: aws.String(end.Format("2006-01-02")), 17 | } 18 | return &dateRange 19 | } 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24-alpine as builder 2 | 3 | RUN apk add --no-cache git openssl 4 | 5 | ENV CGO_ENABLED=0 6 | RUN go install golang.org/x/lint/golint@latest 7 | 8 | COPY . /build 9 | RUN cd /build && ./buildutil 10 | 11 | FROM alpine:latest 12 | 13 | RUN apk add --no-cache ca-certificates tzdata && \ 14 | cp /usr/share/zoneinfo/Europe/Berlin /etc/localtime && \ 15 | echo "Europe/Berlin" > /etc/timezone && \ 16 | apk del tzdata 17 | 18 | COPY --from=builder /build/dist/cost-exporter /usr/local/bin/ 19 | COPY run.sh /run.sh 20 | 21 | RUN adduser -D cost-exporter 22 | USER cost-exporter 23 | 24 | ENTRYPOINT ["/run.sh"] 25 | -------------------------------------------------------------------------------- /pkg/prom/server.go: -------------------------------------------------------------------------------- 1 | package prom 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/prometheus/client_golang/prometheus" 8 | "github.com/prometheus/client_golang/prometheus/promhttp" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | func Run(port string) { 13 | r := prometheus.NewRegistry() 14 | r.MustRegister(C.CoreCount) 15 | r.MustRegister(C.Cost) 16 | r.MustRegister(C.ReservationCoverage) 17 | r.MustRegister(C.ReservationUtilization) 18 | r.MustRegister(C.SpotRequest) 19 | 20 | http.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) 21 | 22 | go func() { 23 | logrus.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil)) 24 | }() 25 | } 26 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "github.com/sirupsen/logrus" 7 | yaml "gopkg.in/yaml.v3" 8 | ) 9 | 10 | type Config struct { 11 | Accounts []Account 12 | Settings struct { 13 | CostCron string `yaml:"costCron"` 14 | CoresInterval int64 `yaml:"coresInterval"` 15 | } 16 | } 17 | 18 | type Account struct { 19 | Name string 20 | ID string 21 | Secret string 22 | } 23 | 24 | func Parse(configPath string) Config { 25 | config := Config{} 26 | var err error 27 | 28 | raw, err := ioutil.ReadFile(configPath) 29 | if err != nil { 30 | logrus.Fatal(err) 31 | } 32 | 33 | err = yaml.Unmarshal([]byte(raw), &config) 34 | if err != nil { 35 | logrus.Fatal(err) 36 | } 37 | 38 | return config 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 reBuy reCommerce GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | 4 | metadata: 5 | name: cost-exporter 6 | labels: 7 | app: cost-exporter 8 | 9 | spec: 10 | replicas: 1 11 | strategy: 12 | rollingUpdate: 13 | maxUnavailable: 0 14 | 15 | selector: 16 | matchLabels: 17 | app: cost-exporter 18 | 19 | template: 20 | metadata: 21 | name: cost-exporter 22 | labels: 23 | app: cost-exporter 24 | annotations: 25 | prometheus.io/scrape: "true" 26 | prometheus.io/port: "8080" 27 | prometheus.io/path: "/metrics" 28 | 29 | spec: 30 | containers: 31 | - name: silo 32 | image: quay.io/rebuy/cost-exporter:main 33 | imagePullPolicy: Always 34 | args: 35 | - '--config=/cost-exporter/config.yaml' 36 | - '--port=8080' 37 | ports: 38 | - containerPort: 8080 39 | resources: 40 | requests: 41 | cpu: 10m 42 | memory: 50Mi 43 | limits: 44 | cpu: 10m 45 | memory: 50Mi 46 | volumeMounts: 47 | - name: config-volume 48 | mountPath: /cost-exporter 49 | volumes: 50 | - name: config-volume 51 | configMap: 52 | name: cost-exporter 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cost-exporter 2 | 3 | ![Build Status](https://github.com/rebuy-de/cost-exporter/workflows/Golang/badge.svg?branch=main) 4 | ![license](https://img.shields.io/github/license/rebuy-de/cost-exporter.svg) 5 | 6 | Retrieves cost metrics and core counts from the AWS API and exposes this information via a Prometheus `/metrics` endpoint. 7 | 8 | > **Development Status** *cost-exporter* is for internal use only. Feel free to use 9 | > it, but expect big unanounced changes at any time. Furthermore we are very 10 | > restrictive about code changes, therefore you should communicate any changes 11 | > before working on an issue. 12 | 13 | 14 | ## Installation 15 | 16 | Docker containers are are provided [here](https://quay.io/repository/rebuy/cost-exporter). To obtain the latest docker image run `docker pull quay.io/rebuy/cost-exporter:main`. 17 | 18 | To compile *cost-exporter* from source you need a working 19 | [Golang](https://golang.org/doc/install) development environment. 20 | 21 | Then you just need to run `./buildutil` to compile a binary into the project 22 | directory which you can then execute. With `./buildutil -x linux/arm64` 23 | you can cross compile *cost-exporter* for other platforms. 24 | 25 | 26 | ## Usage 27 | 28 | **cost-exporter**'s configuration is done using an configuration file that is pointed to when running the command, as well as a flag that defines the port it should listen on: 29 | ``` 30 | cost-exporter --config=/cost-exporter/config.yaml --port=8080 31 | ``` 32 | 33 | For more information, run: 34 | ``` 35 | cost-exporter --help 36 | ``` 37 | 38 | 39 | ### Running in Kubernetes 40 | 41 | Please see the `example/k8s/` directory for an example of how to run `cost-exporter` in Kubernetes. 42 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/rebuy-de/cost-exporter/pkg/config" 7 | "github.com/rebuy-de/cost-exporter/pkg/prom" 8 | "github.com/rebuy-de/cost-exporter/pkg/retriever" 9 | "github.com/rebuy-de/rebuy-go-sdk/v7/pkg/cmdutil" 10 | _ "github.com/rebuy-de/rebuy-go-sdk/v7/pkg/instutil" 11 | "github.com/sirupsen/logrus" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | func NewRootCommand() *cobra.Command { 16 | app := new(App) 17 | 18 | return cmdutil.New( 19 | "cost-exporter", "AWS billing data Prometheus exporter.", 20 | app.Bind, 21 | cmdutil.WithLogToGraylog(), 22 | cmdutil.WithLogVerboseFlag(), 23 | cmdutil.WithVersionCommand(), 24 | cmdutil.WithVersionLog(logrus.DebugLevel), 25 | cmdutil.WithRun(app.Run), 26 | ) 27 | } 28 | 29 | type App struct { 30 | config string 31 | port string 32 | } 33 | 34 | func (app *App) Run(ctx context.Context, cmd *cobra.Command, args []string) { 35 | if app.config == "" { 36 | logrus.Fatal("Configuration file location not defined.") 37 | } 38 | 39 | config := config.Parse(app.config) 40 | 41 | APIRetriever := retriever.APIRetriever{ 42 | Accounts: config.Accounts, 43 | IntervalSec: config.Settings.CoresInterval, 44 | } 45 | APIRetriever.Run(ctx) 46 | 47 | costRetriever := retriever.CostRetriever{ 48 | Accounts: config.Accounts, 49 | Cron: config.Settings.CostCron, 50 | } 51 | costRetriever.Run(ctx) 52 | 53 | prom.Run(app.port) 54 | 55 | select {} 56 | } 57 | 58 | func (app *App) Bind(cmd *cobra.Command) error { 59 | cmd.PersistentFlags().StringVarP( 60 | &app.config, "config", "c", "", `Path to configuration file.`) 61 | cmd.PersistentFlags().StringVarP( 62 | &app.port, "port", "p", "8080", `Port to bind to.`) 63 | 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/prom/model.go: -------------------------------------------------------------------------------- 1 | package prom 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | ) 6 | 7 | type Costs struct { 8 | Cost prometheus.GaugeVec 9 | CoreCount prometheus.GaugeVec 10 | ReservationCoverage prometheus.GaugeVec 11 | ReservationUtilization prometheus.GaugeVec 12 | SpotRequest prometheus.GaugeVec 13 | } 14 | 15 | var C = Costs{ 16 | Cost: *prometheus.NewGaugeVec( 17 | prometheus.GaugeOpts{ 18 | Namespace: "rebuy", 19 | Subsystem: "cost_exporter", 20 | Name: "costs", 21 | Help: "Costs by account and by service.", 22 | }, 23 | []string{"account", "service"}, 24 | ), 25 | CoreCount: *prometheus.NewGaugeVec( 26 | prometheus.GaugeOpts{ 27 | Namespace: "rebuy", 28 | Subsystem: "cost_exporter", 29 | Name: "cores", 30 | Help: "Count of all virtual CPUs in all regions of a specific account.", 31 | }, 32 | []string{"account", "region"}, 33 | ), 34 | ReservationCoverage: *prometheus.NewGaugeVec( 35 | prometheus.GaugeOpts{ 36 | Namespace: "rebuy", 37 | Subsystem: "cost_exporter", 38 | Name: "reservationcoverage", 39 | Help: "Coverage of running EC2 instances by reservations in percent.", 40 | }, 41 | []string{"account"}, 42 | ), 43 | ReservationUtilization: *prometheus.NewGaugeVec( 44 | prometheus.GaugeOpts{ 45 | Namespace: "rebuy", 46 | Subsystem: "cost_exporter", 47 | Name: "reservationutilization", 48 | Help: "Utilization reservations in percent.", 49 | }, 50 | []string{"account"}, 51 | ), 52 | SpotRequest: *prometheus.NewGaugeVec( 53 | prometheus.GaugeOpts{ 54 | Namespace: "rebuy", 55 | Subsystem: "cost_exporter", 56 | Name: "spotinstancerequest", 57 | Help: "Spot Instance Request", 58 | }, 59 | []string{"account", "region", "state", "code", "instance_type", "instance_id", "availability_zone"}, 60 | ), 61 | } 62 | 63 | func (c *Costs) SetTotalCoreCount(account string, region string, count float64) { 64 | c.CoreCount.With(prometheus.Labels{ 65 | "account": account, 66 | "region": region, 67 | }).Set(count) 68 | } 69 | 70 | func (c *Costs) SetCosts(account string, service string, cost float64) { 71 | c.Cost.With(prometheus.Labels{ 72 | "account": account, 73 | "service": service, 74 | }).Set(cost) 75 | } 76 | 77 | func (c *Costs) SetReservationCoverage(account string, coverage float64) { 78 | c.ReservationCoverage.With(prometheus.Labels{ 79 | "account": account, 80 | }).Set(coverage) 81 | } 82 | 83 | func (c *Costs) SetReservationUtilization(account string, coverage float64) { 84 | c.ReservationUtilization.With(prometheus.Labels{ 85 | "account": account, 86 | }).Set(coverage) 87 | } 88 | 89 | func (c *Costs) SetSpotRequest(spotRequestItems []prometheus.Labels) { 90 | c.SpotRequest.Reset() 91 | for _, label := range spotRequestItems { 92 | c.SpotRequest.With(label).Set(1) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Golang 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | types: [opened, reopened, synchronize] 8 | release: 9 | types: [published] 10 | schedule: 11 | - cron: '15 3 * * 0' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-24.04 17 | name: CI Build 18 | 19 | steps: 20 | - name: Setup Go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version: '1.24' 24 | - name: Setup tools 25 | run: | 26 | go install golang.org/x/lint/golint@latest 27 | - name: Checkout code 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Build Project 32 | run: ./buildutil -v 33 | 34 | container_build: 35 | runs-on: ubuntu-24.04 36 | name: Container Build 37 | 38 | steps: 39 | - uses: actions/checkout@v4 40 | with: 41 | fetch-depth: 0 42 | 43 | - name: Generate image tags for releaes 44 | if: ${{ github.event_name == 'release' }} 45 | shell: bash 46 | run: echo "tags=quay.io/rebuy/cost-exporter:${GITHUB_REF#refs/tags/},074509403805.dkr.ecr.eu-west-1.amazonaws.com/cost-exporter:${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT 47 | id: generate_tags_release 48 | 49 | - name: Generate image tags for PRs 50 | if: ${{ github.event_name != 'release' }} 51 | shell: bash 52 | run: | 53 | if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then 54 | BRANCH="$(echo ${GITHUB_HEAD_REF} | tr '/' '_')" 55 | echo "tags=quay.io/rebuy/cost-exporter:${BRANCH},074509403805.dkr.ecr.eu-west-1.amazonaws.com/cost-exporter:${BRANCH}" >> $GITHUB_OUTPUT 56 | else 57 | echo "tags=quay.io/rebuy/cost-exporter:main,074509403805.dkr.ecr.eu-west-1.amazonaws.com/cost-exporter:main,\ 58 | quay.io/rebuy/cost-exporter:latest,074509403805.dkr.ecr.eu-west-1.amazonaws.com/cost-exporter:latest" >> $GITHUB_OUTPUT 59 | fi 60 | id: generate_tags_pr 61 | 62 | - name: Set up QEMU 63 | if: ${{ github.event_name == 'release' }} 64 | id: qemu 65 | uses: docker/setup-qemu-action@v3 66 | with: 67 | platforms: arm64 68 | 69 | - name: Set up Docker Buildx 70 | uses: docker/setup-buildx-action@v3 71 | with: 72 | install: true 73 | 74 | # Only used to prevent rate limits 75 | - name: Login to Docker Hub 76 | uses: docker/login-action@v3 77 | with: 78 | username: ${{ secrets.DOCKER_USERNAME }} 79 | password: ${{ secrets.DOCKER_PASSWORD }} 80 | 81 | - name: Login to ECR 82 | uses: docker/login-action@v3 83 | with: 84 | registry: 074509403805.dkr.ecr.eu-west-1.amazonaws.com 85 | username: ${{ secrets.AWS_ECR_ACCESS_KEY_ID }} 86 | password: ${{ secrets.AWS_ECR_SECRET_ACCESS_KEY }} 87 | 88 | - name: Login to Quay.io 89 | uses: docker/login-action@v3 90 | with: 91 | registry: quay.io 92 | username: ${{ secrets.QUAY_USERNAME }} 93 | password: ${{ secrets.QUAY_PASSWORD }} 94 | 95 | - name: Build and push 96 | if: ${{ github.event_name == 'release' }} 97 | uses: docker/build-push-action@v5 98 | with: 99 | context: . 100 | push: true 101 | tags: ${{ steps.generate_tags_release.outputs.tags }} 102 | platforms: linux/amd64,linux/arm64 103 | 104 | - name: Build and push 105 | if: ${{ github.event_name != 'release' }} 106 | uses: docker/build-push-action@v5 107 | with: 108 | context: . 109 | push: true 110 | tags: ${{ steps.generate_tags_pr.outputs.tags }} 111 | platforms: linux/amd64 112 | -------------------------------------------------------------------------------- /pkg/retriever/cost.go: -------------------------------------------------------------------------------- 1 | package retriever 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/aws/aws-sdk-go-v2/aws" 9 | "github.com/aws/aws-sdk-go-v2/config" 10 | "github.com/aws/aws-sdk-go-v2/credentials" 11 | "github.com/aws/aws-sdk-go-v2/service/costexplorer" 12 | "github.com/aws/aws-sdk-go-v2/service/costexplorer/types" 13 | "github.com/robfig/cron/v3" 14 | "github.com/sirupsen/logrus" 15 | 16 | ceConfig "github.com/rebuy-de/cost-exporter/pkg/config" 17 | "github.com/rebuy-de/cost-exporter/pkg/prom" 18 | "github.com/rebuy-de/cost-exporter/pkg/utils" 19 | ) 20 | 21 | type CostRetriever struct { 22 | Accounts []ceConfig.Account 23 | Cron string 24 | Services map[string]*costexplorer.Client 25 | } 26 | 27 | func (c *CostRetriever) Run(ctx context.Context) { 28 | c.initialize(ctx) 29 | c.getCosts() 30 | c.scheduleCron() 31 | } 32 | 33 | func (c *CostRetriever) initialize(ctx context.Context) { 34 | c.Services = make(map[string]*costexplorer.Client) 35 | for _, account := range c.Accounts { 36 | credProvider := credentials.NewStaticCredentialsProvider(account.ID, account.Secret, "") 37 | conf, err := config.LoadDefaultConfig(ctx, 38 | config.WithCredentialsProvider(credProvider), 39 | config.WithRegion("eu-west-1"), 40 | ) 41 | if err != nil { 42 | fmt.Println(err) 43 | } 44 | 45 | svc := costexplorer.NewFromConfig(conf) 46 | 47 | c.Services[account.Name] = svc 48 | } 49 | } 50 | 51 | func (c *CostRetriever) scheduleCron() { 52 | cron := cron.New() 53 | cron.AddFunc(c.Cron, c.getCosts) 54 | cron.Start() 55 | } 56 | 57 | func (c *CostRetriever) getCosts() { 58 | ctx := context.Background() 59 | for _, account := range c.Accounts { 60 | c.getReservationCoverage(ctx, account) 61 | c.getReservationUtilization(ctx, account) 62 | c.getCostsByService(ctx, account) 63 | } 64 | } 65 | 66 | func (c *CostRetriever) getCostsByService(ctx context.Context, account ceConfig.Account) { 67 | logrus.Infof("Getting costs for account '%s'", account.Name) 68 | svc := c.Services[account.Name] 69 | respCost, err := svc.GetCostAndUsage(ctx, (&costexplorer.GetCostAndUsageInput{ 70 | Metrics: []string{"BlendedCost"}, 71 | // Getting the cost from 2 days ago is a workaround because getting data 72 | // for yesterday yielded unstable numbers: 73 | TimePeriod: utils.GetIntervalForPastDay(2), 74 | Granularity: types.GranularityDaily, 75 | GroupBy: []types.GroupDefinition{{ 76 | Key: aws.String("SERVICE"), 77 | Type: types.GroupDefinitionTypeDimension, 78 | }}, 79 | })) 80 | if err != nil { 81 | logrus.Fatal(err) 82 | } 83 | 84 | for _, cost := range respCost.ResultsByTime[0].Groups { 85 | amount, err := strconv.ParseFloat(*cost.Metrics["BlendedCost"].Amount, 64) 86 | if err != nil { 87 | logrus.Fatal(err) 88 | } 89 | 90 | // ignore taxes because they are not attributed to billing in real-time 91 | if cost.Keys[0] == "Tax" { 92 | continue 93 | } 94 | 95 | prom.C.SetCosts(account.Name, cost.Keys[0], amount) 96 | } 97 | } 98 | 99 | func (c *CostRetriever) getReservationCoverage(ctx context.Context, account ceConfig.Account) { 100 | logrus.Infof("Getting reservation coverage for account '%s'", account.Name) 101 | svc := c.Services[account.Name] 102 | 103 | respReservation, err := svc.GetReservationCoverage(ctx, &costexplorer.GetReservationCoverageInput{ 104 | Granularity: types.GranularityDaily, 105 | // Unfortunately there is no newer data then 3 days ago. 106 | TimePeriod: utils.GetIntervalForPastDay(3), 107 | }) 108 | if err != nil { 109 | logrus.Fatal(err) 110 | } 111 | 112 | coveragePercent, err := strconv.ParseFloat(*respReservation.Total.CoverageHours.CoverageHoursPercentage, 64) 113 | if err != nil { 114 | logrus.Fatal(err) 115 | } 116 | 117 | prom.C.SetReservationCoverage(account.Name, coveragePercent) 118 | } 119 | 120 | func (c *CostRetriever) getReservationUtilization(ctx context.Context, account ceConfig.Account) { 121 | logrus.Infof("Getting reservation utilization for account '%s'", account.Name) 122 | svc := c.Services[account.Name] 123 | 124 | respReservation, err := svc.GetReservationUtilization(ctx, &costexplorer.GetReservationUtilizationInput{ 125 | Granularity: types.GranularityDaily, 126 | // Unfortunately there is no newer data then 3 days ago. 127 | TimePeriod: utils.GetIntervalForPastDay(3), 128 | }) 129 | if err != nil { 130 | logrus.Fatal(err) 131 | } 132 | 133 | utilizationPercent, err := strconv.ParseFloat(*respReservation.Total.UtilizationPercentage, 64) 134 | if err != nil { 135 | logrus.Fatal(err) 136 | } 137 | 138 | prom.C.SetReservationUtilization(account.Name, utilizationPercent) 139 | } 140 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Distributed via https://github.com/rebuy-de/terraform-cluster-config 2 | # Modify only there, changes in project repos will be overwritten 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 4 10 | indent_style = space 11 | insert_final_newline = true 12 | max_line_length = 120 13 | tab_width = 4 14 | trim_trailing_whitespace = true 15 | ij_continuation_indent_size = 4 16 | ij_formatter_off_tag = @formatter:off 17 | ij_formatter_on_tag = @formatter:on 18 | ij_formatter_tags_enabled = true 19 | ij_smart_tabs = false 20 | ij_visual_guides = 21 | ij_wrap_on_typing = false 22 | 23 | [openapi-spec.yaml] 24 | ij_formatter_enabled = false 25 | 26 | [*.bpmn] 27 | ij_formatter_enabled = false 28 | 29 | [*.tf] 30 | ij_formatter_enabled = false 31 | 32 | [{*.htm,*.html,*.sht,*.shtm,*.shtml}] 33 | ij_html_attribute_wrap = off 34 | ij_html_do_not_indent_children_of_tags = 35 | ij_html_keep_blank_lines = 1 36 | ij_html_text_wrap = off 37 | 38 | [{*.cjs,*.js}] 39 | ij_javascript_do_while_brace_force = always 40 | ij_javascript_for_brace_force = always 41 | ij_javascript_if_brace_force = always 42 | ij_javascript_keep_blank_lines_in_code = 1 43 | ij_javascript_use_double_quotes = false 44 | ij_javascript_while_brace_force = always 45 | 46 | [{*.ats,*.cts,*.mts,*.ts}] 47 | ij_typescript_do_while_brace_force = always 48 | ij_typescript_for_brace_force = always 49 | ij_typescript_if_brace_force = always 50 | ij_typescript_import_prefer_absolute_path = true 51 | ij_typescript_keep_blank_lines_in_code = 1 52 | ij_typescript_space_before_function_left_parenth = false 53 | ij_typescript_use_double_quotes = false 54 | ij_typescript_while_brace_force = always 55 | 56 | [*.coffee] 57 | indent_size = 2 58 | 59 | [*.java] 60 | ij_java_blank_lines_around_field = 1 61 | ij_java_blank_lines_around_initializer = 0 62 | ij_java_class_brace_style = next_line 63 | ij_java_class_count_to_use_import_on_demand = 99 64 | ij_java_do_while_brace_force = always 65 | ij_java_doc_add_blank_line_after_param_comments = true 66 | ij_java_doc_add_blank_line_after_return = true 67 | ij_java_for_brace_force = always 68 | ij_java_if_brace_force = always 69 | ij_java_keep_blank_lines_before_right_brace = 0 70 | ij_java_keep_blank_lines_in_code = 1 71 | ij_java_keep_blank_lines_in_declarations = 0 72 | ij_java_keep_simple_classes_in_one_line = true 73 | ij_java_keep_simple_lambdas_in_one_line = true 74 | ij_java_method_brace_style = next_line 75 | ij_java_names_count_to_use_import_on_demand = 99 76 | ij_java_new_line_after_lparen_in_record_header = true 77 | ij_java_packages_to_use_import_on_demand = 78 | ij_java_record_components_wrap = on_every_item 79 | ij_java_rparen_on_new_line_in_record_header = true 80 | ij_java_while_brace_force = always 81 | 82 | [{*.kt,*.kts}] 83 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 84 | ij_kotlin_name_count_to_use_star_import = 2147483647 85 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 86 | ij_kotlin_packages_to_use_import_on_demand = 87 | 88 | [{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] 89 | ij_php_align_multiline_parameters = false 90 | ij_php_blank_lines_around_field = 1 91 | ij_php_blank_lines_before_return_statement = 1 92 | ij_php_comma_after_last_array_element = true 93 | ij_php_force_short_declaration_array_style = true 94 | ij_php_keep_blank_lines_before_right_brace = 0 95 | ij_php_keep_blank_lines_in_code = 1 96 | ij_php_keep_blank_lines_in_declarations = 0 97 | ij_php_keep_rparen_and_lbrace_on_one_line = true 98 | ij_php_lower_case_boolean_const = true 99 | ij_php_lower_case_null_const = true 100 | ij_php_method_parameters_new_line_after_left_paren = true 101 | ij_php_method_parameters_right_paren_on_new_line = true 102 | ij_php_phpdoc_blank_line_before_tags = true 103 | ij_php_phpdoc_blank_lines_around_parameters = true 104 | ij_php_space_after_type_cast = true 105 | ij_php_space_before_short_closure_left_parenthesis = true 106 | 107 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] 108 | ij_xml_space_inside_empty_tag = true 109 | 110 | [{*.tf,*.tfvars,*.hcl}] 111 | tab_width = 2 112 | ij_continuation_indent_size = 2 113 | 114 | [*.less] 115 | tab_width = 2 116 | ij_continuation_indent_size = 2 117 | 118 | [*.sass] 119 | tab_width = 2 120 | ij_continuation_indent_size = 2 121 | 122 | [*.scala] 123 | ij_scala_do_while_brace_force = always 124 | ij_scala_for_brace_force = always 125 | ij_scala_if_brace_force = always 126 | ij_scala_keep_blank_lines_before_right_brace = 0 127 | ij_scala_keep_blank_lines_in_code = 0 128 | ij_scala_keep_blank_lines_in_declarations = 0 129 | ij_scala_multiline_string_closing_quotes_on_new_line = false 130 | 131 | [{*.yaml,*.yml}] 132 | ij_yaml_spaces_within_braces = false 133 | ij_yaml_spaces_within_brackets = false 134 | 135 | [*.pp] 136 | indent_size = 2 137 | tab_width = 2 138 | ij_continuation_indent_size = 2 139 | -------------------------------------------------------------------------------- /pkg/retriever/api.go: -------------------------------------------------------------------------------- 1 | package retriever 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/aws/aws-sdk-go-v2/aws" 9 | "github.com/aws/aws-sdk-go-v2/config" 10 | "github.com/aws/aws-sdk-go-v2/credentials" 11 | "github.com/aws/aws-sdk-go-v2/service/ec2" 12 | "github.com/aws/aws-sdk-go-v2/service/ec2/types" 13 | "github.com/prometheus/client_golang/prometheus" 14 | "github.com/sirupsen/logrus" 15 | 16 | ceConfig "github.com/rebuy-de/cost-exporter/pkg/config" 17 | "github.com/rebuy-de/cost-exporter/pkg/prom" 18 | ) 19 | 20 | type APIRetriever struct { 21 | Accounts []ceConfig.Account 22 | IntervalSec int64 23 | Services []Service 24 | } 25 | 26 | type Service struct { 27 | Region string 28 | svc *ec2.Client 29 | Account string 30 | } 31 | 32 | func (c *APIRetriever) Run(ctx context.Context) { 33 | c.initialize(ctx) 34 | c.getCores(ctx) 35 | c.getSpotInstances(ctx) 36 | go c.scheduleInterval(ctx) 37 | } 38 | 39 | func getAvailableRegions(ctx context.Context, credProvider credentials.StaticCredentialsProvider) []types.Region { 40 | conf, err := config.LoadDefaultConfig(ctx, 41 | config.WithCredentialsProvider(credProvider), 42 | config.WithRegion("eu-west-1"), 43 | ) 44 | if err != nil { 45 | fmt.Println(err) 46 | } 47 | 48 | svc := ec2.NewFromConfig(conf) 49 | regions, err := svc.DescribeRegions(ctx, &ec2.DescribeRegionsInput{AllRegions: aws.Bool(false)}) 50 | if err != nil { 51 | fmt.Println(err) 52 | } 53 | return regions.Regions 54 | } 55 | 56 | func (c *APIRetriever) initialize(ctx context.Context) { 57 | for _, account := range c.Accounts { 58 | credProvider := credentials.NewStaticCredentialsProvider(account.ID, account.Secret, "") 59 | regions := getAvailableRegions(ctx, credProvider) 60 | 61 | for _, region := range regions { 62 | conf, err := config.LoadDefaultConfig(ctx, 63 | config.WithCredentialsProvider(credProvider), 64 | config.WithRegion(*region.RegionName), 65 | ) 66 | if err != nil { 67 | fmt.Println(err) 68 | } 69 | 70 | svc := ec2.NewFromConfig(conf) 71 | 72 | c.Services = append(c.Services, Service{ 73 | Region: *region.RegionName, 74 | svc: svc, 75 | Account: account.Name, 76 | }) 77 | } 78 | } 79 | } 80 | 81 | func (c *APIRetriever) scheduleInterval(ctx context.Context) { 82 | for range time.Tick(time.Duration(c.IntervalSec) * time.Second) { 83 | c.getCores(ctx) 84 | c.getSpotInstances(ctx) 85 | } 86 | } 87 | 88 | func (c *APIRetriever) getCores(ctx context.Context) { 89 | for _, service := range c.Services { 90 | logrus.Infof("Getting cores for account '%s' and region '%s'", service.Account, service.Region) 91 | var totalCoreCount int32 92 | params := &ec2.DescribeInstancesInput{ 93 | MaxResults: aws.Int32(100), 94 | } 95 | for { 96 | resp2, err := service.svc.DescribeInstances(ctx, params) 97 | if err != nil { 98 | fmt.Println(err) 99 | continue 100 | } 101 | for _, reservation := range resp2.Reservations { 102 | for _, instance := range reservation.Instances { 103 | if instance.State.Name == types.InstanceStateNameRunning { 104 | totalCoreCount += *instance.CpuOptions.CoreCount * *instance.CpuOptions.ThreadsPerCore 105 | } 106 | } 107 | } 108 | 109 | if resp2.NextToken == nil { 110 | break 111 | } 112 | 113 | params.NextToken = resp2.NextToken 114 | } 115 | prom.C.SetTotalCoreCount(service.Account, service.Region, float64(totalCoreCount)) 116 | } 117 | } 118 | 119 | func (c *APIRetriever) getSpotInstances(ctx context.Context) { 120 | spotRequestItems := []prometheus.Labels{} 121 | for _, service := range c.Services { 122 | logrus.Infof("Getting SpotInstances for account '%s' and region '%s'", service.Account, service.Region) 123 | resp, err := service.svc.DescribeSpotInstanceRequests(ctx, &ec2.DescribeSpotInstanceRequestsInput{}) 124 | if err != nil { 125 | fmt.Println(err) 126 | continue 127 | } 128 | 129 | for _, request := range resp.SpotInstanceRequests { 130 | var instanceID string 131 | if request.InstanceId == nil { 132 | instanceID = "" 133 | } else { 134 | instanceID = *request.InstanceId 135 | } 136 | 137 | var launchedAvailabilityZone string 138 | if request.LaunchedAvailabilityZone == nil { 139 | launchedAvailabilityZone = "" 140 | } else { 141 | launchedAvailabilityZone = *request.LaunchedAvailabilityZone 142 | } 143 | 144 | labels := prometheus.Labels{ 145 | "account": service.Account, 146 | "region": service.Region, 147 | "state": string(request.State), 148 | "code": *request.Status.Code, 149 | "instance_type": string(request.LaunchSpecification.InstanceType), 150 | "instance_id": instanceID, 151 | "availability_zone": launchedAvailabilityZone, 152 | } 153 | spotRequestItems = append(spotRequestItems, labels) 154 | } 155 | } 156 | prom.C.SetSpotRequest(spotRequestItems) 157 | } 158 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rebuy-de/cost-exporter 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go-v2 v1.41.0 7 | github.com/aws/aws-sdk-go-v2/config v1.32.4 8 | github.com/aws/aws-sdk-go-v2/credentials v1.19.4 9 | github.com/aws/aws-sdk-go-v2/service/costexplorer v1.62.0 10 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.276.0 11 | github.com/prometheus/client_golang v1.23.2 12 | github.com/rebuy-de/rebuy-go-sdk/v7 v7.1.0 13 | github.com/robfig/cron/v3 v3.0.1 14 | github.com/sirupsen/logrus v1.9.3 15 | github.com/spf13/cobra v1.10.2 16 | gopkg.in/yaml.v3 v3.0.1 17 | ) 18 | 19 | require ( 20 | dario.cat/mergo v1.0.0 // indirect 21 | github.com/AlekSi/pointer v1.2.0 // indirect 22 | github.com/Masterminds/goutils v1.1.1 // indirect 23 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 24 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect 25 | github.com/Microsoft/go-winio v0.6.1 // indirect 26 | github.com/ProtonMail/go-crypto v1.1.3 // indirect 27 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0 // indirect 28 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect 29 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2 // indirect 30 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect 31 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect 32 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect 33 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0 // indirect 34 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect 35 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0 // indirect 36 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect 37 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 // indirect 38 | github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1 // indirect 39 | github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect 40 | github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect 41 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect 42 | github.com/aws/aws-sdk-go-v2/service/sts v1.41.4 // indirect 43 | github.com/aws/smithy-go v1.24.0 // indirect 44 | github.com/beorn7/perks v1.0.1 // indirect 45 | github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect 46 | github.com/cavaliergopher/cpio v1.0.1 // indirect 47 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 48 | github.com/cloudflare/circl v1.6.1 // indirect 49 | github.com/cyphar/filepath-securejoin v0.2.5 // indirect 50 | github.com/emirpasic/gods v1.18.1 // indirect 51 | github.com/gemnasium/logrus-graylog-hook/v3 v3.2.0 // indirect 52 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 53 | github.com/go-git/go-billy/v5 v5.6.0 // indirect 54 | github.com/go-git/go-git/v5 v5.13.0 // indirect 55 | github.com/gobwas/glob v0.2.3 // indirect 56 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 57 | github.com/google/rpmpack v0.5.0 // indirect 58 | github.com/google/uuid v1.6.0 // indirect 59 | github.com/goreleaser/chglog v0.5.0 // indirect 60 | github.com/goreleaser/fileglob v1.3.0 // indirect 61 | github.com/goreleaser/nfpm/v2 v2.35.3 // indirect 62 | github.com/huandu/xstrings v1.4.0 // indirect 63 | github.com/imdario/mergo v0.3.16 // indirect 64 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 65 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 66 | github.com/kevinburke/ssh_config v1.2.0 // indirect 67 | github.com/klauspost/compress v1.18.0 // indirect 68 | github.com/klauspost/pgzip v1.2.6 // indirect 69 | github.com/mitchellh/copystructure v1.2.0 // indirect 70 | github.com/mitchellh/mapstructure v1.5.0 // indirect 71 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 72 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 73 | github.com/pjbgf/sha1cd v0.3.0 // indirect 74 | github.com/pkg/errors v0.9.1 // indirect 75 | github.com/prometheus/client_model v0.6.2 // indirect 76 | github.com/prometheus/common v0.66.1 // indirect 77 | github.com/prometheus/procfs v0.16.1 // indirect 78 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect 79 | github.com/shopspring/decimal v1.2.0 // indirect 80 | github.com/skeema/knownhosts v1.3.0 // indirect 81 | github.com/spf13/cast v1.5.1 // indirect 82 | github.com/spf13/pflag v1.0.9 // indirect 83 | github.com/tidwall/pretty v1.2.1 // indirect 84 | github.com/ulikunitz/xz v0.5.14 // indirect 85 | github.com/xanzy/ssh-agent v0.3.3 // indirect 86 | gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect 87 | go.yaml.in/yaml/v2 v2.4.2 // indirect 88 | golang.org/x/crypto v0.45.0 // indirect 89 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect 90 | golang.org/x/mod v0.19.0 // indirect 91 | golang.org/x/net v0.47.0 // indirect 92 | golang.org/x/sync v0.13.0 // indirect 93 | golang.org/x/sys v0.38.0 // indirect 94 | golang.org/x/tools v0.23.0 // indirect 95 | google.golang.org/protobuf v1.36.8 // indirect 96 | gopkg.in/warnings.v0 v0.1.2 // indirect 97 | ) 98 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= 4 | github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= 5 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 6 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 7 | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 8 | github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= 9 | github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 10 | github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= 11 | github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= 12 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 13 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 14 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 15 | github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= 16 | github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= 17 | github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= 18 | github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= 19 | github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s= 20 | github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs= 21 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 22 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 23 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 24 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 25 | github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= 26 | github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= 27 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0 h1:2UO6/nT1lCZq1LqM67Oa4tdgP1CvL1sLSxvuD+VrOeE= 28 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0/go.mod h1:5zGj2eA85ClyedTDK+Whsu+w9yimnVIZvhvBKrDquM8= 29 | github.com/aws/aws-sdk-go-v2/config v1.32.4 h1:gl+DxVuadpkYoaDcWllZqLkhGEbvwyqgNVRTmlaf5PI= 30 | github.com/aws/aws-sdk-go-v2/config v1.32.4/go.mod h1:MBUp9Og/bzMmQHjMwace4aJfyvJeadzXjoTcR/SxLV0= 31 | github.com/aws/aws-sdk-go-v2/credentials v1.19.4 h1:KeIZxHVbGWRLhPvhdPbbi/DtFBHNKm6OsVDuiuFefdQ= 32 | github.com/aws/aws-sdk-go-v2/credentials v1.19.4/go.mod h1:Smw5n0nCZE9PeFEguofdXyt8kUC4JNrkDTfBOioPhFA= 33 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= 34 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= 35 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2 h1:VEekE/fJWqAWYozxFQ07B+h8NdvTPAYhV13xIBenuO0= 36 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2/go.mod h1:8vozqAHmDNmoD4YbuDKIfpnLbByzngczL4My1RELLVo= 37 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= 38 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= 39 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= 40 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= 41 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= 42 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= 43 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0 h1:TkbRExyKSVHELwG9gz2+gql37jjec2R5vus9faTomwE= 44 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0/go.mod h1:T3/9xMKudHhnj8it5EqIrhvv11tVZqWYkKcot+BFStc= 45 | github.com/aws/aws-sdk-go-v2/service/costexplorer v1.62.0 h1:YD2xJ3wFL8svkw7cEpt/1rUq1NeMnz+TRXgMooMFoqo= 46 | github.com/aws/aws-sdk-go-v2/service/costexplorer v1.62.0/go.mod h1:SCRS6FhD8HFqq9ISjLdNO4X6uCZ/ESRL2JlIKSI75RQ= 47 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.276.0 h1:EXwbpkq/tsz1lHI5QRoXjnkZRKgW0Xa+mPSv6Dz/9N0= 48 | github.com/aws/aws-sdk-go-v2/service/ec2 v1.276.0/go.mod h1:Wg68QRgy2gEGGdmTPU/UbVpdv8sM14bUZmF64KFwAsY= 49 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= 50 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= 51 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0 h1:UiSyK6ent6OKpkMJN3+k5HZ4sk4UfchEaaW5wv7SblQ= 52 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0/go.mod h1:l7kzl8n8DXoRyFz5cIMG70HnPauWa649TUhgw8Rq6lo= 53 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= 54 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= 55 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 h1:l5puwOHr7IxECuPMIuZG7UKOzAnF24v6t4l+Z5Moay4= 56 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0/go.mod h1:Oov79flWa/n7Ni+lQC3z+VM7PoRM47omRqbJU9B5Y7E= 57 | github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1 h1:bjpWJEXch7moIt3PX2r5XpGROsletl7enqG1Q3Te1Dc= 58 | github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1/go.mod h1:1o/W6JFUuREj2ExoQ21vHJgO7wakvjhol91M9eknFgs= 59 | github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= 60 | github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= 61 | github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw= 62 | github.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= 63 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= 64 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= 65 | github.com/aws/aws-sdk-go-v2/service/sts v1.41.4 h1:YCu/iAhQer8WZ66lldyKkpvMyv+HkPufMa4dyT6wils= 66 | github.com/aws/aws-sdk-go-v2/service/sts v1.41.4/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= 67 | github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= 68 | github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= 69 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 70 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 71 | github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= 72 | github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= 73 | github.com/caarlos0/go-rpmutils v0.2.1-0.20240105125627-01185134a559 h1:5TPRjT2njvPKzXUcrcg6Dt+JPzQF+M5K7xb5V1Nwteg= 74 | github.com/caarlos0/go-rpmutils v0.2.1-0.20240105125627-01185134a559/go.mod h1:sUS7SdlihaphHRYa/Uu4haxl9zL6DLGrFjoTsurEYOw= 75 | github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8= 76 | github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk= 77 | github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= 78 | github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= 79 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 80 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 81 | github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= 82 | github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 83 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 84 | github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= 85 | github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 86 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 87 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 88 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 89 | github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= 90 | github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= 91 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 92 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 93 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 94 | github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 95 | github.com/gemnasium/logrus-graylog-hook/v3 v3.2.0 h1:kWqYB1mtpWV+VM0a4epClv1dP6ICDJiA0Zas3d2G0ow= 96 | github.com/gemnasium/logrus-graylog-hook/v3 v3.2.0/go.mod h1:h8lScu3mcvljXI5lvuIt72V6NdJiW2KqxWWN0asGxNQ= 97 | github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= 98 | github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= 99 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 100 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 101 | github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= 102 | github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= 103 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= 104 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= 105 | github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= 106 | github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= 107 | github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= 108 | github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= 109 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 110 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 111 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 112 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 113 | github.com/google/rpmpack v0.5.0 h1:L16KZ3QvkFGpYhmp23iQip+mx1X39foEsqszjMNBm8A= 114 | github.com/google/rpmpack v0.5.0/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI= 115 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 116 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 117 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 118 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 119 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 120 | github.com/goreleaser/chglog v0.5.0 h1:Sk6BMIpx8+vpAf8KyPit34OgWui8c7nKTMHhYx88jJ4= 121 | github.com/goreleaser/chglog v0.5.0/go.mod h1:Ri46M3lrMuv76FHszs3vtABR8J8k1w9JHYAzxeeOl28= 122 | github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I= 123 | github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU= 124 | github.com/goreleaser/nfpm/v2 v2.35.3 h1:YGEygriY8hbsNdCBUif6RLb5xPISDHc+d22rRGXV4Zk= 125 | github.com/goreleaser/nfpm/v2 v2.35.3/go.mod h1:eyKRLSdXPCV1GgJ0tDNe4SqcZD0Fr5cezRwcuLjpxyM= 126 | github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 127 | github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= 128 | github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 129 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 130 | github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= 131 | github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 132 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 133 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 134 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 135 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 136 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 137 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 138 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 139 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 140 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 141 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 142 | github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= 143 | github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 144 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 145 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 146 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 147 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 148 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 149 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 150 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 151 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 152 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 153 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= 154 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= 155 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 156 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 157 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 158 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 159 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 160 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 161 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 162 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 163 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 164 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 165 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 166 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 167 | github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= 168 | github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= 169 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 170 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 171 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 172 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 173 | github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= 174 | github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= 175 | github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= 176 | github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= 177 | github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= 178 | github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= 179 | github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= 180 | github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= 181 | github.com/rebuy-de/rebuy-go-sdk/v7 v7.1.0 h1:aZBhjdRk9oQx+K3CBenBftgDvPUWgMYAVqS79Z1Ec1o= 182 | github.com/rebuy-de/rebuy-go-sdk/v7 v7.1.0/go.mod h1:IZR3ItPtZ7lg4f9341S2dBcms/xlEsmwXQ0jSFQTOc0= 183 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 184 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 185 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 186 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 187 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 188 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= 189 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= 190 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 191 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 192 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 193 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 194 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 195 | github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= 196 | github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= 197 | github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU= 198 | github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY= 199 | github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w= 200 | github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg= 201 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 202 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 203 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 204 | github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= 205 | github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 206 | github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= 207 | github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 208 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 209 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 210 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 211 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 212 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 213 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 214 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 215 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 216 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 217 | github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg= 218 | github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 219 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 220 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 221 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 222 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 223 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 224 | gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8= 225 | gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0= 226 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 227 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 228 | go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= 229 | go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= 230 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 231 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 232 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 233 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 234 | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 235 | golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= 236 | golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= 237 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= 238 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 239 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 240 | golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= 241 | golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 242 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 243 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 244 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 245 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 246 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 247 | golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 248 | golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 249 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 250 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 251 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= 252 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 253 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 254 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 255 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 256 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 257 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 259 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 260 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 261 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 262 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 263 | golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 264 | golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 265 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 266 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 267 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 268 | golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= 269 | golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 270 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 271 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 272 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 273 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 274 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 275 | golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= 276 | golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 277 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 278 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 279 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 280 | golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= 281 | golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 282 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 283 | google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= 284 | google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 285 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 286 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 287 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 288 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 289 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 290 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 291 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 292 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 293 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 294 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 295 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 296 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 297 | --------------------------------------------------------------------------------