diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 189695be..c9e20edc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,11 @@ -# What does this PR do +#### What does this PR do -# The HW vendor this change applies to (if applicable) +#### The HW vendor this change applies to (if applicable) -# The HW model number, product name this change applies to (if applicable) +#### The HW model number, product name this change applies to (if applicable) -# The BMC firmware and/or BIOS versions that this change applies to (if applicable) +#### The BMC firmware and/or BIOS versions that this change applies to (if applicable) -# What version of tooling - vendor specific or opensource does this change depend on (if applicable) +#### What version of tooling - vendor specific or opensource does this change depend on (if applicable) -# How can this change be tested by a PR reviewer? +#### How can this change be tested by a PR reviewer? diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d54b5135..c540a3b3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,72 +1,72 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. +## For most projects, this workflow file will not need changing; you simply need +## to commit it to your repository. +## +## You may wish to alter this file to override the set of languages analyzed, +## or to provide custom queries or build logic. +## +## ******** NOTE ******** +## We have attempted to detect the languages in your repository. Please check +## the `language` matrix defined below to confirm you have the correct set of +## supported CodeQL languages. +## +#name: "CodeQL" # -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. +#on: +# push: +# branches: [ main ] +# pull_request: +# # The branches below must be a subset of the branches above +# branches: [ main ] +# schedule: +# - cron: '34 3 * * 0' # -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. +#jobs: +# analyze: +# name: Analyze +# runs-on: ubuntu-latest +# permissions: +# actions: read +# contents: read +# security-events: write # -name: "CodeQL" - -on: - push: - branches: [ main ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] - schedule: - - cron: '34 3 * * 0' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 +# strategy: +# fail-fast: false +# matrix: +# language: [ 'go' ] +# # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] +# # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support +# +# steps: +# - name: Checkout repository +# uses: actions/checkout@v3 +# +# # Initializes the CodeQL tools for scanning. +# - name: Initialize CodeQL +# uses: github/codeql-action/init@v2 +# with: +# languages: ${{ matrix.language }} +# # If you wish to specify custom queries, you can do so here or in a config file. +# # By default, queries listed here will override any specified in a config file. +# # Prefix the list here with "+" to use these queries and those in the config file. +# +# # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs +# # queries: security-extended,security-and-quality +# +# +# # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). +# # If this step fails, then you should remove it and run the build manually (see below) +# - name: Autobuild +# uses: github/codeql-action/autobuild@v2 +# +# # ℹī¸ Command-line programs to run using the OS shell. +# # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun +# +# # If the Autobuild fails above, remove it and uncomment the following three lines. +# # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. +# +# # - run: | +# # echo "Run, Build Application using script" +# # ./location_of_script_within_repo/buildscript.sh +# +# - name: Perform CodeQL Analysis +# uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/push-pr-lint.yaml b/.github/workflows/push-pr-lint.yaml index 7a9738fa..a6f603d0 100644 --- a/.github/workflows/push-pr-lint.yaml +++ b/.github/workflows/push-pr-lint.yaml @@ -8,14 +8,14 @@ jobs: - name: Install Go uses: actions/setup-go@v3 with: - go-version: '^1.17.0' + go-version: '1.18' - name: Checkout code uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: args: --config .golangci.yml - version: v1.45.2 + version: v1.46.2 - name: Test run: go test ./... build: @@ -28,13 +28,14 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - name: Build image - no push - id: dockerbuild + - name: Build Alloy inband Docker image - no push + id: dockerbuild-alloy-inband uses: docker/build-push-action@v3 with: context: . push: false - tags: ghcr.io/metal-toolbox/alloy:latest + tags: ghcr.io/metal-toolbox/alloy-inband:latest + file: Dockerfile.inband - name: Scan image id: scan diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9046a313..d58e441c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,6 @@ jobs: container-push-latest: uses: metal-toolbox/container-push/.github/workflows/container-push.yml@main with: - name: alloy + name: alloy-inband tag: latest - dockerfile_path: Dockerfile + dockerfile_path: Dockerfile.inband diff --git a/.gitignore b/.gitignore index a79c69e6..3c483295 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ *.test # Application binary itself -/fup +/alloy *~ .*.swp diff --git a/.golangci.yml b/.golangci.yml index e318c4ce..54434f99 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,6 +7,7 @@ linters-settings: govet: enable: - fieldalignment + auto-fix: true check-shadowing: true settings: printf: @@ -19,8 +20,6 @@ linters-settings: min-confidence: 0 gocyclo: min-complexity: 10 - maligned: - suggest-new: true dupl: threshold: 100 goconst: @@ -80,17 +79,14 @@ linters: - stylecheck - whitespace - wsl + - gosec enable-all: false disable-all: true run: - build-tags: - - gingonic +# build-tags: skip-dirs: - - scripts - - docker - - samples - #modules-download-mode: vendor + - internal/fixtures issues: exclude-rules: diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5ee4eab2..00000000 --- a/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -# https://github.com/metal-toolbox/ironlib/blob/main/Dockerfile -FROM ghcr.io/metal-toolbox/alloy:latest - -ENTRYPOINT [ "/bin/bash", "-l", "-c" ] diff --git a/Dockerfile.inband b/Dockerfile.inband new file mode 100644 index 00000000..2a6e2666 --- /dev/null +++ b/Dockerfile.inband @@ -0,0 +1,7 @@ +# https://github.com/metal-toolbox/ironlib/blob/main/Dockerfile +FROM ghcr.io/metal-toolbox/ironlib:latest + +COPY alloy /usr/sbin/alloy +RUN chmod +x /usr/sbin/alloy + +ENTRYPOINT ["/bin/bash", "-l", "-c"] diff --git a/Makefile b/Makefile index ee212754..431bf1ae 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ export DOCKER_BUILDKIT=1 GIT_COMMIT_FULL := $(shell git rev-parse HEAD) GO_VERSION := $(shell expr `go version |cut -d ' ' -f3 |cut -d. -f2` \>= 16) -DOCKER_REGISTRY := "ghcr.io/metal-toolbox/alloy:latest" +DOCKER_REGISTRY := "ghcr.io/metal-toolbox/alloy-inband" REPO := "https://github.com/metal-toolbox/alloy.git" .DEFAULT_GOAL := help @@ -12,7 +12,7 @@ lint: ## Go test test: - CGO_ENABLED=0 go test -v -covermode=atomic ./... + CGO_ENABLED=0 go test -timeout 30s -v -covermode=atomic ./... ## build osx bin build-osx: @@ -29,17 +29,17 @@ ifeq ($(GO_VERSION), 0) endif GOOS=linux GOARCH=amd64 go build -o alloy -## build docker image and tag as ghcr.io/metal-toolbox/alloy:latest -build-image: +## build docker image and tag as ghcr.io/metal-toolbox/alloy-inband:latest +build-image-inband: @echo ">>>> NOTE: You may want to execute 'make build-image-nocache' depending on the Docker stages changed" - docker build --rm=true -f Dockerfile -t ${DOCKER_REGISTRY}:latest . \ + docker build --rm=true -f Dockerfile.inband -t ${DOCKER_REGISTRY}:latest . \ --label org.label-schema.schema-version=1.0 \ --label org.label-schema.vcs-ref=$(GIT_COMMIT_FULL) \ --label org.label-schema.vcs-url=$(REPO) ## build docker image, ignoring the cache -build-image-nocache: - docker build --no-cache --rm=true -f Dockerfile -t ${DOCKER_REGISTRY}:latest . \ +build-image-inband-nocache: + docker build --no-cache --rm=true -f Dockerfile.inband -t ${DOCKER_REGISTRY}:latest . \ --label org.label-schema.schema-version=1.0 \ --label org.label-schema.vcs-ref=$(GIT_COMMIT_FULL) \ --label org.label-schema.vcs-url=$(REPO) diff --git a/README.md b/README.md index cf8ce9c3..d61432e8 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,67 @@ -Alloy collects and reports hardware inventory inband. +### Alloy - hardware inventory collector. +Alloy collects and publishes server hardware inventory. + +Hardware inventory includes information on the hardware components present on a server, +the firmware versions installed and the component health status. + +Inventory collection with Alloy can be executed in two modes, + - `In band` - the alloy command is executed on the target host OS. + - `Out of band` - the alloy command is executed on a remote system that can reach the target BMC. + +The `outofband` command will cause Alloy to collect inventory from the server BMC. + +The command requires BMC credential information provided by the `-asset-source` flag, +see [examples](examples/assets.csv). + +The command also requires the `-publish-target`, which must be either `stdout` or `serverService`. + +For Alloy internals see [README-development.md](docs/README-development.md) + +##### sample commands + +CSV file asset source with inventory published to stdout +``` +./alloy outofband -asset-source csv \ + -csv-file examples/assets.csv \ + -publish-target stdout ``` -❯ ./alloy inventory -NAME: - alloy inventory - collect inventory -USAGE: - alloy inventory [command options] [arguments...] +CSV file asset source with inventory published to serverService +``` +export SERVERSERVICE_AUTH_TOKEN="hunter2" +export SERVERSERVICE_ENDPOINT="http://127.0.0.1:8000" -OPTIONS: - --component-type value, -t value Component slug to collect inventory for. - --server-url value, -u value server URL to submit inventory. [$SERVER_URL] - --local-file value, -l value write inventory results to local file. - --dry-run, -d collect inventory, skip posting data to server URL. - --verbose, -v Turn on verbose messages for debugging. - -2022/05/13 16:37:29 Required flag "server-url" not set +./alloy outofband -asset-source csv \ + -csv-file examples/assets.csv \ + -publish-target serverService +``` -``` \ No newline at end of file + +EMAPI as an asset source with inventory published to stdout. + +In this case the asset id is passed to the `-list` flag, and the `-config-file` parameter is required. +``` +alloy outofband -asset-source emapi \ + -publish-target stdout \ + -config-file examples/alloy.yaml \ + -list fc167440-18d3-4455-b5ee-1c8e347b3f36 +``` + +### Alloy commands + +``` +❯ ./alloy --help +USAGE + alloy [inband|outofband] [flags] + +SUBCOMMANDS + outofband outofband command collects asset inventory out of band + inband inband command runs on target hardware to collect inventory inband + +FLAGS + -config-file ... Alloy config file + -debug=false Set logging to debug level. + -publish-target ... Publish collected inventory to [serverService|stdout] + -trace=false Set logging to trace level. +``` diff --git a/cli.go b/cli.go deleted file mode 100644 index 09f783ac..00000000 --- a/cli.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "log" - "os" - - "github.com/urfave/cli" -) - -func run() { - collector := &collector{} - - app := cli.NewApp() - - app.Commands = []cli.Command{ - { - Name: "inventory", - Usage: "collect inventory", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "component-type, t", - Usage: "Component slug to collect inventory for.", - Destination: &collector.component, - }, - cli.StringFlag{ - Name: "server-url, u", - Usage: "server URL to submit inventory.", - EnvVar: "SERVER_URL", - Required: true, - Destination: &collector.serverURL, - }, - cli.StringFlag{ - Name: "local-file, l", - Usage: "write inventory results to local file.", - Required: false, - Destination: &collector.localFile, - }, - cli.BoolFlag{ - Name: "dry-run, d", - Usage: "collect inventory, skip posting data to server URL.", - Required: false, - Destination: &collector.dryRun, - }, - cli.BoolFlag{ - Name: "verbose, v", - Usage: "Turn on verbose messages for debugging.", - Required: false, - Destination: &collector.verbose, - }, - }, - - Action: func(c *cli.Context) error { - return collector.inventory() - }, - }, - } - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/inband.go b/cmd/inband.go new file mode 100644 index 00000000..37cecee2 --- /dev/null +++ b/cmd/inband.go @@ -0,0 +1,83 @@ +package cmd + +import ( + "flag" + "fmt" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/collect" + "github.com/metal-toolbox/alloy/internal/publish" + "github.com/peterbourgon/ff/v3/ffcli" + "golang.org/x/net/context" +) + +type inbandCmd struct { + rootCmd *rootCmd +} + +func newInbandCmd(rootCmd *rootCmd) *ffcli.Command { + c := inbandCmd{ + rootCmd: rootCmd, + } + + fs := flag.NewFlagSet("alloy inband", flag.ExitOnError) + rootCmd.RegisterFlags(fs) + + return &ffcli.Command{ + Name: "inband", + ShortUsage: "alloy inband -publish-target", + ShortHelp: "inband command runs on target hardware to collect inventory inband", + FlagSet: fs, + Exec: c.Exec, + } +} + +func (c *inbandCmd) Exec(ctx context.Context, _ []string) error { + alloy, err := app.New(ctx, app.KindOutOfBand, c.rootCmd.cfgFile, c.rootCmd.LogLevel()) + if err != nil { + return err + } + + // setup cancel context with cancel func + ctx, cancelFunc := context.WithCancel(ctx) + + publisher, err := publish.NewStdoutPublisher(ctx, alloy) + if err != nil { + return err + } + + // spawn publisher as a routine + alloy.SyncWg.Add(1) + + go func() { + defer alloy.SyncWg.Done() + + if err := publisher.Run(ctx); err != nil { + alloy.Logger.WithField("err", err).Error("error running inventory publisher routine") + cancelFunc() + } + + fmt.Println("publisher done") + }() + + // routine listens for termination signal + go func() { + <-alloy.TermCh + cancelFunc() + }() + + // spawn out of band collector as a routine + collector := collect.NewInbandCollector(alloy) + if err := collector.Inventory(ctx); err != nil { + alloy.Logger.WithField("err", err).Error("error running inband collector") + } + + fmt.Println("collector done") + + // wait all routines are complete + alloy.Logger.Trace("waiting for routines..") + alloy.SyncWg.Wait() + alloy.Logger.Trace("done..") + + return nil +} diff --git a/cmd/outofband.go b/cmd/outofband.go new file mode 100644 index 00000000..2c85173c --- /dev/null +++ b/cmd/outofband.go @@ -0,0 +1,249 @@ +package cmd + +import ( + "flag" + "log" + "net/http" + "os" + "strings" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/asset" + "github.com/metal-toolbox/alloy/internal/collect" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/metal-toolbox/alloy/internal/publish" + + "github.com/peterbourgon/ff/v3/ffcli" + "github.com/pkg/errors" + "golang.org/x/net/context" +) + +type outOfBandCmd struct { + rootCmd *rootCmd + + // assets source is the cli flag for where assets are to be retrieved from. + // supported sources: csv OR emapi + assetSourceKind string + + // assetSourceCSVFile is required when assetSource is set to csv. + assetSourceCSVFile string + + // assetIDList is a comma separated list of asset IDs to lookup inventory. + assetIDList string + + // assetListAll sets up the asset getter to fetch all assets from the store. + assetListAll bool +} + +func newOutOfBandCmd(rootCmd *rootCmd) *ffcli.Command { + c := outOfBandCmd{ + rootCmd: rootCmd, + } + + fs := flag.NewFlagSet("alloy outofband", flag.ExitOnError) + fs.StringVar(&c.assetSourceKind, "asset-source", "", "Source from where asset information are to be retrieved (csv|emapi)") + fs.StringVar(&c.assetIDList, "list", "", "Collect inventory for the given comma separated list of asset IDs.") + fs.BoolVar(&c.assetListAll, "all", false, "Collect inventory for all assets.") + fs.StringVar(&c.assetSourceCSVFile, "csv-file", "", "Source assets from csv file (required when -asset-source=csv)") + + rootCmd.RegisterFlags(fs) + + return &ffcli.Command{ + Name: "outofband", + ShortUsage: "alloy outofband [-asset-source -list, -all] -publish-target] [-csv-file]", + ShortHelp: "outofband command collects asset inventory out of band", + FlagSet: fs, + Exec: c.Exec, + } +} + +func (c *outOfBandCmd) Exec(ctx context.Context, _ []string) error { + if err := c.validateFlags(); err != nil { + return err + } + + // default collect inventory for all assets + if c.assetIDList == "" { + c.assetListAll = true + } + + // init alloy app + alloy, err := app.New(ctx, app.KindOutOfBand, c.rootCmd.cfgFile, c.rootCmd.LogLevel()) + if err != nil { + return err + } + + // init asset getter + getter, err := c.initAssetGetter(ctx, alloy) + if err != nil { + return err + } + + // init asset publisher + publisher, err := c.initAssetPublisher(ctx, alloy) + if err != nil { + return err + } + + return c.collect(ctx, alloy, getter, publisher) +} + +// initAssetGetter initializes the Asset Getter which retrieves asset information to collect inventory data. +func (c *outOfBandCmd) initAssetGetter(ctx context.Context, alloy *app.App) (asset.Getter, error) { + switch c.assetSourceKind { + case asset.SourceKindCSV: + if c.assetSourceCSVFile != "" { + alloy.Config.AssetGetter.Csv.File = c.assetSourceCSVFile + } + + if alloy.Config.AssetGetter.Csv.File == "" { + return nil, errors.Wrap(model.ErrConfig, "csv asset source requires a csv file parameter") + } + + fh, err := os.Open(c.assetSourceCSVFile) + if err != nil { + return nil, err + } + + // init csv asset source + return asset.NewCSVSource(ctx, alloy, fh) + + case asset.SourceKindEMAPI: + return asset.NewEMAPISource(ctx, alloy) + default: + return nil, errors.Wrap(model.ErrConfig, "unknown asset getter: "+c.assetSourceKind) + } +} + +// initAssetPublisher initializes the inventory publisher. +func (c *outOfBandCmd) initAssetPublisher(ctx context.Context, alloy *app.App) (publish.Publisher, error) { + switch c.rootCmd.publisherKind { + case publish.KindStdout: + return publish.NewStdoutPublisher(ctx, alloy) + case publish.KindServerService: + return publish.NewServerServicePublisher(ctx, alloy) + default: + return nil, errors.Wrap(model.ErrConfig, "unknown inventory publisher: "+c.rootCmd.publisherKind) + } +} + +// validateFlags checks expected outofband flag parameters are valid +func (c *outOfBandCmd) validateFlags() error { + if err := c.validateFlagSource(); err != nil { + return err + } + + if err := c.validateFlagPublish(); err != nil { + return err + } + + return nil +} + +// validateFlagSource checks the -asset-source flag parameter values are as expected. +func (c *outOfBandCmd) validateFlagSource() error { + switch c.assetSourceKind { + case asset.SourceKindEMAPI: + if c.rootCmd.cfgFile == "" { + return errors.Wrap( + model.ErrConfig, + "-asset-source=emapi requires a valid config file parameter -config-file", + ) + } + + case asset.SourceKindCSV: + if c.assetSourceCSVFile == "" { + return errors.Wrap( + errParseCLIParam, + "-asset-source=csv requires parameter -csv-file", + ) + } + + default: + return errors.Wrap( + errParseCLIParam, + "invalid -asset-source parameter, accepted values are csv OR emapi", + ) + } + + return nil +} + +// validateFlagPublish checks the -publish flag parameter values are as expected. +func (c *outOfBandCmd) validateFlagPublish() error { + switch c.rootCmd.publisherKind { + case publish.KindServerService, publish.KindStdout: + return nil + default: + return errors.Wrap( + errParseCLIParam, + "-publish parameter required, accepted values are stdout OR serverService", + ) + } +} + +// collect runs the asset getter, publisher and collects inventory out of band +func (c *outOfBandCmd) collect(ctx context.Context, alloy *app.App, getter asset.Getter, publisher publish.Publisher) error { + go func() { + log.Println(http.ListenAndServe("localhost:9091", nil)) + }() + + // setup cancel context with cancel func + ctx, cancelFunc := context.WithCancel(ctx) + + // spawn asset getter as a routine + alloy.SyncWg.Add(1) + + go func() { + defer alloy.SyncWg.Done() + + if c.assetIDList != "" { + if err := getter.ListByIDs(ctx, strings.Split(c.assetIDList, ",")); err != nil { + alloy.Logger.WithField("err", err).Error("error running asset getter routine") + cancelFunc() + } + } else { + if err := getter.ListAll(ctx); err != nil { + alloy.Logger.WithField("err", err).Error("error running asset getter routine") + cancelFunc() + } + } + + alloy.Logger.Trace("getter done") + }() + + // spawn publisher as a routine + alloy.SyncWg.Add(1) + + go func() { + defer alloy.SyncWg.Done() + + if err := publisher.Run(ctx); err != nil { + alloy.Logger.WithField("err", err).Error("error running inventory publisher routine") + cancelFunc() + } + + alloy.Logger.Trace("publisher done") + }() + + // routine listens for termination signal + go func() { + <-alloy.TermCh + cancelFunc() + }() + + // spawn out of band collector as a routine + collector := collect.NewOutOfBandCollector(alloy) + if err := collector.Inventory(ctx); err != nil { + alloy.Logger.WithField("err", err).Error("error running outofband collector") + } + + alloy.Logger.Trace("collector done") + + // wait all routines are complete + alloy.Logger.Trace("waiting for routines..") + alloy.SyncWg.Wait() + alloy.Logger.Trace("done..") + + return nil +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 00000000..2e3515b3 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,89 @@ +package cmd + +import ( + "flag" + "fmt" + "os" + + "github.com/metal-toolbox/alloy/internal/model" + "github.com/pkg/errors" + + "github.com/peterbourgon/ff/v3/ffcli" + "golang.org/x/net/context" +) + +var ( + errParseCLIParam = errors.New("parameter parse failed") +) + +// Run is the main command entry point, all sub commands are registered here +func Run() { + var ( + cmd, cfg = newRootCmd() + outOfBandCmd = newOutOfBandCmd(cfg) + inbandCmd = newInbandCmd(cfg) + ) + + cmd.Subcommands = append(cmd.Subcommands, outOfBandCmd, inbandCmd) + + if err := cmd.Parse(os.Args[1:]); err != nil { + fmt.Fprintf(os.Stderr, "error in cli parse: %v\n", err) + os.Exit(1) + } + + if err := cmd.Run(context.Background()); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +} + +// rootCmd is the cli root command instance, it holds attributes available to subcommands +type rootCmd struct { + // cfgFile is the configuration file + cfgFile string + // publisherKind is where collected inventory is published + // stdout OR csv + publisherKind string + + // flag sets trace log level + trace bool + // flag sets debug log level + debug bool +} + +func (c *rootCmd) RegisterFlags(fs *flag.FlagSet) { + fs.BoolVar(&c.debug, "debug", false, "Set logging to debug level.") + fs.BoolVar(&c.trace, "trace", false, "Set logging to trace level.") + fs.StringVar(&c.cfgFile, "config-file", "", "Alloy config file") + fs.StringVar(&c.publisherKind, "publish-target", "", "Publish collected inventory to [serverService|stdout]") +} + +func (c *rootCmd) Exec(context.Context, []string) error { + return flag.ErrHelp +} + +func (c *rootCmd) LogLevel() int { + switch { + case c.debug: + return model.LogLevelDebug + case c.trace: + return model.LogLevelTrace + default: + return model.LogLevelInfo + } +} + +func newRootCmd() (*ffcli.Command, *rootCmd) { + var c rootCmd + + fs := flag.NewFlagSet("alloy", flag.ExitOnError) + c.RegisterFlags(fs) + + return &ffcli.Command{ + Name: "alloy", + ShortHelp: "alloy collects device inventory attributes", + ShortUsage: "alloy [inband|outofband] [flags]", + FlagSet: fs, + Exec: c.Exec, + }, &c +} diff --git a/collector.go b/collector.go deleted file mode 100644 index b7a2e559..00000000 --- a/collector.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/metal-toolbox/ironlib" - "github.com/sirupsen/logrus" -) - -// collector collects hardware inventory -type collector struct { - component string - serverURL string - localFile string - verbose bool - dryRun bool -} - -func (*collector) inventory() error { - logger := logrus.New() - - device, err := ironlib.New(logger) - if err != nil { - logger.Fatal(err) - } - - inv, err := device.GetInventory(context.TODO()) - if err != nil { - logger.Fatal(err) - } - - j, err := json.MarshalIndent(inv, " ", " ") - if err != nil { - logger.Fatal(err) - } - - fmt.Println(j) - - return nil -} diff --git a/docs/README-development.md b/docs/README-development.md new file mode 100644 index 00000000..0c578e2c --- /dev/null +++ b/docs/README-development.md @@ -0,0 +1,54 @@ +### Alloy software components + +Alloy is internally composed of three main components, +the `Asset getter`, `Inventory collector` and `Inventory publisher`. + +![Alloy software components](alloy_components.png) + +#### Asset getter + +The asset getter (only) runs in the `Out of band` mode - that is when, +Alloy is invoked with the `outofband` command and BMC credentials are required +for remote inventory collection. + +The assets getter then sends the assets it retrieved as `model.Asset` +on the `asset channel`, for the `Inventory collector` component. + +Asset getters implement the `Getter` interface, at the time of writing +Alloy comes with a `CSV` and an `EMAPI` asset getter. + +#### Inventory collector + +The `Inventory collector` listens for devices on the asset channel (from the asset getter), +and proceeds to collect inventory for the devices received - through `In band` or `Out of band`. + +The collector then sends the collected inventory as an `model.AssetDevice` on the +`collector channel`, for the `Inventory publisher` component. + +Inventory collectors implement the `Collector` interface. + +###### In band collection + +In band inventory is collected when alloy is invoked with the `inband` command, +this calls into the [ironlib](https://github.com/metal-toolbox/ironlib) library +which abstracts the hardware/vendor specific data collection through a host OS. + +![Alloy software components](alloy_inband.png) + + +###### Out of band collection + +Out of band inventory is collected when Alloy is invoked with the `out of band` +command, which calls into the [bmclib](https://github.com/bmc-toolbox/bmclib/) +library, which abstracts hardware/vendor specific data collection remotely through the +BMC. + + +![Alloy software components](alloy_oob.png) + +#### Inventory publisher + +The `Inventory publisher` listens for asset devices from the inventory collector, +and proceeds to transform the data for publishing, and then publishes the data. + +Inventory publishers implement the `Publisher` interface. diff --git a/docs/README-diagrams.md b/docs/README-diagrams.md new file mode 100644 index 00000000..f09aef5a --- /dev/null +++ b/docs/README-diagrams.md @@ -0,0 +1,2 @@ +The diagram images included in this directory are drawn and maintained in the Miro board - [Alloy]( +https://miro.com/app/board/uXjVOjskT6c=/) diff --git a/docs/alloy_components.png b/docs/alloy_components.png new file mode 100644 index 00000000..d573d8a4 Binary files /dev/null and b/docs/alloy_components.png differ diff --git a/docs/alloy_inband.png b/docs/alloy_inband.png new file mode 100644 index 00000000..cf9922a6 Binary files /dev/null and b/docs/alloy_inband.png differ diff --git a/docs/alloy_oob.png b/docs/alloy_oob.png new file mode 100644 index 00000000..8a9921b9 Binary files /dev/null and b/docs/alloy_oob.png differ diff --git a/examples/alloy.yaml b/examples/alloy.yaml new file mode 100644 index 00000000..6c30d932 --- /dev/null +++ b/examples/alloy.yaml @@ -0,0 +1,25 @@ +app_kind: outofband +collector_outofband: + # routines to spawn to collect oob inventory + concurrency: 5 +# stdout OR serverService (https://github.com/metal-toolbox/hollow-serverservice) +#inventory_publisher: stdout +inventory_publisher: serverService +# csv OR emapi +asset_getter: emapi +serverService: + endpoint: http://localhost:8080 + authToken: "" + concurrency: 10 +emapi: + auth_token: + consumer_token: + endpoint: https://foo.bar + facility: dc13 + # concurrent emapi requests + concurrency: 5 + # hardware items retrieved per emapi request + batch_size: 5 + # custom HTTP headers to be included in the emapi request + custom_headers: + X-Is-Boss: 1 diff --git a/examples/assets.csv b/examples/assets.csv new file mode 100644 index 00000000..f4c88f4c --- /dev/null +++ b/examples/assets.csv @@ -0,0 +1,2 @@ +id,ipaddress,username,password +f00b000-18d3-4455-b5ee-1c8e347b3f36, 192.168.1.1, root, calvin diff --git a/go.mod b/go.mod index 9f80b226..118466ce 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,90 @@ module github.com/metal-toolbox/alloy -go 1.17 +go 1.18 -require github.com/urfave/cli v1.22.9 +require ( + github.com/bmc-toolbox/bmclib/v2 v2.0.0 + github.com/bmc-toolbox/common v0.0.0-20220707135204-5368ecd5d175 + github.com/bombsimon/logrusr/v2 v2.0.1 + github.com/gammazero/workerpool v1.1.2 + github.com/google/uuid v1.3.0 + github.com/hashicorp/go-retryablehttp v0.7.1 + github.com/jinzhu/copier v0.3.5 + github.com/metal-toolbox/ironlib v0.1.1-staging.0.20220620125540-b6f10fbbba26 + github.com/peterbourgon/ff/v3 v3.1.2 + github.com/pkg/errors v0.9.1 + github.com/r3labs/diff/v3 v3.0.0 + github.com/sirupsen/logrus v1.8.1 + github.com/spf13/viper v1.12.0 + github.com/stretchr/testify v1.7.4 + go.hollow.sh/serverservice v0.13.5 + golang.org/x/net v0.0.0-20220615171555-694bf12d69de + gotest.tools v2.2.0+incompatible +) require ( github.com/beevik/etree v1.1.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dselans/dmidecode v0.0.0-20180814053009-65c3f9d81910 // indirect - github.com/golang/protobuf v1.3.1 // indirect - github.com/metal-toolbox/ironlib v0.0.0-20220513131631-9be17392c451 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/ericlagergren/decimal v0.0.0-20181231230500-73749d4874d5 // indirect + github.com/friendsofgo/errors v0.9.2 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/gammazero/deque v0.1.0 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.8.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.10.0 // indirect + github.com/goccy/go-json v0.9.7 // indirect + github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jacobweinstock/registrar v0.4.6 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/lib/pq v1.10.6 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/r3labs/diff/v2 v2.15.1 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cobra v1.4.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stmcginnis/gofish v0.13.0 // indirect + github.com/subosito/gotenv v1.3.0 // indirect github.com/tidwall/gjson v1.14.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + github.com/volatiletech/inflect v0.0.1 // indirect + github.com/volatiletech/null/v8 v8.1.2 // indirect + github.com/volatiletech/randomize v0.0.1 // indirect + github.com/volatiletech/sqlboiler/v4 v4.11.0 // indirect + github.com/volatiletech/strmangle v0.0.4 // indirect + go.hollow.sh/toolbox v0.1.5 // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/text v0.3.7 // indirect - google.golang.org/appengine v1.6.6 // indirect + golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/ini.v1 v1.66.4 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6146b089..72eca1c3 100644 --- a/go.sum +++ b/go.sum @@ -1,55 +1,1046 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= +github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apmckinlay/gsuneido v0.0.0-20180907175622-1f10244968e3/go.mod h1:hJnaqxrCRgMCTWtpNz9XUFkBCREiQdlcyK6YNmOfroM= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bmc-toolbox/bmclib/v2 v2.0.0 h1:4iYc/TMErQ2a7geubO1W/pJZk/MhORLieQvk9m3AWLg= +github.com/bmc-toolbox/bmclib/v2 v2.0.0/go.mod h1:J8VqwJ83ciXbN4IimBYS/voa6+8GjJhzKFOXK4mLJdw= +github.com/bmc-toolbox/common v0.0.0-20220707135204-5368ecd5d175 h1:sBkvK0BfJqFEJ3OMNqmXc9iAutobuHhv3/QvAt+b2YU= +github.com/bmc-toolbox/common v0.0.0-20220707135204-5368ecd5d175/go.mod h1:SY//n1PJjZfbFbmAsB6GvEKbc7UXz3d30s3kWxfJQ/c= +github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM= +github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go/v2 v2.2.13 h1:IsQmOtHQrfv0v3AqIEv+mStB09amzbH8gDDyVcpl3xQ= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dselans/dmidecode v0.0.0-20180814053009-65c3f9d81910 h1:w9T/rS5VP0SPXqYr7BpUeqf6ukp/GEWm6nXhziWzBY4= github.com/dselans/dmidecode v0.0.0-20180814053009-65c3f9d81910/go.mod h1:yGxJ4za56u74+F00gmg9RJoyXLzvrOrIat4b/Dgw9Lo= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ericlagergren/decimal v0.0.0-20181231230500-73749d4874d5 h1:HQGCJNlqt1dUs/BhtEKmqWd6LWS+DWYVxi9+Jo4r0jE= +github.com/ericlagergren/decimal v0.0.0-20181231230500-73749d4874d5/go.mod h1:1yj25TwtUlJ+pfOu9apAVaM1RWfZGg+aFpd4hPQZekQ= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk= +github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik= +github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= +github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= +github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-contrib/zap v0.0.2 h1:VnIucI+kUsxgzmcrX0gMk19a2I12KirTxi+ufuT2xZk= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/metal-toolbox/ironlib v0.0.0-20220513131631-9be17392c451 h1:F5YOcFz88G3j/BmmstwdgUyxk70cPO9AnT5mbC9+N9k= -github.com/metal-toolbox/ironlib v0.0.0-20220513131631-9be17392c451/go.mod h1:KBIWTZR2FSPkdPfNrvrka1wglKoJl0zMPdLEdPv/obc= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= +github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= +github.com/jacobweinstock/registrar v0.4.6 h1:0O3g2jT2Lx+Bf+yl4QsMUN48fVZxUpM3kS+NtIJ+ucw= +github.com/jacobweinstock/registrar v0.4.6/go.mod h1:IDx65tQ7DLJ2UqiVjE1zo74jMZZfel9YZW8VrC26m6o= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.1-0.20191011153232-f91d3411e481/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= +github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= +github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/metal-toolbox/ironlib v0.1.1-staging.0.20220620125540-b6f10fbbba26 h1:WyAA/M5VTF/YEd/YGaJy0/WGY0suu4G1H4k7UHHVbEY= +github.com/metal-toolbox/ironlib v0.1.1-staging.0.20220620125540-b6f10fbbba26/go.mod h1:fADbYgPiBep99SDckbH6OefRraTTdgp8TaAstn5WB4Y= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/peterbourgon/ff/v3 v3.1.2 h1:0GNhbRhO9yHA4CC27ymskOsuRpmX0YQxwxM9UPiP6JM= +github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/r3labs/diff/v3 v3.0.0 h1:ZhPwNxn9gW5WLPBV9GCYaVbMdLOSmJ0DeKdCiSbOLUI= +github.com/r3labs/diff/v3 v3.0.0/go.mod h1:wCkTySAiDnZao1sZrVTDIzuzgLZ+cNPGn3LC8DlIg5g= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= +github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= +github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/stmcginnis/gofish v0.13.0 h1:qq6q3yNt9vw7ZuJxiw87hq9+BdPLsuRQBwl+XoZSz60= +github.com/stmcginnis/gofish v0.13.0/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= +github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo= github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= -github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/volatiletech/inflect v0.0.1 h1:2a6FcMQyhmPZcLa+uet3VJ8gLn/9svWhJxJYwvE8KsU= +github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= +github.com/volatiletech/null/v8 v8.1.2 h1:kiTiX1PpwvuugKwfvUNX/SU/5A2KGZMXfGD0DUHdKEI= +github.com/volatiletech/null/v8 v8.1.2/go.mod h1:98DbwNoKEpRrYtGjWFctievIfm4n4MxG0A6EBUcoS5g= +github.com/volatiletech/randomize v0.0.1 h1:eE5yajattWqTB2/eN8df4dw+8jwAzBtbdo5sbWC4nMk= +github.com/volatiletech/randomize v0.0.1/go.mod h1:GN3U0QYqfZ9FOJ67bzax1cqZ5q2xuj2mXrXBjWaRTlY= +github.com/volatiletech/sqlboiler/v4 v4.11.0 h1:jItTUGIXfCfFiNEGIBZZj4rFMO/gXhjqX03sJ5LiDk8= +github.com/volatiletech/sqlboiler/v4 v4.11.0/go.mod h1:AAaQj77uX6nyU+Q5q6OcVCFFEs/gs+qsthM18/NVemo= +github.com/volatiletech/strmangle v0.0.1/go.mod h1:F6RA6IkB5vq0yTG4GQ0UsbbRcl3ni9P76i+JrTBKFFg= +github.com/volatiletech/strmangle v0.0.4 h1:CxrEPhobZL/PCZOTDSH1aq7s4Kv76hQpRoTVVlUOim4= +github.com/volatiletech/strmangle v0.0.4/go.mod h1:ycDvbDkjDvhC0NUU8w3fWwl5JEMTV56vTKXzR3GeR+0= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.hollow.sh/serverservice v0.13.5 h1:Iv/gaU0A1WFAaZ0zsuxi3Juk8Ga644StHtolzaa8uMg= +go.hollow.sh/serverservice v0.13.5/go.mod h1:+pds67a/R3oikKVPnXPbxE7VCsQiTUvT1yrXB5rWVXA= +go.hollow.sh/toolbox v0.1.5 h1:6cyfndvARl63kjLc+AIx3EQ3GipFqk6iLDBg2s6HffM= +go.hollow.sh/toolbox v0.1.5/go.mod h1:5ardda6GhoTrhgocY7tW4ri8auq/FhK+jvjIWf+7FXk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.32.0 h1:ht6IqV6njVN4cMHYpN7pX5oDXZqGtl4fqvbGax1QFNU= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220615171555-694bf12d69de h1:ogOG2+P6LjO2j55AkRScrkB2BFpd+Z8TY2wcM0Z3MGo= +golang.org/x/net v0.0.0-20220615171555-694bf12d69de/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= +gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= +modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= +modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= +modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= +modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6Q= +modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= +modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= +modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= +modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= +modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.14.5/go.mod h1:YyX5Rx0WbXokitdWl2GJIDy4BrPxBP0PwwhpXOHCDLE= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.10.0/go.mod h1:WzWapmP/7dHVhFoyPpEaNSVTL8xtewhouN/cqSJ5A2s= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.2.21/go.mod h1:uXrObx4pGqXWIMliC5MiKuwAyMrltzwpteOFUP1PWCc= +modernc.org/z v1.3.0/go.mod h1:+mvgLH814oDjtATDdT3rs84JnUIpkvAF5B8AVkNlE2g= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 00000000..98e5bc90 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,120 @@ +package app + +import ( + "context" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/metal-toolbox/alloy/internal/model" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +const ( + KindInband = "inband" + KindOutOfBand = "outofband" +) + +var ( + ErrAppInit = errors.New("error initializing app") +) + +// App holds attributes for running alloy. +type App struct { + // App configuration. + Config *model.Config + // AssetCh is where the asset getter retrieves assets from the asset store for the inventory collector to consume. + AssetCh chan *model.Asset + // CollectorCh is where the asset inventory information is written, for the publisher to consume. + CollectorCh chan *model.AssetDevice + // TermCh is the channel to terminate the app based on a signal + TermCh chan os.Signal + // Sync waitgroup to wait for running go routines on termination. + SyncWg *sync.WaitGroup + // Logger is the app logger + Logger *logrus.Logger +} + +// New returns a new alloy application object with the configuration loaded +func New(ctx context.Context, kind, cfgFile string, loglevel int) (app *App, err error) { + switch kind { + case KindInband, KindOutOfBand: + default: + return nil, errors.Wrap(ErrAppInit, "invalid app kind: "+kind) + } + + cfg, err := configLoad(kind, cfgFile) + if err != nil { + return nil, errors.Wrap(ErrAppInit, "config load error: "+err.Error()) + } + + cfg.AppKind = kind + + app = &App{ + Config: cfg, + AssetCh: make(chan *model.Asset), + CollectorCh: make(chan *model.AssetDevice), + TermCh: make(chan os.Signal), + SyncWg: &sync.WaitGroup{}, + Logger: logrus.New(), + } + + switch loglevel { + case model.LogLevelDebug: + app.Logger.Level = logrus.DebugLevel + case model.LogLevelTrace: + app.Logger.Level = logrus.TraceLevel + default: + app.Logger.Level = logrus.InfoLevel + } + + app.Logger.SetFormatter(&logrus.JSONFormatter{}) + + // register for SIGINT, SIGTERM + signal.Notify(app.TermCh, syscall.SIGINT, syscall.SIGTERM) + + return app, nil +} + +func configLoad(kind, cfgFile string) (config *model.Config, err error) { + cfg := configDefault() + + if cfgFile == "" { + return cfg, nil + } + + cfg.File = cfgFile + viper.SetConfigFile(cfgFile) + + err = viper.ReadInConfig() + if err != nil { + return nil, err + } + + err = viper.Unmarshal(cfg) + if err != nil { + return nil, err + } + + return cfg, nil +} + +// configDefault returns a default app configuration +func configDefault() *model.Config { + return &model.Config{ + AppKind: KindInband, + LogLevel: 0, + } +} + +// NewLogrusEntryFromLogger returns a logger contextualized with the given logrus fields. +func NewLogrusEntryFromLogger(fields logrus.Fields, logger *logrus.Logger) *logrus.Entry { + l := logrus.New() + l.Formatter = logger.Formatter + l.Level = logger.Level + + return logger.WithFields(fields) +} diff --git a/internal/asset/csv.go b/internal/asset/csv.go new file mode 100644 index 00000000..337166dc --- /dev/null +++ b/internal/asset/csv.go @@ -0,0 +1,154 @@ +package asset + +import ( + "context" + "encoding/csv" + "io" + "net" + "strings" + "sync" + + "github.com/google/uuid" + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + // SourceKindCSV identifies a csv asset source + SourceKindCSV = "csv" +) + +var ( + ErrCSVSource = errors.New("error in CSV") +) + +type csvee struct { + csvReader io.ReadCloser + logger *logrus.Entry + config *model.Config + syncWg *sync.WaitGroup + assetCh chan<- *model.Asset +} + +// NewCSVSource returns a new csv asset sourcer to retrieve asset information from a CSV file for inventory collection. +func NewCSVSource(ctx context.Context, alloy *app.App, csvReader io.ReadCloser) (Getter, error) { + return &csvee{ + logger: alloy.Logger.WithField("component", "getter.csv"), + syncWg: alloy.SyncWg, + config: alloy.Config, + assetCh: alloy.AssetCh, + csvReader: csvReader, + }, nil +} + +// SetClient satisfies the Getter interface +func (c *csvee) SetClient(client interface{}) { +} + +// ListAll runs the csv asset getter which returns all assets on the assetCh +func (c *csvee) ListAll(ctx context.Context) error { + // channel close tells the channel reader we're done + defer close(c.assetCh) + + c.logger.Trace("load assets") + + assets, err := c.loadAssets(ctx, c.csvReader) + if err != nil { + return err + } + + c.logger.Trace("list all assets") + + for _, asset := range assets { + c.assetCh <- asset + } + + return nil +} + +// ListByIDs runs the csv asset getter which returns on the assetCh +func (c *csvee) ListByIDs(ctx context.Context, assetIDs []string) error { + // channel close tells the channel reader we're done + defer close(c.assetCh) + + assets, err := c.loadAssets(ctx, c.csvReader) + if err != nil { + return err + } + + for _, asset := range assets { + if sliceContains(assetIDs, asset.ID) { + c.assetCh <- asset + } + } + + return nil +} + +func sliceContains(sl []string, str string) bool { + for _, elem := range sl { + if elem == str { + return true + } + } + + return false +} + +// loadAssets returns a slice of assets from the given csv io.Reader +func (c *csvee) loadAssets(ctx context.Context, csvReader io.ReadCloser) ([]*model.Asset, error) { + records, err := csv.NewReader(csvReader).ReadAll() + if err != nil { + return nil, err + } + + defer csvReader.Close() + + assets := []*model.Asset{} + + // csv is of the format + // uuid, IP address, username, password + for idx, rec := range records { + // skip csv header + if idx == 0 { + continue + } + + id := strings.TrimSpace(rec[0]) + ip := strings.TrimSpace(rec[1]) + username := strings.TrimSpace(rec[2]) + password := strings.TrimSpace(rec[3]) + + _, err := uuid.Parse(strings.TrimSpace(id)) + if err != nil { + return nil, errors.Wrap(ErrCSVSource, err.Error()+": "+id) + } + + parsedIP := net.ParseIP(ip) + if parsedIP == nil { + return nil, errors.Wrap(ErrCSVSource, "invalid IP address: "+ip) + } + + if username == "" { + return nil, errors.Wrap(ErrCSVSource, "invalid username string") + } + + if password == "" { + return nil, errors.Wrap(ErrCSVSource, "invalid password string") + } + + assets = append( + assets, + &model.Asset{ + ID: id, + BMCUsername: username, + BMCPassword: password, + BMCAddress: parsedIP, + }, + ) + } + + return assets, nil +} diff --git a/internal/asset/csv_test.go b/internal/asset/csv_test.go new file mode 100644 index 00000000..3a8e82ac --- /dev/null +++ b/internal/asset/csv_test.go @@ -0,0 +1,98 @@ +package asset + +import ( + "bytes" + "context" + "io" + "net" + "sync" + "testing" + "time" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/stretchr/testify/assert" +) + +// This type exists to allow the csv bytes to be passed around as a io.ReadCloser +// which is expected by the csv asset loader +type ioReaderFakeCloser struct { + io.Reader +} + +func (i ioReaderFakeCloser) Close() error { + return nil +} + +func Test_RunCSVListAll(t *testing.T) { + // init alloy app + alloy, err := app.New(context.TODO(), app.KindOutOfBand, "", model.LogLevelInfo) + if err != nil { + t.Fatal(err) + } + + got := []*model.Asset{} + expected := []*model.Asset{ + { + ID: "070db820-d807-013a-c0bf-3e22fbc86c7a", + BMCUsername: "foo", + BMCPassword: "bar", + BMCAddress: net.ParseIP("127.0.0.1"), + }, + { + ID: "050db820-c807-013a-c0bf-3e22fbc86c5a", + BMCUsername: "admin", + BMCPassword: "hunter2", + BMCAddress: net.ParseIP("127.0.0.2"), + }, + } + + var collectTimedOut bool + + // background routine to receive asset objects objects sent from the asset getter + // mocks a collector + alloy.SyncWg.Add(1) + + go func(t *testing.T, wg *sync.WaitGroup) { + t.Helper() + + defer wg.Done() + + timeout := time.NewTicker(time.Second * 5).C + Loop: + for { + select { + case asset, ok := <-alloy.AssetCh: + if !ok { + break Loop + } + got = append(got, asset) + case <-timeout: + collectTimedOut = true + break Loop + } + } + }(t, alloy.SyncWg) + + csvee := []byte(`id,ipaddress,username,password +070db820-d807-013a-c0bf-3e22fbc86c7a,127.0.0.1,foo,bar +050db820-c807-013a-c0bf-3e22fbc86c5a,127.0.0.2,admin,hunter2`) + + // init asset getter + csvs, err := NewCSVSource(context.TODO(), alloy, ioReaderFakeCloser{bytes.NewReader(csvee)}) + if err != nil { + t.Fatal(err) + } + + err = csvs.ListAll(context.TODO()) + if err != nil { + t.Fatal(err) + } + + // wait for routines to complete + alloy.SyncWg.Wait() + + // test inventory items match expected + assert.ElementsMatch(t, expected, got) + assert.False(t, collectTimedOut) +} diff --git a/internal/asset/emapi.go b/internal/asset/emapi.go new file mode 100644 index 00000000..d88c8c29 --- /dev/null +++ b/internal/asset/emapi.go @@ -0,0 +1,524 @@ +package asset + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net" + "net/http" + "net/url" + "os" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/hashicorp/go-retryablehttp" + + "github.com/gammazero/workerpool" + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + // default number of concurrent requests + concurrency = 5 + // delay between EMAPI requests + delayBetweenRequests = 2 * time.Second + // SourceKindEMAPI identifies an Equinic metal API asset getter. + SourceKindEMAPI = "emapi" +) + +var ( + ErrEMAPI = errors.New("emapi error") + + // default count of assets to retrieve per request + batchSize = 5 +) + +// emapi is an asset sourcer +type emapi struct { + client emapiRequestor + logger *logrus.Entry + config *model.Config + syncWg *sync.WaitGroup + assetCh chan<- *model.Asset + workers *workerpool.WorkerPool +} + +// NewEMAPISource returns a new emapi asset sourcer to retieve asset information from EMAPI for inventory collection. +func NewEMAPISource(ctx context.Context, alloy *app.App) (Getter, error) { + client, err := newEMAPIClient(alloy.Config, alloy.Logger) + if err != nil { + return nil, err + } + + e := &emapi{ + logger: alloy.Logger.WithField("component", "getter.emapi"), + syncWg: alloy.SyncWg, + config: alloy.Config, + assetCh: alloy.AssetCh, + client: client, + } + + e.workers = workerpool.New(alloy.Config.AssetGetter.Emapi.Concurrency) + + return e, nil +} + +func (e *emapi) SetClient(client interface{}) { + e.client = client.(emapiRequestor) +} + +func (e *emapi) ListByIDs(ctx context.Context, assetIDs []string) error { + // close assetCh to notify consumers + defer close(e.assetCh) + + // channel for routines spawned to indicate completion + doneCh := make(chan struct{}) + + // count of routines spawned to retrieve assets + var dispatched int32 + + // submit inventory collection to worker pool + for _, assetID := range assetIDs { + assetID := assetID + + // increment wait group + e.syncWg.Add(1) + + // increment spawned count + atomic.AddInt32(&dispatched, 1) + + e.workers.Submit( + func() { + defer e.syncWg.Done() + defer func() { doneCh <- struct{}{} }() + + // lookup asset by its ID from the inventory asset store + asset, err := e.client.AssetByID(ctx, assetID) + if err != nil { + e.logger.Warn(err) + } + + // send asset for inventory collection + e.assetCh <- asset + }, + ) + } + + for dispatched > 0 { + <-doneCh + atomic.AddInt32(&dispatched, ^int32(0)) + } + + return nil +} + +func (e *emapi) ListAll(ctx context.Context) error { + // close assetCh to notify consumers + defer close(e.assetCh) + + // channel for routines spawned to indicate completion + doneCh := make(chan struct{}) + + // count of routines spawned to retrieve assets + var dispatched int32 + + // increment wait group + e.syncWg.Add(1) + + // increment spawned count + atomic.AddInt32(&dispatched, 1) + + func(dispatched *int32) { + e.workers.Submit( + + func() { + defer e.syncWg.Done() + defer func() { doneCh <- struct{}{} }() + + err := e.dispatcher(ctx, dispatched, doneCh) + if err != nil { + e.logger.Warn(err) + } + }, + ) + }(&dispatched) + + for dispatched > 0 { + <-doneCh + atomic.AddInt32(&dispatched, ^int32(0)) + } + + return nil +} + +// dispatcher queries EMAPI for total assets and spawns workers to retrieve asset information +// +// nolint:gocyclo // this method has various cases to consider and shared context information which is ideal to keep together. +func (e *emapi) dispatcher(ctx context.Context, dispatched *int32, doneCh chan<- struct{}) error { + // first request to figures out total items + beginOffset := 1 + + assets, total, err := e.client.AssetsByOffsetLimit(ctx, beginOffset, 1) + if err != nil { + return err + } + + // submit the assets collected in the first request + for _, asset := range assets { + e.assetCh <- asset + } + + if total == 1 { + return nil + } + + var finalBatch bool + + // continue from offset 2 + beginOffset = 2 + for idx := beginOffset; idx <= total; { + // final batch + if total > batchSize && total-beginOffset <= batchSize { + batchSize = total - beginOffset + finalBatch = true + } + + for e.workers.WaitingQueueSize() > 0 { + if ctx.Err() != nil { + break + } + + e.logger.WithFields(logrus.Fields{ + "component": "asset getter", + "queue size": e.workers.WaitingQueueSize(), + "concurrency": e.config.AssetGetter.Emapi.Concurrency, + }).Debug("delay for queue size to drop..") + + // nolint:gomnd // delay is a magic number + time.Sleep(5 * time.Second) + } + + // increment wait group + e.syncWg.Add(1) + + // increment spawned count + atomic.AddInt32(dispatched, 1) + + // pause between spawning workers - skip delay for tests + if os.Getenv("TEST_ENV") == "" { + time.Sleep(delayBetweenRequests) + } + + // spawn worker with the offset, limit parameters + // this is done within a closure to capture the offset, limit values + func(offset, limit int) { + e.workers.Submit( + func() { + defer e.syncWg.Done() + defer func() { doneCh <- struct{}{} }() + + e.logger.WithFields(logrus.Fields{ + "offset": offset, + "limit": limit, + }).Trace() + + assets, _, err := e.client.AssetsByOffsetLimit(ctx, offset, limit) + if err != nil { + e.logger.Warn(err) + } + + for _, asset := range assets { + e.assetCh <- asset + } + }, + ) + }(beginOffset, batchSize) + + if !finalBatch { + idx += batchSize + beginOffset = idx + } else { + break + } + } + + return nil +} + +type emapiRequestor interface { + // AssetByID queries EMAPI for an asset and returns its model.Asset equivalent object. + AssetByID(ctx context.Context, id string) (asset *model.Asset, err error) + AssetsByOffsetLimit(ctx context.Context, offset, limit int) (assets []*model.Asset, total int, err error) +} + +type emapiClient struct { + client *retryablehttp.Client + endpoint *url.URL + customHeaders map[string]string + logger *logrus.Logger + authToken string + consumerToken string + facility string +} + +// newEMAPIClient validates the EMAPI configuration object and returns a emapiClient object +// +// nolint:gocyclo // validation is cyclomatic +func newEMAPIClient(cfg *model.Config, logger *logrus.Logger) (*emapiClient, error) { + if cfg == nil { + return nil, errors.Wrap(ErrConfig, "expected valid Config object, got nil") + } + + // env var auth token + if authToken := os.Getenv("EMAPI_AUTH_TOKEN"); authToken != "" { + cfg.AssetGetter.Emapi.AuthToken = authToken + } + + if cfg.AssetGetter.Emapi.AuthToken == "" { + return nil, errors.Wrap(ErrConfig, "expected valid emapi auth token, got empty") + } + + // env var consumer token + if consumerToken := os.Getenv("EMAPI_CONSUMER_TOKEN"); consumerToken != "" { + cfg.AssetGetter.Emapi.ConsumerToken = consumerToken + } + + if cfg.AssetGetter.Emapi.ConsumerToken == "" { + return nil, errors.Wrap(ErrConfig, "expected valid emapi consumer token, got empty") + } + + if cfg.AssetGetter.Emapi.Facility == "" { + return nil, errors.Wrap(ErrConfig, "expected valid emapi facility, got empty") + } + + // env var endpoint + if endpoint := os.Getenv("EMAPI_ENDPOINT"); endpoint != "" { + cfg.AssetGetter.Emapi.Endpoint = endpoint + } + + endpoint, err := url.Parse(cfg.AssetGetter.Emapi.Endpoint) + if err != nil { + return nil, errors.Wrap(ErrConfig, "error in emapi endpoint URL: "+err.Error()) + } + + if cfg.AssetGetter.Emapi.BatchSize == 0 { + cfg.AssetGetter.Emapi.BatchSize = batchSize + } + + if cfg.AssetGetter.Emapi.Concurrency == 0 { + cfg.AssetGetter.Emapi.Concurrency = concurrency + } + + // init retryable http client + retryableClient := retryablehttp.NewClient() + + // disable default debug logging on the retryable client + if logger.Level < logrus.DebugLevel { + retryableClient.Logger = nil + } else { + retryableClient.Logger = logger + } + + return &emapiClient{ + client: retryableClient, + endpoint: endpoint, + authToken: cfg.AssetGetter.Emapi.AuthToken, + consumerToken: cfg.AssetGetter.Emapi.ConsumerToken, + facility: cfg.AssetGetter.Emapi.Facility, + customHeaders: cfg.AssetGetter.Emapi.CustomHeaders, + logger: logger, + }, nil +} + +// assetByID queries emapi for the hardware asset by ID and returns an Asset object +func (c *emapiClient) AssetByID(ctx context.Context, id string) (*model.Asset, error) { + hardware, err := c.requestHardwareByID(ctx, id) + if err != nil { + return nil, errors.Wrap(ErrEMAPI, err.Error()) + } + + // ensure hardware object has all required attributes + switch { + case hardware.ID == "": + return nil, errors.Wrap(ErrEMAPI, "queried hardware object has no ID attribute set") + case hardware.BMC.Address == "": + return nil, errors.Wrap(ErrEMAPI, "queried hardware object has no BMC address attribute set") + case hardware.BMC.Username == "": + return nil, errors.Wrap(ErrEMAPI, "queried hardware object has no BMC username attribute set") + case hardware.BMC.Password == "": + return nil, errors.Wrap(ErrEMAPI, "queried hardware object has no BMC password attribute set") + } + + return c.toAsset(hardware), nil +} + +func (c *emapiClient) toAsset(h *Hardware) *model.Asset { + return &model.Asset{ + ID: h.ID, + Model: h.Model, + Vendor: h.Manufacturer.Name, + BMCUsername: h.BMC.Username, + BMCPassword: h.BMC.Password, + BMCAddress: net.ParseIP(h.BMC.Address), + } +} + +// APIData struct is the response from the Packet API +type APIData struct { + APIMeta struct { + CurrentPage *int `json:"current_page"` + LastPage *int `json:"next_page"` + PrevPage *int `json:"prev_page"` + TotalPages *int `json:"total_pages"` + } `json:"meta"` + Hardware []Hardware `json:"hardware"` +} + +// Hardware struct is where hardware data is unmashalled into +type Hardware struct { + ID string `json:"id"` + Model string `json:"model_number"` + Manufacturer struct { + Slug string `json:"slug"` + Name string `json:"name"` + } `json:"manufacturer"` + + BMC struct { + Address string `json:"address"` + Username string `json:"username"` + Password string `json:"password"` + } `json:"management"` +} + +func (c *emapiClient) requestHardwareByID(ctx context.Context, id string) (*Hardware, error) { + endpoint := *c.endpoint + + query := endpoint.Query() + query.Add("include", "manufacturer") + + endpoint.RawQuery = query.Encode() + endpoint.Path += "/hardware/" + id + + body, statusCode, err := c.query(ctx, endpoint.String(), "GET", nil) + if err != nil { + c.logger.WithFields( + logrus.Fields{ + "url": endpoint, + "err": err, + "statusCode": statusCode, + }).Error("error returned in EMAPI request") + + return nil, err + } + + hardware := &Hardware{} + + err = json.Unmarshal(body, hardware) + if err != nil { + c.logger.WithFields( + logrus.Fields{ + "url": endpoint, + "err": err, + }).Error("error in EMAPI response unmarshal") + + return nil, err + } + + return hardware, nil +} + +func (c *emapiClient) AssetsByOffsetLimit(ctx context.Context, offset, limit int) (assets []*model.Asset, total int, err error) { + endpoint := *c.endpoint + + query := endpoint.Query() + query.Add("include", "manufacturer") + query.Add("facility", c.facility) + query.Add("type", "server") + query.Add("model", "r6515") + query.Add("page", strconv.Itoa(offset)) + query.Add("per_page", strconv.Itoa(limit)) + + endpoint.RawQuery = query.Encode() + endpoint.Path += "/hardware/" + + body, statusCode, err := c.query(ctx, endpoint.String(), "GET", nil) + if err != nil { + c.logger.WithFields( + logrus.Fields{ + "url": endpoint.String(), + "err": err, + "statusCode": statusCode, + }).Error("error returned in EMAPI request") + + return nil, 0, err + } + + data := &APIData{} + + err = json.Unmarshal(body, data) + if err != nil { + c.logger.WithFields( + logrus.Fields{ + "url": endpoint, + "err": err, + }).Error("error in EMAPI response unmarshal") + + return nil, 0, err + } + + assets = make([]*model.Asset, 0, len(data.Hardware)) + + for _, hw := range data.Hardware { + hw := hw + assets = append(assets, c.toAsset(&hw)) + } + + return assets, *data.APIMeta.TotalPages, nil +} + +func (c *emapiClient) query(ctx context.Context, endpoint, method string, payload []byte) (body []byte, statusCode int, err error) { + var req *http.Request + + if len(payload) > 0 { + req, err = http.NewRequestWithContext(ctx, method, endpoint, bytes.NewReader(payload)) + } else { + req, err = http.NewRequestWithContext(ctx, method, endpoint, http.NoBody) + } + + if err != nil { + return body, 0, err + } + + req.Header.Add("X-Auth-Token", c.authToken) + req.Header.Add("X-Consumer-Token", c.consumerToken) + req.Header.Add("Content-Type", "application/json") + + for key, value := range c.customHeaders { + req.Header.Add(key, value) + } + + requestRetryable, err := retryablehttp.FromRequest(req) + if err != nil { + return body, 0, err + } + + resp, err := c.client.Do(requestRetryable) + if err != nil { + return body, 0, err + } + + body, err = io.ReadAll(resp.Body) + if err != nil { + return body, 0, err + } + + defer resp.Body.Close() + + return body, resp.StatusCode, nil +} diff --git a/internal/asset/emapi_test.go b/internal/asset/emapi_test.go new file mode 100644 index 00000000..d34fc288 --- /dev/null +++ b/internal/asset/emapi_test.go @@ -0,0 +1,174 @@ +package asset + +import ( + "context" + "os" + "strconv" + "sync" + "testing" + "time" + + "github.com/gammazero/workerpool" + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/fixtures" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + // _ "net/http/pprof" +) + +func Test_EMAPIListByIDs(t *testing.T) { + // left commented out here to indicate a means of debugging + // go func() { + // log.Println(http.ListenAndServe("localhost:9091", nil)) + // }() + // init alloy app + alloy, err := app.New(context.TODO(), app.KindOutOfBand, "", model.LogLevelTrace) + if err != nil { + t.Fatal(err) + } + + alloy.Config.AssetGetter.Emapi.AuthToken = "authtoken" + alloy.Config.AssetGetter.Emapi.ConsumerToken = "consumertoken" + alloy.Config.AssetGetter.Emapi.Facility = "ac12" + + got := []*model.Asset{} + expected := []*model.Asset{ + fixtures.MockAssets["foo"], + fixtures.MockAssets["bar"], + } + + var collectTimedOut bool + + // background routine to receive asset objects objects sent from the asset getter + // mocks a collector + alloy.SyncWg.Add(1) + + go func(t *testing.T, wg *sync.WaitGroup) { + t.Helper() + + defer wg.Done() + + timeout := time.NewTicker(time.Second * 5).C + Loop: + for { + select { + case asset, ok := <-alloy.AssetCh: + if !ok { + break Loop + } + got = append(got, asset) + case <-timeout: + collectTimedOut = true + break Loop + } + } + }(t, alloy.SyncWg) + + // init asset getter + emapi, err := NewEMAPISource(context.TODO(), alloy) + if err != nil { + t.Fatal(err) + } + + // set mock emapi requestor client to mock client method responses + emapi.SetClient(fixtures.NewMockEMAPIClient()) + + err = emapi.ListByIDs(context.TODO(), []string{"foo", "bar"}) + if err != nil { + t.Fatal(err) + } + + // wait for routines to complete + alloy.SyncWg.Wait() + + // test inventory items match expected + assert.ElementsMatch(t, expected, got) + assert.False(t, collectTimedOut) +} + +func Test_EMAPIListAll(t *testing.T) { + // nolint:govet // test struct is clearer to read in this alignment + testcases := []struct { + batchSize int + totalAssets int + name string + }{ + { + 1, + 1, + "total == batch size", + }, + { + 1, + 10, + "total > batch size", + }, + { + 20, + 10, + "total < batch size", + }, + } + + os.Setenv("TEST_ENV", "1") + defer os.Unsetenv("TEST_ENV") + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + var collectTimedOut bool + got := []*model.Asset{} + + assetCh := make(chan *model.Asset) + e := &emapi{ + logger: logrus.New().WithField("component", "getter.emapi"), + syncWg: &sync.WaitGroup{}, + assetCh: assetCh, + workers: workerpool.New(2), + } + e.logger.Level = logrus.TraceLevel + + // e.logger.Level = logrus.TraceLevel + // set mock emapi requestor client to mock client method responses + e.SetClient(fixtures.NewMockEMAPIClient()) + + // rigs the mock AssetsByOffsetLimit to return the total count of assets + os.Setenv("EMAPI_FIXTURE_TOTAL_ASSETS", strconv.Itoa(tc.totalAssets)) + defer os.Unsetenv("EMAPI_FIXTURE_TOTAL_ASSETS") + + // background routine to receive asset objects objects sent from the asset getter + // mocks a collector + e.syncWg.Add(1) + go func(t *testing.T) { + t.Helper() + defer e.syncWg.Done() + + timeout := time.NewTicker(time.Second*delayBetweenRequests + 5).C + Loop: + for { + select { + case asset, ok := <-assetCh: + if !ok { + break Loop + } + got = append(got, asset) + case <-timeout: + collectTimedOut = true + break Loop + } + } + }(t) + + err := e.ListAll(context.TODO()) + if err != nil { + t.Fatal(err) + } + + // wait for routines to return + e.syncWg.Wait() + + assert.Equal(t, tc.totalAssets, len(got)) + assert.False(t, collectTimedOut) + }) + } +} diff --git a/internal/asset/errors.go b/internal/asset/errors.go new file mode 100644 index 00000000..4a3c4259 --- /dev/null +++ b/internal/asset/errors.go @@ -0,0 +1,8 @@ +package asset + +import "errors" + +var ( + ErrGetter = errors.New("asset getter error") + ErrConfig = errors.New("required config not set") +) diff --git a/internal/asset/interface.go b/internal/asset/interface.go new file mode 100644 index 00000000..ecaf29d7 --- /dev/null +++ b/internal/asset/interface.go @@ -0,0 +1,17 @@ +package asset + +import ( + "context" +) + +// Getter interface declares methods to be implemented for asset retrieval from the asset store. +type Getter interface { + // All runs the asset getter which retrieves all assets in the inventory store and sends them over the asset channel. + ListAll(ctx context.Context) error + + // ByIDs runs the asset getter which retrieves all assets based on the list of IDs and sends them over the asset channel. + ListByIDs(ctx context.Context, assetIDs []string) error + + // SetClient sets the given client as the Getter client to enable mocking for tests. + SetClient(interface{}) +} diff --git a/internal/collect/errors.go b/internal/collect/errors.go new file mode 100644 index 00000000..1311a94c --- /dev/null +++ b/internal/collect/errors.go @@ -0,0 +1,7 @@ +package collect + +import "errors" + +var ( + ErrCollectorKind = errors.New("invalid collector kind") +) diff --git a/internal/collect/inband.go b/internal/collect/inband.go new file mode 100644 index 00000000..aba6c78d --- /dev/null +++ b/internal/collect/inband.go @@ -0,0 +1,67 @@ +package collect + +import ( + "context" + "os" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/metal-toolbox/ironlib" + ironlibm "github.com/metal-toolbox/ironlib/model" + "github.com/sirupsen/logrus" +) + +// Inband collector collects hardware, firmware inventory inband +type InbandCollector struct { + deviceManager ironlibm.DeviceManager + logger *logrus.Entry + collectorCh chan<- *model.AssetDevice + termCh <-chan os.Signal + mock bool +} + +// New returns an inband inventory collector +func NewInbandCollector(alloy *app.App) Collector { + logger := app.NewLogrusEntryFromLogger(logrus.Fields{"component": "collector.inband"}, alloy.Logger) + + return &InbandCollector{ + logger: logger, + collectorCh: alloy.CollectorCh, + termCh: alloy.TermCh, + } +} + +func (i *InbandCollector) SetMockGetter(getter interface{}) { + i.deviceManager = getter.(ironlibm.DeviceManager) + i.mock = true +} + +// Inventory implements the Collector interface to collect inventory inband +func (i *InbandCollector) Inventory(ctx context.Context) error { + if !i.mock { + var err error + + i.deviceManager, err = ironlib.New(i.logger.Logger) + if err != nil { + return err + } + } + + defer close(i.collectorCh) + + for { + select { + case <-ctx.Done(): + return nil + default: + device, err := i.deviceManager.GetInventory(ctx) + if err != nil { + return err + } + + i.collectorCh <- &model.AssetDevice{ID: "", Device: device} + + return nil + } + } +} diff --git a/internal/collect/inband_test.go b/internal/collect/inband_test.go new file mode 100644 index 00000000..5b3de9e9 --- /dev/null +++ b/internal/collect/inband_test.go @@ -0,0 +1,61 @@ +package collect + +import ( + "context" + "testing" + "time" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/fixtures" + "gotest.tools/assert" + + "github.com/metal-toolbox/alloy/internal/model" +) + +func Test_InbandInventory(t *testing.T) { + // init mock ironlib with test fixture + mockIronlib := fixtures.NewMockIronlib() + mockIronlib.SetMockDevice(fixtures.CopyDevice(fixtures.E3C246D4INL)) + + // init alloy app + alloy, err := app.New(context.TODO(), app.KindInband, "", model.LogLevelTrace) + if err != nil { + t.Fatal(err) + } + + // init mock inband inventory collector + collector := NewInbandCollector(alloy) + collector.SetMockGetter(mockIronlib) + + var got *model.AssetDevice + + // background routine to collect device inventory objects sent from the collector + go func(t *testing.T) { + t.Helper() + + timeout := time.NewTicker(time.Second * 2).C + + var ok bool + + Loop: + for { + select { + case got, ok = <-alloy.CollectorCh: + if !ok { + continue + } + break Loop + case <-timeout: + break Loop + } + } + }(t) + + // collect inventory + err = collector.Inventory(context.TODO()) + if err != nil { + t.Fatal(err) + } + + assert.DeepEqual(t, &model.AssetDevice{Device: fixtures.E3C246D4INL}, got) +} diff --git a/internal/collect/interface.go b/internal/collect/interface.go new file mode 100644 index 00000000..010bc918 --- /dev/null +++ b/internal/collect/interface.go @@ -0,0 +1,13 @@ +package collect + +import ( + "context" +) + +// Collector interface defines methods to collect device inventory +type Collector interface { + // Inventory listens on the AssetCh collects inventory for all assets until the AssetCh is closed. + Inventory(ctx context.Context) error + // SetMockGetter sets a mock device inventory getter to be used for tests. + SetMockGetter(getter interface{}) +} diff --git a/internal/collect/outofband.go b/internal/collect/outofband.go new file mode 100644 index 00000000..43a137ea --- /dev/null +++ b/internal/collect/outofband.go @@ -0,0 +1,209 @@ +package collect + +import ( + "context" + "os" + "sync" + "sync/atomic" + "time" + + bmclibv2 "github.com/bmc-toolbox/bmclib/v2" + "github.com/bmc-toolbox/common" + logrusrv2 "github.com/bombsimon/logrusr/v2" + "github.com/gammazero/workerpool" + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/sirupsen/logrus" +) + +const ( + // concurrency is the default number of workers to concurrently query BMCs + concurrency = 10 +) + +// OutOfBand collector collects hardware, firmware inventory out of band +type OutOfBandCollector struct { + mockClient oobGetter + logger *logrus.Entry + config *model.Config + syncWg *sync.WaitGroup + assetCh <-chan *model.Asset + termCh <-chan os.Signal + collectorCh chan<- *model.AssetDevice + workers workerpool.WorkerPool +} + +// oobGetter interface defines methods that the bmclib client exposes +// this is mainly to swap the bmclib instance for tests +type oobGetter interface { + Open(ctx context.Context) error + Close(ctx context.Context) error + Inventory(ctx context.Context) (*common.Device, error) +} + +// NewOutOfBandCollector returns a instance of the OutOfBandCollector inventory collector +func NewOutOfBandCollector(alloy *app.App) Collector { + logger := app.NewLogrusEntryFromLogger(logrus.Fields{"component": "collector.outofband"}, alloy.Logger) + + c := &OutOfBandCollector{ + logger: logger, + assetCh: alloy.AssetCh, + termCh: alloy.TermCh, + syncWg: alloy.SyncWg, + config: alloy.Config, + collectorCh: alloy.CollectorCh, + workers: *workerpool.New(concurrency), + } + + if c.config.CollectorOutofband.Concurrency == 0 { + c.config.CollectorOutofband.Concurrency = concurrency + } + + return c +} + +func (o *OutOfBandCollector) SetMockGetter(getter interface{}) { + o.mockClient = getter.(oobGetter) +} + +// Inventory implements the Collector interface to collect inventory inband +func (o *OutOfBandCollector) Inventory(ctx context.Context) error { + // close collectorCh to notify consumers + defer close(o.collectorCh) + + // channel for routines spawned to indicate completion + doneCh := make(chan struct{}) + + // count of routines spawned to retrieve assets + var dispatched int32 + + // spawn routines to collect inventory for assets + for asset := range o.assetCh { + if asset == nil { + continue + } + + // increment wait group + o.syncWg.Add(1) + + // increment spawned count + atomic.AddInt32(&dispatched, 1) + + for o.workers.WaitingQueueSize() > 0 { + if ctx.Err() != nil { + break + } + + o.logger.WithFields(logrus.Fields{ + "component": "oob collector", + "queue size": o.workers.WaitingQueueSize(), + "concurrency": concurrency, + }).Debug("delay for queue size to drop..") + + // nolint:gomnd // delay is a magic number + time.Sleep(5 * time.Second) + } + + // submit inventory collection to worker pool + o.workers.Submit( + func() { + defer o.syncWg.Done() + defer func() { doneCh <- struct{}{} }() + + o.spawn(ctx, asset) + }, + ) + } + + // wait for dispatched routines to complete + for dispatched > 0 { + <-doneCh + atomic.AddInt32(&dispatched, ^int32(0)) + } + + return nil +} + +// spawn runs the asset inventory collection and writes the collected inventory to the collectorCh +func (o *OutOfBandCollector) spawn(ctx context.Context, asset *model.Asset) { + // bmc is the bmc client instance + var bmc oobGetter + + o.logger.WithFields( + logrus.Fields{ + "IP": asset.BMCAddress.String(), + }).Trace("collecting inventory for asset..") + + if o.mockClient == nil { + bmc = newBMCClient( + ctx, + asset.BMCUsername, + asset.BMCPassword, + asset.BMCAddress.String(), + o.logger.Logger, + ) + } else { + // mock client for tests + bmc = o.mockClient + } + + if err := bmc.Open(ctx); err != nil { + o.logger.WithFields( + logrus.Fields{ + "IP": asset.BMCAddress.String(), + "err": err, + }).Warn("error in bmc connection open") + + return + } + + defer func() { + if err := bmc.Close(ctx); err != nil { + o.logger.WithFields( + logrus.Fields{ + "IP": asset.BMCAddress.String(), + "err": err, + }).Warn("error in bmc connection close") + } + }() + + device, err := bmc.Inventory(ctx) + if err != nil { + o.logger.WithFields( + logrus.Fields{ + "IP": asset.BMCAddress.String(), + "err": err, + }).Warn("error in bmc inventory collection") + + return + } + + o.collectorCh <- &model.AssetDevice{ID: asset.ID, Device: device} +} + +// newBMCClient initializes a bmclib client with the given credentials +func newBMCClient(ctx context.Context, user, pass, host string, l *logrus.Logger) *bmclibv2.Client { + logger := logrus.New() + logger.Formatter = l.Formatter + + // setup a logr logger for bmclib + // bmclib uses logr, for which the trace logs are logged with log.V(3), + // this is a hax so the logrusr lib will enable trace logging + // since any value that is less than (logrus.LogLevel - 4) >= log.V(3) is ignored + // https://github.com/bombsimon/logrusr/blob/master/logrusr.go#L64 + switch l.GetLevel() { + case logrus.TraceLevel: + logger.Level = 7 + case logrus.DebugLevel: + logger.Level = 5 + } + + logruslogr := logrusrv2.New(logger) + + bmcClient := bmclibv2.NewClient(host, "", user, pass, bmclibv2.WithLogger(logruslogr)) + + // filter BMC providers based on compatibility + bmcClient.Registry.Drivers = bmcClient.Registry.FilterForCompatible(ctx) + + return bmcClient +} diff --git a/internal/collect/outofband_test.go b/internal/collect/outofband_test.go new file mode 100644 index 00000000..23dd7d7d --- /dev/null +++ b/internal/collect/outofband_test.go @@ -0,0 +1,93 @@ +package collect + +import ( + "context" + "fmt" + "os" + "sync" + "testing" + "time" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/fixtures" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/stretchr/testify/assert" +) + +func Test_OutOfBandInventory(t *testing.T) { + // init alloy app + alloy, err := app.New(context.TODO(), app.KindInband, "", model.LogLevelInfo) + if err != nil { + t.Fatal(err) + } + + // env variables set by mock bmclib fixture + defer func() { + os.Unsetenv(fixtures.EnvMockBMCOpen) + os.Unsetenv(fixtures.EnvMockBMCClose) + }() + + // init mock collector which mocks OOB inventory + collector := NewOutOfBandCollector(alloy) + collector.SetMockGetter(fixtures.NewMockBmclib()) + + // mock assets the collector will collect OOB inventory for + mockAssets := fixtures.MockAssets + got := []*model.AssetDevice{} + + // background routine to get assets from the store for which inventory is collected + // mocks an AssetGetter + alloy.SyncWg.Add(1) + + go func(t *testing.T, wg *sync.WaitGroup) { + t.Helper() + + defer wg.Done() + + assetGetter := fixtures.NewMockAssetGetter(alloy.AssetCh, mockAssets) + assetGetter.Run(context.TODO()) + }(t, alloy.SyncWg) + + // background routine to collect device inventory objects sent from the collector + // mocks a Publisher + alloy.SyncWg.Add(1) + + go func(t *testing.T, wg *sync.WaitGroup) { + t.Helper() + + defer wg.Done() + + timeout := time.NewTicker(time.Second * 5).C + Loop: + for { + select { + case device, ok := <-alloy.CollectorCh: + if !ok { + break Loop + } + got = append(got, device) + case <-timeout: + fmt.Println("hit timeout...") + break Loop + } + } + }(t, alloy.SyncWg) + + // run the inventory collector + err = collector.Inventory(context.TODO()) + if err != nil { + t.Fatal(err) + } + + // wait for routines to complete + alloy.SyncWg.Wait() + + // test inventory items match expected + assert.Equal(t, len(mockAssets), len(got)) + + // test bmc connection was opened + assert.Equal(t, os.Getenv(fixtures.EnvMockBMCOpen), "true") + + // test bmc connection was closed + assert.Equal(t, os.Getenv(fixtures.EnvMockBMCClose), "true") +} diff --git a/internal/fixtures/assets.go b/internal/fixtures/assets.go new file mode 100644 index 00000000..45b13278 --- /dev/null +++ b/internal/fixtures/assets.go @@ -0,0 +1,51 @@ +package fixtures + +import ( + "context" + "net" + + "github.com/metal-toolbox/alloy/internal/model" +) + +var ( + MockAssets = map[string]*model.Asset{ + "foo": { + ID: "foo", + BMCAddress: net.ParseIP("127.0.0.1"), + BMCUsername: "foo", + BMCPassword: "bar", + }, + "bar": { + ID: "bar", + BMCAddress: net.ParseIP("127.0.0.2"), + BMCUsername: "foo", + BMCPassword: "bar", + }, + "borky": { + ID: "", + BMCAddress: net.ParseIP(""), + BMCUsername: "foo", + BMCPassword: "bar", + }, + } +) + +// MockAssetGetter mocks an asset acquirer +type MockAssetGetter struct { + ch chan<- *model.Asset + assets map[string]*model.Asset +} + +// NewMockAssetGetter returns a MockAssetGetter that writes the given []*model.Asset to the given channel +func NewMockAssetGetter(ch chan<- *model.Asset, assets map[string]*model.Asset) *MockAssetGetter { + return &MockAssetGetter{ch: ch, assets: assets} +} + +// Run implements the AssetGetter interface, sending mock assets over the asset channel +func (m *MockAssetGetter) Run(ctx context.Context) { + for _, asset := range m.assets { + m.ch <- asset + } + + close(m.ch) +} diff --git a/internal/fixtures/bmclib.go b/internal/fixtures/bmclib.go new file mode 100644 index 00000000..2e7d5100 --- /dev/null +++ b/internal/fixtures/bmclib.go @@ -0,0 +1,46 @@ +package fixtures + +import ( + "context" + "os" + + bmclibv2 "github.com/bmc-toolbox/bmclib/v2" + "github.com/bmc-toolbox/common" +) + +const ( + EnvMockBMCOpen = "MOCK_BMC_OPEN" + EnvMockBMCClose = "MOCK_BMC_CLOSE" +) + +type MockBmclib struct { + // embed bmclib client to provide methods + bmclibv2.Client + device *common.Device +} + +func NewBmclibClient() *MockBmclib { + return &MockBmclib{} +} + +func (m *MockBmclib) Open(ctx context.Context) error { + os.Setenv(EnvMockBMCOpen, "true") + return nil +} + +func (m *MockBmclib) Close(ctx context.Context) error { + os.Setenv(EnvMockBMCClose, "true") + return nil +} + +func (m *MockBmclib) Inventory(ctx context.Context) (*common.Device, error) { + return CopyDevice(E3C246D4INL), nil +} + +func NewMockBmclib() *MockBmclib { + return &MockBmclib{} +} + +func (m *MockBmclib) SetMockDevice(d *common.Device) { + m.device = d +} diff --git a/internal/fixtures/device.go b/internal/fixtures/device.go new file mode 100644 index 00000000..2307af2b --- /dev/null +++ b/internal/fixtures/device.go @@ -0,0 +1,1864 @@ +package fixtures + +import ( + "github.com/bmc-toolbox/common" +) + +// nolint //test fixtures +var ( + E3C246D4INL = &common.Device{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "Packet", + Model: "c3.small.x86 (To Be Filled By O.E.M.)", + Serial: "D5S0R8000105", + ProductName: "", + Firmware: nil, + Status: nil, + Metadata: map[string]string(nil), // p0 + }, + HardwareType: "", + Chassis: "", + BIOS: &common.BIOS{ + Common: common.Common{ + Oem: false, + Description: "BIOS", + Vendor: "American Megatrends Inc.", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "L2.07B", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: nil, + }, + SizeBytes: 65536, + CapacityBytes: 33554432, + }, + BMC: &common.BMC{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: nil, + Status: nil, + }, + ID: "", + NIC: &common.NIC{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: nil, + Status: nil, + }, + ID: "", + Description: "", + SpeedBits: 0, + PhysicalID: "", + BusInfo: "", + MacAddress: "", + }, + }, + Mainboard: &common.Mainboard{ + Common: common.Common{ + Oem: false, + Description: "Motherboard", + Vendor: "ASRockRack", + Model: "E3C246D4I-NL", + Serial: "196231220000153", + ProductName: "E3C246D4I-NL", + Firmware: nil, + Status: nil, + }, + PhysicalID: "0", + }, + CPLDs: []*common.CPLD{}, + TPMs: []*common.TPM{}, + GPUs: []*common.GPU{}, + CPUs: []*common.CPU{ + { + Common: common.Common{ + Oem: false, + Description: "CPU", + Vendor: "Intel Corp.", + Model: "Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz", + Serial: "", + ProductName: "Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz", + Firmware: nil, + Status: nil, + }, + ID: "", + Slot: "CPU1", + Architecture: "", + ClockSpeedHz: 100000000, + Cores: 8, + Threads: 16, + }, + }, + Memory: []*common.Memory{ + { + Common: common.Common{ + Oem: false, + Description: "SODIMM DDR4 Synchronous 2666 MHz (0.4 ns)", + Vendor: "Micron", + Model: "18ASF2G72HZ-2G6E1", + Serial: "F0F9053F", + ProductName: "18ASF2G72HZ-2G6E1", + Firmware: nil, + Status: nil, + }, + ID: "", + Slot: "ChannelA-DIMM0", + Type: "", + SizeBytes: 17179869184, + FormFactor: "", + PartNumber: "", + ClockSpeedHz: 2666000000, + }, + { + Common: common.Common{ + Oem: false, + Description: "SODIMM DDR4 Synchronous 2666 MHz (0.4 ns)", + Vendor: "Micron", + Model: "18ASF2G72HZ-2G6E1", + Serial: "F0F90894", + ProductName: "18ASF2G72HZ-2G6E1", + Firmware: nil, + Status: nil, + }, + ID: "", + Slot: "ChannelB-DIMM0", + Type: "", + SizeBytes: 17179869184, + FormFactor: "", + PartNumber: "", + ClockSpeedHz: 2666000000, + }, + }, + NICs: []*common.NIC{ + { + Common: common.Common{ + Oem: false, + Description: "Ethernet interface", + Vendor: "Intel Corporation", + Model: "Ethernet Controller X710 for 10GbE SFP+", + Serial: "b4:96:91:70:26:c8", + ProductName: "Ethernet Controller X710 for 10GbE SFP+", + Firmware: &common.Firmware{ + Installed: "1.1853.0", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: nil, + Metadata: map[string]string{ + "driver": "i40e", + "duplex": "full", + "firmware": "6.01 0x80003fa1 1.1853.0", + "link": "yes", + "speed": "10Gbit/s", + }, + }, + ID: "", + Description: "Ethernet interface", + SpeedBits: 10000000000, + PhysicalID: "0", + BusInfo: "pci@0000:01:00.0", + MacAddress: "", + }, + }, + Drives: []*common.Drive{ + { + Common: common.Common{ + Oem: false, + Description: "ATA Disk", + Vendor: "intel", + Model: "INTEL SSDSC2KB48", + Serial: "PHYF001300HB480BGN", + ProductName: "INTEL SSDSC2KB48", + Firmware: nil, + Status: nil, + }, + ID: "", + OemID: "", + Type: "", + StorageController: "", + BusInfo: "scsi@4:0.0.0", + WWN: "", + Protocol: "", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 480103981056, + BlockSizeBytes: 0, + CapableSpeedGbps: 0, + NegotiatedSpeedGbps: 0, + }, + { + Common: common.Common{ + Oem: false, + Description: "ATA Disk", + Vendor: "intel", + Model: "INTEL SSDSC2KB48", + Serial: "PHYF001209KL480BGN", + ProductName: "INTEL SSDSC2KB48", + Firmware: nil, + Status: nil, + }, + ID: "", + OemID: "", + Type: "", + StorageController: "", + BusInfo: "scsi@5:0.0.0", + WWN: "", + Protocol: "", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 480103981056, + BlockSizeBytes: 0, + CapableSpeedGbps: 0, + NegotiatedSpeedGbps: 0, + }, + }, + StorageControllers: []*common.StorageController{ + { + Common: common.Common{ + Oem: false, + Description: "SATA controller", + Vendor: "Intel Corporation", + Model: "Cannon Lake PCH SATA AHCI Controller", + Serial: "", + ProductName: "Cannon Lake PCH SATA AHCI Controller", + Firmware: nil, + Status: nil, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "SATA", + SupportedRAIDTypes: "", + PhysicalID: "17", + BusInfo: "pci@0000:00:17.0", + SpeedGbps: 0, + }, + }, + PSUs: []*common.PSU{}, + Enclosures: []*common.Enclosure{}, + } + + R6515_f0c8e4ac = &common.Device{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "Dell Inc.", + Model: "PowerEdge R6515", + Serial: "L00NEYT00NS", + ProductName: "", + Firmware: nil, + Status: nil, + Metadata: map[string]string(nil), // p0 + }, + HardwareType: "", + Chassis: "", + BIOS: &common.BIOS{ + Common: common.Common{ + Oem: false, + Description: "BIOS Configuration Current Settings", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "2.6.6", + Available: "", + SoftwareID: "159", + Previous: []*common.Firmware{ + &common.Firmware{ + Installed: "2.2.4", + Available: "", + SoftwareID: "159", + Previous: nil, + }, + }, + Metadata: map[string]string{ + "name": "BIOS", + }, + }, + Status: nil, + }, + SizeBytes: 0, + CapacityBytes: 0, + }, + BMC: &common.BMC{ + Common: common.Common{ + Oem: false, + Description: "BMC", + Vendor: "Dell Inc.", + Model: "PowerEdge R6515", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "5.10.00.00", + Available: "", + SoftwareID: "25227", + Previous: []*common.Firmware{ + &common.Firmware{ + Installed: "4.40.00.00", + Available: "", + SoftwareID: "25227", + Previous: nil, + }, + }, + Metadata: map[string]string{ + "name": "Integrated Dell Remote Access Controller", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "iDRAC.Embedded.1", + NIC: nil, + }, + Mainboard: &common.Mainboard{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: nil, + Status: nil, + }, + PhysicalID: "", + }, + CPLDs: []*common.CPLD{ + { + Common: common.Common{ + Oem: false, + Description: "System CPLD", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "1.0.3", + Available: "", + SoftwareID: "27763", + Previous: nil, + Metadata: map[string]string{ + "name": "System CPLD", + }, + }, + Status: nil, + }, + }, + }, + TPMs: []*common.TPM{ + &common.TPM{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "1.3.1.0", + Available: "", + SoftwareID: "109673", + Previous: nil, + Metadata: map[string]string{ + "name": "TPM", + }, + }, + Status: &common.Status{ + Health: "", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + InterfaceType: "TPM2_0", + }, + }, + GPUs: []*common.GPU{}, + CPUs: []*common.CPU{ + &common.CPU{ + Common: common.Common{ + Oem: false, + Description: "Represents the properties of a Processor attached to this System", + Vendor: "AMD", + Model: "AMD EPYC 7402P 24-Core Processor", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "0x8301052", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "CPU.Socket.1", + Slot: "CPU.Socket.1", + Architecture: "x86", + ClockSpeedHz: 3900, + Cores: 24, + Threads: 48, + }, + }, + Memory: []*common.Memory{ + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A7", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "53737518", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A7", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A8", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "537374F7", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A8", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A4", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "53737489", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A4", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A2", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "537374FE", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A2", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A5", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "537374F0", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A5", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A6", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "5373745B", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A6", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A1", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "53737450", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A1", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A3", + Vendor: "Hynix Semiconductor", + Model: "", + Serial: "53737520", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A3", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "HMA81GR7CJR8N-XN", + ClockSpeedHz: 3200, + }, + }, + NICs: []*common.NIC{ + &common.NIC{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "1.5.12", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "NIC.Embedded.1", + Description: "Embedded NIC 1 Port 1 Partition 1", + SpeedBits: 6, + PhysicalID: "", + BusInfo: "", + MacAddress: "", + }, + &common.NIC{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "Intel Corporation", + Model: "Intel(R) 10GbE 2P X710 Adapter", + Serial: "MYFLMIT04P00HQ", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "19.5.12", + Available: "", + SoftwareID: "102302", + Previous: nil, + Metadata: map[string]string{ + "name": "Intel(R) Ethernet Converged Network Adapter X710 - F8:F2:1E:A0:0D:E1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "NIC.Slot.3", + Description: "NIC in Slot 3 Port 1 Partition 1", + SpeedBits: 100006, + PhysicalID: "", + BusInfo: "", + MacAddress: "F8:F2:1E:A0:0D:E0", + }, + }, + Drives: []*common.Drive{ + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 1 in Backplane 1 of Integrated Storage Controller 1", + Vendor: "MICRON", + Model: "MTFDDAK480TDC", + Serial: "201827F5890A", + ProductName: "MTFDDAK480TDC", + Firmware: &common.Firmware{ + Installed: "F003", + Available: "", + SoftwareID: "107865", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 1 in Backplane 1 of Integrated Storage Controller 1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Bay.1:Enclosure.Internal.0-1:NonRAID.Integrated.1-1", + OemID: "", + Type: "SSD", + StorageController: "NonRAID.Integrated.1-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 480103980544, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 0 in Backplane 1 of Integrated Storage Controller 1", + Vendor: "MICRON", + Model: "MTFDDAK480TDC", + Serial: "201827F58A0E", + ProductName: "MTFDDAK480TDC", + Firmware: &common.Firmware{ + Installed: "F003", + Available: "", + SoftwareID: "107865", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 0 in Backplane 1 of Integrated Storage Controller 1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Bay.0:Enclosure.Internal.0-1:NonRAID.Integrated.1-1", + OemID: "", + Type: "SSD", + StorageController: "NonRAID.Integrated.1-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 480103980544, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 0 on AHCI Controller in slot 2", + Vendor: "INTEL", + Model: "SSDSCKKB240G8R", + Serial: "PHYH0115000B240J", + ProductName: "SSDSCKKB240G8R", + Firmware: &common.Firmware{ + Installed: "DL6P", + Available: "", + SoftwareID: "108313", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 0 on AHCI Controller in slot 2", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Direct.0-0:AHCI.Slot.2-1", + OemID: "", + Type: "SSD", + StorageController: "AHCI.Slot.2-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 240057409536, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 1 on AHCI Controller in slot 2", + Vendor: "INTEL", + Model: "SSDSCKKB240G8R", + Serial: "PHYH011304S4240J", + ProductName: "SSDSCKKB240G8R", + Firmware: &common.Firmware{ + Installed: "DL6P", + Available: "", + SoftwareID: "108313", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 1 on AHCI Controller in slot 2", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Direct.1-1:AHCI.Slot.2-1", + OemID: "", + Type: "SSD", + StorageController: "AHCI.Slot.2-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 240057409536, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + }, + StorageControllers: []*common.StorageController{ + &common.StorageController{ + Common: common.Common{ + Oem: false, + Description: "Dell HBA330 Mini", + Vendor: "DELL", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "16.17.00.05", + Available: "", + SoftwareID: "104298", + Previous: nil, + Metadata: map[string]string{ + "name": "Dell HBA330 Mini", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "", + SupportedRAIDTypes: "", + PhysicalID: "", + BusInfo: "", + SpeedGbps: 12, + }, + &common.StorageController{ + Common: common.Common{ + Oem: false, + Description: "BOSS-S1", + Vendor: "DELL", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "2.5.13.3022", + Available: "", + SoftwareID: "0", + Previous: nil, + Metadata: map[string]string{ + "name": "BOSS-S1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "", + SupportedRAIDTypes: "", + PhysicalID: "", + BusInfo: "", + SpeedGbps: 6, + }, + &common.StorageController{ + Common: common.Common{ + Oem: false, + Description: "FCH SATA Controller [AHCI mode]", + Vendor: "DELL", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "", + SupportedRAIDTypes: "", + PhysicalID: "", + BusInfo: "", + SpeedGbps: 0, + }, + &common.StorageController{ + Common: common.Common{ + Oem: false, + Description: "FCH SATA Controller [AHCI mode]", + Vendor: "DELL", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "", + SupportedRAIDTypes: "", + PhysicalID: "", + BusInfo: "", + SpeedGbps: 0, + }, + }, + PSUs: []*common.PSU{ + &common.PSU{ + Common: common.Common{ + Oem: false, + Description: "PS1 Status", + Vendor: "DELL", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0005L1PQV", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "00.0C.7D", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + PowerCapacityWatts: 550, + }, + &common.PSU{ + Common: common.Common{ + Oem: false, + Description: "PS2 Status", + Vendor: "DELL", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0005L1PQZ", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "00.0C.7D", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + PowerCapacityWatts: 550, + }, + }, + Enclosures: []*common.Enclosure{ + &common.Enclosure{ + Common: common.Common{ + Oem: false, + Description: "It represents the properties for physical components for any system.It represent racks, rackmount servers, blades, standalone, modular systems,enclosures, and all other containers.The non-cpu/device centric parts of the schema are all accessed either directly or indirectly through this resource.", + Vendor: "Dell Inc.", + Model: "PowerEdge R6515", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "System.Embedded.1", + ChassisType: "RackMount", + Firmware: nil, + }, + &common.Enclosure{ + Common: common.Common{ + Oem: false, + Description: "Backplane 1 on Connector 0 of Integrated Storage Controller 1", + Vendor: "", + Model: "BP14G+ 0:1", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Enclosure.Internal.0-1:NonRAID.Integrated.1-1", + ChassisType: "StorageEnclosure", + Firmware: nil, + }, + &common.Enclosure{ + Common: common.Common{ + Oem: false, + Description: "PCIe SSD Backplane 1", + Vendor: "", + Model: "PCIe SSD Backplane 1", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Enclosure.Internal.0-1", + ChassisType: "StorageEnclosure", + Firmware: nil, + }, + }, + } + + R6515_fc167440 = &common.Device{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "Dell Inc.", + Model: "PowerEdge R6515", + Serial: "CNCMS0017V03CN", + ProductName: "", + Firmware: nil, + Status: nil, + }, + HardwareType: "", + Chassis: "", + BIOS: &common.BIOS{ + Common: common.Common{ + Oem: false, + Description: "BIOS Configuration Current Settings", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "2.2.4", + Available: "", + SoftwareID: "159", + Previous: []*common.Firmware{ + &common.Firmware{ + Installed: "2.3.6", + Available: "", + SoftwareID: "159", + Previous: nil, + }, + }, + Metadata: map[string]string{ + "name": "BIOS", + }, + }, + Status: nil, + }, + SizeBytes: 0, + CapacityBytes: 0, + }, + BMC: &common.BMC{ + Common: common.Common{ + Oem: false, + Description: "BMC", + Vendor: "Dell Inc.", + Model: "PowerEdge R6515", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "5.10.00.00", + Available: "", + SoftwareID: "25227", + Previous: []*common.Firmware{ + &common.Firmware{ + Installed: "5.00.10.10", + Available: "", + SoftwareID: "25227", + Previous: nil, + }, + }, + Metadata: map[string]string{ + "name": "Integrated Dell Remote Access Controller", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "iDRAC.Embedded.1", + NIC: nil, + }, + Mainboard: &common.Mainboard{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: nil, + Status: nil, + }, + PhysicalID: "", + }, + CPLDs: []*common.CPLD{ + &common.CPLD{ + Common: common.Common{ + Oem: false, + Description: "System CPLD", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "1.0.7", + Available: "", + SoftwareID: "27763", + Previous: nil, + Metadata: map[string]string{ + "name": "System CPLD", + }, + }, + Status: nil, + }, + }, + }, + TPMs: []*common.TPM{ + &common.TPM{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "1.3.2.8", + Available: "", + SoftwareID: "109673", + Previous: nil, + Metadata: map[string]string{ + "name": "TPM", + }, + }, + Status: &common.Status{ + Health: "", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + InterfaceType: "TPM2_0", + }, + }, + GPUs: []*common.GPU{}, + CPUs: []*common.CPU{ + &common.CPU{ + Common: common.Common{ + Oem: false, + Description: "Represents the properties of a Processor attached to this System", + Vendor: "AMD", + Model: "AMD EPYC 7443P 24-Core Processor", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "0xA00111D", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "CPU.Socket.1", + Slot: "CPU.Socket.1", + Architecture: "x86", + ClockSpeedHz: 3900, + Cores: 24, + Threads: 48, + }, + }, + Memory: []*common.Memory{ + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A2", + Vendor: "Samsung", + Model: "", + Serial: "20390266", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A2", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A1", + Vendor: "Samsung", + Model: "", + Serial: "20390296", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A1", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A4", + Vendor: "Samsung", + Model: "", + Serial: "2035D193", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A4", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A5", + Vendor: "Samsung", + Model: "", + Serial: "203902BC", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A5", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A6", + Vendor: "Samsung", + Model: "", + Serial: "20390263", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A6", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A7", + Vendor: "Samsung", + Model: "", + Serial: "2035D1D9", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A7", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A8", + Vendor: "Samsung", + Model: "", + Serial: "203902BB", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A8", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + &common.Memory{ + Common: common.Common{ + Oem: false, + Description: "DIMM A3", + Vendor: "Samsung", + Model: "", + Serial: "2035D1E1", + ProductName: "", + Firmware: nil, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + Slot: "DIMM.Socket.A3", + Type: "DRAM", + SizeBytes: 8192, + FormFactor: "", + PartNumber: "M393A1K43DB2-CWE", + ClockSpeedHz: 3200, + }, + }, + NICs: []*common.NIC{ + &common.NIC{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "21.81.3", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "NIC.Embedded.1", + Description: "Embedded NIC 1 Port 1 Partition 1", + SpeedBits: 6, + PhysicalID: "", + BusInfo: "", + MacAddress: "", + }, + &common.NIC{ + Common: common.Common{ + Oem: false, + Description: "", + Vendor: "Intel Corporation", + Model: "Intel(R) 25GbE 2P E810-XXV Adptr", + Serial: "MYFLMIT1A100S1", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "20.5.13", + Available: "", + SoftwareID: "109028", + Previous: nil, + Metadata: map[string]string{ + "name": "Intel(R) Ethernet 25G 2P E810-XXV Adapter - B4:96:91:D1:F4:AD", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "NIC.Slot.3", + Description: "NIC in Slot 3 Port 2 Partition 1", + SpeedBits: 250006, + PhysicalID: "", + BusInfo: "", + MacAddress: "B4:96:91:D1:F4:AD", + }, + }, + Drives: []*common.Drive{ + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 1 in Backplane 1 of Integrated Storage Controller 1", + Vendor: "SAMSUNG", + Model: "MZ7LH480HBHQ0D3", + Serial: "S5YJNA0R800552", + ProductName: "MZ7LH480HBHQ0D3", + Firmware: &common.Firmware{ + Installed: "HG58", + Available: "", + SoftwareID: "108622", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 1 in Backplane 1 of Integrated Storage Controller 1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Bay.1:Enclosure.Internal.0-1:NonRAID.Integrated.1-1", + OemID: "", + Type: "SSD", + StorageController: "NonRAID.Integrated.1-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 480103980544, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 0 in Backplane 1 of Integrated Storage Controller 1", + Vendor: "SAMSUNG", + Model: "MZ7LH480HBHQ0D3", + Serial: "S5YJNA0R800525", + ProductName: "MZ7LH480HBHQ0D3", + Firmware: &common.Firmware{ + Installed: "HG58", + Available: "", + SoftwareID: "108622", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 0 in Backplane 1 of Integrated Storage Controller 1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Bay.0:Enclosure.Internal.0-1:NonRAID.Integrated.1-1", + OemID: "", + Type: "SSD", + StorageController: "NonRAID.Integrated.1-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 480103980544, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 0 on AHCI Controller in slot 2", + Vendor: "INTEL", + Model: "SSDSCKKB240G8R", + Serial: "PHYH124303Z1240J", + ProductName: "SSDSCKKB240G8R", + Firmware: &common.Firmware{ + Installed: "DL6R", + Available: "", + SoftwareID: "108313", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 0 on AHCI Controller in slot 2", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Direct.0-0:AHCI.Slot.2-1", + OemID: "", + Type: "SSD", + StorageController: "AHCI.Slot.2-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 240057409536, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + &common.Drive{ + Common: common.Common{ + Oem: false, + Description: "Disk 1 on AHCI Controller in slot 2", + Vendor: "INTEL", + Model: "SSDSCKKB240G8R", + Serial: "PHYH124302RL240J", + ProductName: "SSDSCKKB240G8R", + Firmware: &common.Firmware{ + Installed: "DL6R", + Available: "", + SoftwareID: "108313", + Previous: nil, + Metadata: map[string]string{ + "name": "Disk 1 on AHCI Controller in slot 2", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Disk.Direct.1-1:AHCI.Slot.2-1", + OemID: "", + Type: "SSD", + StorageController: "AHCI.Slot.2-1", + BusInfo: "", + WWN: "", + Protocol: "SATA", + SmartStatus: "", + SmartErrors: nil, + CapacityBytes: 240057409536, + BlockSizeBytes: 512, + CapableSpeedGbps: 6, + NegotiatedSpeedGbps: 6, + }, + }, + StorageControllers: []*common.StorageController{ + &common.StorageController{ + Common: common.Common{ + Oem: false, + Description: "Dell HBA330 Mini", + Vendor: "DELL", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "16.17.01.00", + Available: "", + SoftwareID: "104298", + Previous: nil, + Metadata: map[string]string{ + "name": "Dell HBA330 Mini", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "", + SupportedRAIDTypes: "", + PhysicalID: "", + BusInfo: "", + SpeedGbps: 12, + }, + &common.StorageController{ + Common: common.Common{ + Oem: false, + Description: "BOSS-S1", + Vendor: "DELL", + Model: "", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "2.5.13.3024", + Available: "", + SoftwareID: "0", + Previous: nil, + Metadata: map[string]string{ + "name": "BOSS-S1", + }, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + SupportedControllerProtocols: "", + SupportedDeviceProtocols: "", + SupportedRAIDTypes: "", + PhysicalID: "", + BusInfo: "", + SpeedGbps: 6, + }, + }, + PSUs: []*common.PSU{ + &common.PSU{ + Common: common.Common{ + Oem: false, + Description: "PS1 Status", + Vendor: "DELL", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0015C1STJ", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "00.0C.7D", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + PowerCapacityWatts: 550, + }, + &common.PSU{ + Common: common.Common{ + Oem: false, + Description: "PS2 Status", + Vendor: "DELL", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0015C1STY", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "00.0C.7D", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "", + PowerCapacityWatts: 550, + }, + }, + Enclosures: []*common.Enclosure{ + &common.Enclosure{ + Common: common.Common{ + Oem: false, + Description: "It represents the properties for physical components for any system.It represent racks, rackmount servers, blades, standalone, modular systems,enclosures, and all other containers.The non-cpu/device centric parts of the schema are all accessed either directly or indirectly through this resource.", + Vendor: "Dell Inc.", + Model: "PowerEdge R6515", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "System.Embedded.1", + ChassisType: "RackMount", + Firmware: nil, + }, + &common.Enclosure{ + Common: common.Common{ + Oem: false, + Description: "Backplane 1 on Connector 0 of Integrated Storage Controller 1", + Vendor: "", + Model: "BP14G+ 0:1", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Enclosure.Internal.0-1:NonRAID.Integrated.1-1", + ChassisType: "StorageEnclosure", + Firmware: nil, + }, + &common.Enclosure{ + Common: common.Common{ + Oem: false, + Description: "PCIe SSD Backplane 1", + Vendor: "", + Model: "PCIe SSD Backplane 1", + Serial: "", + ProductName: "", + Firmware: &common.Firmware{ + Installed: "", + Available: "", + SoftwareID: "", + Previous: nil, + }, + Status: &common.Status{ + Health: "OK", + State: "Enabled", + PostCode: 0, + PostCodeStatus: "", + }, + }, + ID: "Enclosure.Internal.0-1", + ChassisType: "StorageEnclosure", + Firmware: nil, + }, + }, + } +) diff --git a/internal/fixtures/emapi.go b/internal/fixtures/emapi.go new file mode 100644 index 00000000..aeed045c --- /dev/null +++ b/internal/fixtures/emapi.go @@ -0,0 +1,79 @@ +package fixtures + +import ( + "context" + "errors" + "fmt" + "net" + "os" + "strconv" + + "github.com/metal-toolbox/alloy/internal/model" +) + +// MockEMAPIClient mocks an EMAPI asset requestor +type MockEMAPIClient struct{} + +// NewMockEMAPIClient returns a MockEMAPIClient that implements the emapiRequestor interface +func NewMockEMAPIClient() *MockEMAPIClient { + return &MockEMAPIClient{} +} + +func (c *MockEMAPIClient) AssetByID(ctx context.Context, id string) (*model.Asset, error) { + if id == "borky" { + return nil, errors.New("asset is missing an ID attribute") + } + + return MockAssets[id], nil +} + +func (c *MockEMAPIClient) AssetsByOffsetLimit(ctx context.Context, offset, limit int) ([]*model.Asset, int, error) { + var total int + + totalEnv := os.Getenv("EMAPI_FIXTURE_TOTAL_ASSETS") + if totalEnv != "" { + total, _ = strconv.Atoi(totalEnv) + } else { + return nil, 0, errors.New("test fixture error, expected env var EMAPI_FIXTURE_TOTAL_ASSETS") + } + + assets := []*model.Asset{} + + if offset == limit { + return []*model.Asset{ + { + ID: fmt.Sprintf("bar-%d", offset), + BMCAddress: net.ParseIP(fmt.Sprintf("127.0.0.%d", offset)), + BMCUsername: "foo", + BMCPassword: "bar", + }, + }, total, nil + } + + i := offset + for { + if offset+limit == total { + if i >= offset+limit+1 { + break + } + } else { + if i >= offset+limit { + break + } + } + + assets = append( + assets, + &model.Asset{ + ID: "bar-" + strconv.Itoa(i), + BMCAddress: net.ParseIP(fmt.Sprintf("127.0.0.%d", i)), + BMCUsername: "foo", + BMCPassword: "bar", + }) + + i++ + + } + + return assets, total, nil +} diff --git a/internal/fixtures/fixtures.go b/internal/fixtures/fixtures.go new file mode 100644 index 00000000..c2064681 --- /dev/null +++ b/internal/fixtures/fixtures.go @@ -0,0 +1,35 @@ +package fixtures + +import ( + "github.com/bmc-toolbox/common" + "github.com/jinzhu/copier" + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +// CopyDevice returns a pointer to a copy of the given ironlib device object +func CopyDevice(src *common.Device) *common.Device { + dst := &common.Device{} + + copyOptions := copier.Option{IgnoreEmpty: true, DeepCopy: true} + + err := copier.CopyWithOption(&dst, &src, copyOptions) + if err != nil { + panic(err) + } + + return dst +} + +// CopyServerServiceComponentSlice returns a pointer to a copy of the server service components slice +func CopyServerServiceComponentSlice(src serverservice.ServerComponentSlice) serverservice.ServerComponentSlice { + dst := serverservice.ServerComponentSlice{} + + copyOptions := copier.Option{IgnoreEmpty: true, DeepCopy: true} + + err := copier.CopyWithOption(&dst, src, copyOptions) + if err != nil { + panic(err) + } + + return dst +} diff --git a/internal/fixtures/ironlib.go b/internal/fixtures/ironlib.go new file mode 100644 index 00000000..6d4997bf --- /dev/null +++ b/internal/fixtures/ironlib.go @@ -0,0 +1,30 @@ +package fixtures + +import ( + "context" + + "github.com/bmc-toolbox/common" + + ironlibm "github.com/metal-toolbox/ironlib/model" +) + +// MockIronlib mocks ironlib methods, responses +type MockIronlib struct { + // embed DeviceManager interface so we don't have to implement all interface methods + ironlibm.DeviceManager + + // device is the device object returned by this mock instance + device *common.Device +} + +func NewMockIronlib() *MockIronlib { + return &MockIronlib{} +} + +func (m *MockIronlib) SetMockDevice(d *common.Device) { + m.device = d +} + +func (m *MockIronlib) GetInventory(ctx context.Context) (*common.Device, error) { + return m.device, nil +} diff --git a/internal/fixtures/serverservice_component_types.go b/internal/fixtures/serverservice_component_types.go new file mode 100644 index 00000000..120c155d --- /dev/null +++ b/internal/fixtures/serverservice_component_types.go @@ -0,0 +1,115 @@ +package fixtures + +import ( + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +var ( + ServerServiceComponentTypes = serverservice.ServerComponentTypeSlice{ + &serverservice.ServerComponentType{ + ID: "02dc2503-b64c-439b-9f25-8e130705f14a", + Name: "Backplane-Expander", + Slug: "backplane-expander", + }, + &serverservice.ServerComponentType{ + ID: "1e0c3417-d63c-4fd5-88f7-4c525c70da12", + Name: "Mainboard", + Slug: "mainboard", + }, + &serverservice.ServerComponentType{ + ID: "262e1a12-25a0-4d84-8c79-b3941603d48e", + Name: "BIOS", + Slug: "bios", + }, + &serverservice.ServerComponentType{ + ID: "322b8728-dcc9-44e3-a139-81220c75a339", + Name: "NVMe-PCIe-SSD", + Slug: "nvme-pcie-ssd", + }, + &serverservice.ServerComponentType{ + ID: "352631d2-b1ed-4d8e-85f7-9c92ddb76679", + Name: "Sata-SSD", + Slug: "sata-ssd", + }, + &serverservice.ServerComponentType{ + ID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + Name: "Drive", + Slug: "drive", + }, + &serverservice.ServerComponentType{ + ID: "3fc448ce-ea68-4f7c-beb1-c376f594db80", + Name: "Chassis", + Slug: "chassis", + }, + &serverservice.ServerComponentType{ + ID: "4588a8fb-2e0f-4fa1-9634-9819a319b70b", + Name: "GPU", + Slug: "gpu", + }, + &serverservice.ServerComponentType{ + ID: "5850ede2-d6d6-4df7-89d6-eab9110a9113", + Name: "NIC", + Slug: "nic", + }, + &serverservice.ServerComponentType{ + ID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + Name: "PhysicalMemory", + Slug: "physicalmemory", + }, + &serverservice.ServerComponentType{ + ID: "75fc736e-fe42-4495-8e62-02d46fd08528", + Name: "CPU", + Slug: "cpu", + }, + &serverservice.ServerComponentType{ + ID: "79ad53a2-0c05-4912-a156-8311bd54017d", + Name: "TPM", + Slug: "tpm", + }, + &serverservice.ServerComponentType{ + ID: "9f5f39a4-82ed-4870-ab32-268bec45c8c8", + Name: "Enclosure", + Slug: "enclosure", + }, + &serverservice.ServerComponentType{ + ID: "cbfbbe99-8d79-49e0-8f5d-c5296932bbd1", + Name: "Sata-HDD", + Slug: "sata-hdd", + }, + &serverservice.ServerComponentType{ + ID: "ce396912-210e-4f6e-902d-9f07a8efe092", + Name: "CPLD", + Slug: "cpld", + }, + &serverservice.ServerComponentType{ + ID: "d51b438b-a767-459e-8eda-fd0700a46686", + Name: "Power-Supply", + Slug: "power-supply", + }, + &serverservice.ServerComponentType{ + ID: "e96c8557-4a71-4887-a3bb-28b6f90e5489", + Name: "BMC", + Slug: "bmc", + }, + &serverservice.ServerComponentType{ + ID: "eb82dbe3-df77-4409-833b-c44241885410", + Name: "unknown", + Slug: "unknown", + }, + &serverservice.ServerComponentType{ + ID: "ef563926-8011-4985-bc4a-7ed7e9933971", + Name: "StorageController", + Slug: "storagecontroller", + }, + } +) + +func ServerServiceSlugMap() map[string]*serverservice.ServerComponentType { + m := make(map[string]*serverservice.ServerComponentType) + + for _, ct := range ServerServiceComponentTypes { + m[ct.Slug] = ct + } + + return m +} diff --git a/internal/fixtures/serverservice_components_E3C246D4INL.go b/internal/fixtures/serverservice_components_E3C246D4INL.go new file mode 100644 index 00000000..ca04a62d --- /dev/null +++ b/internal/fixtures/serverservice_components_E3C246D4INL.go @@ -0,0 +1,1966 @@ +package fixtures + +import ( + "encoding/json" + "time" + + "github.com/google/uuid" + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +var ( + // fixture dumped with litter.Dump + // see gist for filter func: https://gist.github.com/joelrebel/b7926b9f6101dbdde146e4bebe285be9 + // + // nolint:simplifycompositelit // testdata + ServerServiceE3C246D4INLcomponents = serverservice.ServerComponentSlice{ + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "BIOS", + Vendor: "american megatrends", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 73, + 79, + 83, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 54, + 53, + 53, + 51, + 54, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 51, + 51, + 53, + 53, + 52, + 52, + 51, + 50, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 76, + 50, + 46, + 48, + 55, + 66, + 34, + 125, + 125, + }, + Tally: 0, + LastReportedAt: time.Time{}, + CreatedAt: time.Time{}, + }, + }, + ComponentTypeID: "262e1a12-25a0-4d84-8c79-b3941603d48e", + ComponentTypeName: "BIOS", + ComponentTypeSlug: "bios", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "BMC", + Vendor: "", + Model: "", + Serial: "0", + Attributes: nil, + VersionedAttributes: nil, + ComponentTypeID: "e96c8557-4a71-4887-a3bb-28b6f90e5489", + ComponentTypeName: "BMC", + ComponentTypeSlug: "bmc", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "Mainboard", + Vendor: "asrockrack", + Model: "E3C246D4I-NL", + Serial: "196231220000153", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 77, + 111, + 116, + 104, + 101, + 114, + 98, + 111, + 97, + 114, + 100, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 69, + 51, + 67, + 50, + 52, + 54, + 68, + 52, + 73, + 45, + 78, + 76, + 34, + 44, + 34, + 112, + 104, + 121, + 115, + 105, + 100, + 34, + 58, + 34, + 48, + 34, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "1e0c3417-d63c-4fd5-88f7-4c525c70da12", + ComponentTypeName: "Mainboard", + ComponentTypeSlug: "mainboard", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "PhysicalMemory", + Vendor: "micron", + Model: "18ASF2G72HZ-2G6E1", + Serial: "F0F9053F", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 83, + 79, + 68, + 73, + 77, + 77, + 32, + 68, + 68, + 82, + 52, + 32, + 83, + 121, + 110, + 99, + 104, + 114, + 111, + 110, + 111, + 117, + 115, + 32, + 50, + 54, + 54, + 54, + 32, + 77, + 72, + 122, + 32, + 40, + 48, + 46, + 52, + 32, + 110, + 115, + 41, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 49, + 56, + 65, + 83, + 70, + 50, + 71, + 55, + 50, + 72, + 90, + 45, + 50, + 71, + 54, + 69, + 49, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 67, + 104, + 97, + 110, + 110, + 101, + 108, + 65, + 45, + 68, + 73, + 77, + 77, + 48, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 49, + 55, + 49, + 55, + 57, + 56, + 54, + 57, + 49, + 56, + 52, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 50, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "PhysicalMemory", + Vendor: "micron", + Model: "18ASF2G72HZ-2G6E1", + Serial: "F0F90894", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 83, + 79, + 68, + 73, + 77, + 77, + 32, + 68, + 68, + 82, + 52, + 32, + 83, + 121, + 110, + 99, + 104, + 114, + 111, + 110, + 111, + 117, + 115, + 32, + 50, + 54, + 54, + 54, + 32, + 77, + 72, + 122, + 32, + 40, + 48, + 46, + 52, + 32, + 110, + 115, + 41, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 49, + 56, + 65, + 83, + 70, + 50, + 71, + 55, + 50, + 72, + 90, + 45, + 50, + 71, + 54, + 69, + 49, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 67, + 104, + 97, + 110, + 110, + 101, + 108, + 66, + 45, + 68, + 73, + 77, + 77, + 48, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 49, + 55, + 49, + 55, + 57, + 56, + 54, + 57, + 49, + 56, + 52, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 50, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "NIC", + Vendor: "intel", + Model: "Ethernet Controller X710 for 10GbE SFP+", + Serial: "b4:96:91:70:26:c8", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 100, + 114, + 105, + 118, + 101, + 114, + 34, + 58, + 34, + 105, + 52, + 48, + 101, + 34, + 44, + 34, + 100, + 117, + 112, + 108, + 101, + 120, + 34, + 58, + 34, + 102, + 117, + 108, + 108, + 34, + 44, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 34, + 54, + 46, + 48, + 49, + 32, + 48, + 120, + 56, + 48, + 48, + 48, + 51, + 102, + 97, + 49, + 32, + 49, + 46, + 49, + 56, + 53, + 51, + 46, + 48, + 34, + 44, + 34, + 108, + 105, + 110, + 107, + 34, + 58, + 34, + 121, + 101, + 115, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 34, + 58, + 34, + 49, + 48, + 71, + 98, + 105, + 116, + 47, + 115, + 34, + 125, + 44, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 69, + 116, + 104, + 101, + 114, + 110, + 101, + 116, + 32, + 105, + 110, + 116, + 101, + 114, + 102, + 97, + 99, + 101, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 69, + 116, + 104, + 101, + 114, + 110, + 101, + 116, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 88, + 55, + 49, + 48, + 32, + 102, + 111, + 114, + 32, + 49, + 48, + 71, + 98, + 69, + 32, + 83, + 70, + 80, + 43, + 34, + 44, + 34, + 112, + 104, + 121, + 115, + 105, + 100, + 34, + 58, + 34, + 48, + 34, + 44, + 34, + 98, + 117, + 115, + 95, + 105, + 110, + 102, + 111, + 34, + 58, + 34, + 112, + 99, + 105, + 64, + 48, + 48, + 48, + 48, + 58, + 48, + 49, + 58, + 48, + 48, + 46, + 48, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 98, + 105, + 116, + 115, + 34, + 58, + 49, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 46, + 49, + 56, + 53, + 51, + 46, + 48, + 34, + 125, + 125, + }, + Tally: 0, + LastReportedAt: time.Time{}, + CreatedAt: time.Time{}, + }, + }, + ComponentTypeID: "5850ede2-d6d6-4df7-89d6-eab9110a9113", + ComponentTypeName: "NIC", + ComponentTypeSlug: "nic", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "Drive", + Vendor: "intel", + Model: "INTEL SSDSC2KB48", + Serial: "PHYF001300HB480BGN", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 65, + 84, + 65, + 32, + 68, + 105, + 115, + 107, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 78, + 84, + 69, + 76, + 32, + 83, + 83, + 68, + 83, + 67, + 50, + 75, + 66, + 52, + 56, + 34, + 44, + 34, + 98, + 117, + 115, + 95, + 105, + 110, + 102, + 111, + 34, + 58, + 34, + 115, + 99, + 115, + 105, + 64, + 52, + 58, + 48, + 46, + 48, + 46, + 48, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 52, + 56, + 48, + 49, + 48, + 51, + 57, + 56, + 49, + 48, + 53, + 54, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "Drive", + Vendor: "intel", + Model: "INTEL SSDSC2KB48", + Serial: "PHYF001209KL480BGN", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 65, + 84, + 65, + 32, + 68, + 105, + 115, + 107, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 78, + 84, + 69, + 76, + 32, + 83, + 83, + 68, + 83, + 67, + 50, + 75, + 66, + 52, + 56, + 34, + 44, + 34, + 98, + 117, + 115, + 95, + 105, + 110, + 102, + 111, + 34, + 58, + 34, + 115, + 99, + 115, + 105, + 64, + 53, + 58, + 48, + 46, + 48, + 46, + 48, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 52, + 56, + 48, + 49, + 48, + 51, + 57, + 56, + 49, + 48, + 53, + 54, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "CPU", + Vendor: "intel", + Model: "Intel(R) Xeon(R) E-2278G CPU @ 3.40GHz", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 67, + 80, + 85, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 110, + 116, + 101, + 108, + 40, + 82, + 41, + 32, + 88, + 101, + 111, + 110, + 40, + 82, + 41, + 32, + 69, + 45, + 50, + 50, + 55, + 56, + 71, + 32, + 67, + 80, + 85, + 32, + 64, + 32, + 51, + 46, + 52, + 48, + 71, + 72, + 122, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 67, + 80, + 85, + 49, + 34, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 49, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 44, + 34, + 99, + 111, + 114, + 101, + 115, + 34, + 58, + 56, + 44, + 34, + 116, + 104, + 114, + 101, + 97, + 100, + 115, + 34, + 58, + 49, + 54, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "75fc736e-fe42-4495-8e62-02d46fd08528", + ComponentTypeName: "CPU", + ComponentTypeSlug: "cpu", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + serverservice.ServerComponent{ + UUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + ServerUUID: uuid.UUID{ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, + Name: "StorageController", + Vendor: "intel", + Model: "Cannon Lake PCH SATA AHCI Controller", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 32, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 67, + 97, + 110, + 110, + 111, + 110, + 32, + 76, + 97, + 107, + 101, + 32, + 80, + 67, + 72, + 32, + 83, + 65, + 84, + 65, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 44, + 34, + 115, + 117, + 112, + 112, + 111, + 114, + 116, + 101, + 100, + 95, + 100, + 101, + 118, + 105, + 99, + 101, + 95, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 112, + 104, + 121, + 115, + 105, + 100, + 34, + 58, + 34, + 49, + 55, + 34, + 44, + 34, + 98, + 117, + 115, + 95, + 105, + 110, + 102, + 111, + 34, + 58, + 34, + 112, + 99, + 105, + 64, + 48, + 48, + 48, + 48, + 58, + 48, + 48, + 58, + 49, + 55, + 46, + 48, + 34, + 125, + }, + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + }, + VersionedAttributes: nil, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + CreatedAt: time.Time{}, + UpdatedAt: time.Time{}, + }, + } +) diff --git a/internal/fixtures/serverservice_components_R6515_f0c8e4ac.go b/internal/fixtures/serverservice_components_R6515_f0c8e4ac.go new file mode 100644 index 00000000..304ff24a --- /dev/null +++ b/internal/fixtures/serverservice_components_R6515_f0c8e4ac.go @@ -0,0 +1,5996 @@ +package fixtures + +import ( + "encoding/json" + + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +var ( + TestserverID_Dell_f0c8e4ac = "f0c8e4ac-5cce-4370-93ff-bd3196fd3b9e" + + // fixture dumped with litter.Dump + // see gist for filter func: https://gist.github.com/joelrebel/b7926b9f6101dbdde146e4bebe285be9 + ServerServiceR6515Components_f0c8e4ac = serverservice.ServerComponentSlice{ + serverservice.ServerComponent{ + Name: "BIOS", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 73, + 79, + 83, + 32, + 67, + 111, + 110, + 102, + 105, + 103, + 117, + 114, + 97, + 116, + 105, + 111, + 110, + 32, + 67, + 117, + 114, + 114, + 101, + 110, + 116, + 32, + 83, + 101, + 116, + 116, + 105, + 110, + 103, + 115, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 46, + 54, + 46, + 54, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 53, + 57, + 34, + 44, + 34, + 112, + 114, + 101, + 118, + 105, + 111, + 117, + 115, + 34, + 58, + 91, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 46, + 50, + 46, + 52, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 53, + 57, + 34, + 125, + 93, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 66, + 73, + 79, + 83, + 34, + 125, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "262e1a12-25a0-4d84-8c79-b3941603d48e", + ComponentTypeName: "BIOS", + ComponentTypeSlug: "bios", + }, + serverservice.ServerComponent{ + Name: "BMC", + Vendor: "dell", + Model: "PowerEdge R6515", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 77, + 67, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 53, + 46, + 49, + 48, + 46, + 48, + 48, + 46, + 48, + 48, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 50, + 53, + 50, + 50, + 55, + 34, + 44, + 34, + 112, + 114, + 101, + 118, + 105, + 111, + 117, + 115, + 34, + 58, + 91, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 52, + 46, + 52, + 48, + 46, + 48, + 48, + 46, + 48, + 48, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 50, + 53, + 50, + 50, + 55, + 34, + 125, + 93, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 68, + 101, + 108, + 108, + 32, + 82, + 101, + 109, + 111, + 116, + 101, + 32, + 65, + 99, + 99, + 101, + 115, + 115, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "e96c8557-4a71-4887-a3bb-28b6f90e5489", + ComponentTypeName: "BMC", + ComponentTypeSlug: "bmc", + }, + serverservice.ServerComponent{ + Name: "Mainboard", + Vendor: "", + Model: "", + Serial: "0", + Attributes: nil, + VersionedAttributes: nil, + ComponentTypeID: "1e0c3417-d63c-4fd5-88f7-4c525c70da12", + ComponentTypeName: "Mainboard", + ComponentTypeSlug: "mainboard", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "53737518", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 55, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 55, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "537374F7", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 56, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 56, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "53737489", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 52, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 52, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "537374FE", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 50, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 50, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "537374F0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 53, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 53, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "5373745B", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 54, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 54, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "53737450", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 49, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 49, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "hynix", + Model: "", + Serial: "53737520", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 51, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 51, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 72, + 77, + 65, + 56, + 49, + 71, + 82, + 55, + 67, + 74, + 82, + 56, + 78, + 45, + 88, + 78, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "NIC", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 69, + 109, + 98, + 101, + 100, + 100, + 101, + 100, + 32, + 78, + 73, + 67, + 32, + 49, + 32, + 80, + 111, + 114, + 116, + 32, + 49, + 32, + 80, + 97, + 114, + 116, + 105, + 116, + 105, + 111, + 110, + 32, + 49, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 98, + 105, + 116, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 46, + 53, + 46, + 49, + 50, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5850ede2-d6d6-4df7-89d6-eab9110a9113", + ComponentTypeName: "NIC", + ComponentTypeSlug: "nic", + }, + serverservice.ServerComponent{ + Name: "NIC", + Vendor: "intel", + Model: "Intel(R) 10GbE 2P X710 Adapter", + Serial: "MYFLMIT04P00HQ", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 78, + 73, + 67, + 32, + 105, + 110, + 32, + 83, + 108, + 111, + 116, + 32, + 51, + 32, + 80, + 111, + 114, + 116, + 32, + 49, + 32, + 80, + 97, + 114, + 116, + 105, + 116, + 105, + 111, + 110, + 32, + 49, + 34, + 44, + 34, + 109, + 97, + 99, + 97, + 100, + 100, + 114, + 101, + 115, + 115, + 34, + 58, + 34, + 70, + 56, + 58, + 70, + 50, + 58, + 49, + 69, + 58, + 65, + 48, + 58, + 48, + 68, + 58, + 69, + 48, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 98, + 105, + 116, + 115, + 34, + 58, + 49, + 48, + 48, + 48, + 48, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 57, + 46, + 53, + 46, + 49, + 50, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 50, + 51, + 48, + 50, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 110, + 116, + 101, + 108, + 40, + 82, + 41, + 32, + 69, + 116, + 104, + 101, + 114, + 110, + 101, + 116, + 32, + 67, + 111, + 110, + 118, + 101, + 114, + 103, + 101, + 100, + 32, + 78, + 101, + 116, + 119, + 111, + 114, + 107, + 32, + 65, + 100, + 97, + 112, + 116, + 101, + 114, + 32, + 88, + 55, + 49, + 48, + 32, + 45, + 32, + 70, + 56, + 58, + 70, + 50, + 58, + 49, + 69, + 58, + 65, + 48, + 58, + 48, + 68, + 58, + 69, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5850ede2-d6d6-4df7-89d6-eab9110a9113", + ComponentTypeName: "NIC", + ComponentTypeSlug: "nic", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "micron", + Model: "MTFDDAK480TDC", + Serial: "201827F5890A", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 77, + 84, + 70, + 68, + 68, + 65, + 75, + 52, + 56, + 48, + 84, + 68, + 67, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 78, + 111, + 110, + 82, + 65, + 73, + 68, + 46, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 46, + 49, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 52, + 56, + 48, + 49, + 48, + 51, + 57, + 56, + 48, + 53, + 52, + 52, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 70, + 48, + 48, + 51, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 55, + 56, + 54, + 53, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "micron", + Model: "MTFDDAK480TDC", + Serial: "201827F58A0E", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 77, + 84, + 70, + 68, + 68, + 65, + 75, + 52, + 56, + 48, + 84, + 68, + 67, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 78, + 111, + 110, + 82, + 65, + 73, + 68, + 46, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 46, + 49, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 52, + 56, + 48, + 49, + 48, + 51, + 57, + 56, + 48, + 53, + 52, + 52, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 70, + 48, + 48, + 51, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 55, + 56, + 54, + 53, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "intel", + Model: "SSDSCKKB240G8R", + Serial: "PHYH0115000B240J", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 83, + 67, + 75, + 75, + 66, + 50, + 52, + 48, + 71, + 56, + 82, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 65, + 72, + 67, + 73, + 46, + 83, + 108, + 111, + 116, + 46, + 50, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 50, + 52, + 48, + 48, + 53, + 55, + 52, + 48, + 57, + 53, + 51, + 54, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 68, + 76, + 54, + 80, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 56, + 51, + 49, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "intel", + Model: "SSDSCKKB240G8R", + Serial: "PHYH011304S4240J", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 83, + 67, + 75, + 75, + 66, + 50, + 52, + 48, + 71, + 56, + 82, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 65, + 72, + 67, + 73, + 46, + 83, + 108, + 111, + 116, + 46, + 50, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 50, + 52, + 48, + 48, + 53, + 55, + 52, + 48, + 57, + 53, + 51, + 54, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 68, + 76, + 54, + 80, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 56, + 51, + 49, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Power-Supply", + Vendor: "dell", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0005L1PQV", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 80, + 83, + 49, + 32, + 83, + 116, + 97, + 116, + 117, + 115, + 34, + 44, + 34, + 112, + 111, + 119, + 101, + 114, + 95, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 119, + 97, + 116, + 116, + 115, + 34, + 58, + 53, + 53, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 48, + 48, + 46, + 48, + 67, + 46, + 55, + 68, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "d51b438b-a767-459e-8eda-fd0700a46686", + ComponentTypeName: "Power-Supply", + ComponentTypeSlug: "power-supply", + }, + serverservice.ServerComponent{ + Name: "Power-Supply", + Vendor: "dell", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0005L1PQZ", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 80, + 83, + 50, + 32, + 83, + 116, + 97, + 116, + 117, + 115, + 34, + 44, + 34, + 112, + 111, + 119, + 101, + 114, + 95, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 119, + 97, + 116, + 116, + 115, + 34, + 58, + 53, + 53, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 48, + 48, + 46, + 48, + 67, + 46, + 55, + 68, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "d51b438b-a767-459e-8eda-fd0700a46686", + ComponentTypeName: "Power-Supply", + ComponentTypeSlug: "power-supply", + }, + serverservice.ServerComponent{ + Name: "CPU", + Vendor: "amd", + Model: "AMD EPYC 7402P 24-Core Processor", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 105, + 100, + 34, + 58, + 34, + 67, + 80, + 85, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 49, + 34, + 44, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 82, + 101, + 112, + 114, + 101, + 115, + 101, + 110, + 116, + 115, + 32, + 116, + 104, + 101, + 32, + 112, + 114, + 111, + 112, + 101, + 114, + 116, + 105, + 101, + 115, + 32, + 111, + 102, + 32, + 97, + 32, + 80, + 114, + 111, + 99, + 101, + 115, + 115, + 111, + 114, + 32, + 97, + 116, + 116, + 97, + 99, + 104, + 101, + 100, + 32, + 116, + 111, + 32, + 116, + 104, + 105, + 115, + 32, + 83, + 121, + 115, + 116, + 101, + 109, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 67, + 80, + 85, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 49, + 34, + 44, + 34, + 97, + 114, + 99, + 104, + 105, + 116, + 101, + 99, + 116, + 117, + 114, + 101, + 34, + 58, + 34, + 120, + 56, + 54, + 34, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 57, + 48, + 48, + 44, + 34, + 99, + 111, + 114, + 101, + 115, + 34, + 58, + 50, + 52, + 44, + 34, + 116, + 104, + 114, + 101, + 97, + 100, + 115, + 34, + 58, + 52, + 56, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 48, + 120, + 56, + 51, + 48, + 49, + 48, + 53, + 50, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "75fc736e-fe42-4495-8e62-02d46fd08528", + ComponentTypeName: "CPU", + ComponentTypeSlug: "cpu", + }, + serverservice.ServerComponent{ + Name: "TPM", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 105, + 110, + 116, + 101, + 114, + 102, + 97, + 99, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 84, + 80, + 77, + 50, + 95, + 48, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 46, + 51, + 46, + 49, + 46, + 48, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 57, + 54, + 55, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 84, + 80, + 77, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "79ad53a2-0c05-4912-a156-8311bd54017d", + ComponentTypeName: "TPM", + ComponentTypeSlug: "tpm", + }, + serverservice.ServerComponent{ + Name: "CPLD", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 83, + 121, + 115, + 116, + 101, + 109, + 32, + 67, + 80, + 76, + 68, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 46, + 48, + 46, + 51, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 50, + 55, + 55, + 54, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 83, + 121, + 115, + 116, + 101, + 109, + 32, + 67, + 80, + 76, + 68, + 34, + 125, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ce396912-210e-4f6e-902d-9f07a8efe092", + ComponentTypeName: "CPLD", + ComponentTypeSlug: "cpld", + }, + serverservice.ServerComponent{ + Name: "StorageController", + Vendor: "dell", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 101, + 108, + 108, + 32, + 72, + 66, + 65, + 51, + 51, + 48, + 32, + 77, + 105, + 110, + 105, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 49, + 50, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 54, + 46, + 49, + 55, + 46, + 48, + 48, + 46, + 48, + 53, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 52, + 50, + 57, + 56, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 101, + 108, + 108, + 32, + 72, + 66, + 65, + 51, + 51, + 48, + 32, + 77, + 105, + 110, + 105, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + }, + serverservice.ServerComponent{ + Name: "StorageController", + Vendor: "dell", + Model: "", + Serial: "1", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 79, + 83, + 83, + 45, + 83, + 49, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 46, + 53, + 46, + 49, + 51, + 46, + 51, + 48, + 50, + 50, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 48, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 66, + 79, + 83, + 83, + 45, + 83, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + }, + serverservice.ServerComponent{ + Name: "StorageController", + Vendor: "dell", + Model: "", + Serial: "2", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 70, + 67, + 72, + 32, + 83, + 65, + 84, + 65, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 91, + 65, + 72, + 67, + 73, + 32, + 109, + 111, + 100, + 101, + 93, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + }, + serverservice.ServerComponent{ + Name: "StorageController", + Vendor: "dell", + Model: "", + Serial: "3", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 70, + 67, + 72, + 32, + 83, + 65, + 84, + 65, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 91, + 65, + 72, + 67, + 73, + 32, + 109, + 111, + 100, + 101, + 93, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + }, + } +) diff --git a/internal/fixtures/serverservice_components_R6515_fc167440.go b/internal/fixtures/serverservice_components_R6515_fc167440.go new file mode 100644 index 00000000..571b8a14 --- /dev/null +++ b/internal/fixtures/serverservice_components_R6515_fc167440.go @@ -0,0 +1,5736 @@ +package fixtures + +import ( + "encoding/json" + + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +var ( + TestserverID_Dell_fc167440 = "fc167440-18d3-4455-b5ee-1c8e347b3f36" + + // fixture dumped with litter.Dump + // see gist for filter func: https://gist.github.com/joelrebel/b7926b9f6101dbdde146e4bebe285be9 + ServerServiceR6515Components_fc167440 = serverservice.ServerComponentSlice{ + serverservice.ServerComponent{ + Name: "BIOS", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 73, + 79, + 83, + 32, + 67, + 111, + 110, + 102, + 105, + 103, + 117, + 114, + 97, + 116, + 105, + 111, + 110, + 32, + 67, + 117, + 114, + 114, + 101, + 110, + 116, + 32, + 83, + 101, + 116, + 116, + 105, + 110, + 103, + 115, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 46, + 50, + 46, + 52, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 53, + 57, + 34, + 44, + 34, + 112, + 114, + 101, + 118, + 105, + 111, + 117, + 115, + 34, + 58, + 91, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 46, + 51, + 46, + 54, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 53, + 57, + 34, + 125, + 93, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 66, + 73, + 79, + 83, + 34, + 125, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "262e1a12-25a0-4d84-8c79-b3941603d48e", + ComponentTypeName: "BIOS", + ComponentTypeSlug: "bios", + }, + serverservice.ServerComponent{ + Name: "BMC", + Vendor: "dell", + Model: "PowerEdge R6515", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 77, + 67, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 53, + 46, + 49, + 48, + 46, + 48, + 48, + 46, + 48, + 48, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 50, + 53, + 50, + 50, + 55, + 34, + 44, + 34, + 112, + 114, + 101, + 118, + 105, + 111, + 117, + 115, + 34, + 58, + 91, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 53, + 46, + 48, + 48, + 46, + 49, + 48, + 46, + 49, + 48, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 50, + 53, + 50, + 50, + 55, + 34, + 125, + 93, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 68, + 101, + 108, + 108, + 32, + 82, + 101, + 109, + 111, + 116, + 101, + 32, + 65, + 99, + 99, + 101, + 115, + 115, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "e96c8557-4a71-4887-a3bb-28b6f90e5489", + ComponentTypeName: "BMC", + ComponentTypeSlug: "bmc", + }, + serverservice.ServerComponent{ + Name: "Mainboard", + Vendor: "", + Model: "", + Serial: "0", + Attributes: nil, + VersionedAttributes: nil, + ComponentTypeID: "1e0c3417-d63c-4fd5-88f7-4c525c70da12", + ComponentTypeName: "Mainboard", + ComponentTypeSlug: "mainboard", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "20390266", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 50, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 50, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "20390296", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 49, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 49, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "2035D193", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 52, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 52, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "203902BC", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 53, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 53, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "20390263", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 54, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 54, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "2035D1D9", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 55, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 55, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "203902BB", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 56, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 56, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "PhysicalMemory", + Vendor: "Samsung", + Model: "", + Serial: "2035D1E1", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 32, + 65, + 51, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 68, + 73, + 77, + 77, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 65, + 51, + 34, + 44, + 34, + 112, + 97, + 114, + 116, + 95, + 110, + 117, + 109, + 98, + 101, + 114, + 34, + 58, + 34, + 77, + 51, + 57, + 51, + 65, + 49, + 75, + 52, + 51, + 68, + 66, + 50, + 45, + 67, + 87, + 69, + 34, + 44, + 34, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 56, + 49, + 57, + 50, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 50, + 48, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5ac890cc-dd92-4609-9615-ca4b05b62a8e", + ComponentTypeName: "PhysicalMemory", + ComponentTypeSlug: "physicalmemory", + }, + serverservice.ServerComponent{ + Name: "NIC", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 69, + 109, + 98, + 101, + 100, + 100, + 101, + 100, + 32, + 78, + 73, + 67, + 32, + 49, + 32, + 80, + 111, + 114, + 116, + 32, + 49, + 32, + 80, + 97, + 114, + 116, + 105, + 116, + 105, + 111, + 110, + 32, + 49, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 98, + 105, + 116, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 49, + 46, + 56, + 49, + 46, + 51, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5850ede2-d6d6-4df7-89d6-eab9110a9113", + ComponentTypeName: "NIC", + ComponentTypeSlug: "nic", + }, + serverservice.ServerComponent{ + Name: "NIC", + Vendor: "intel", + Model: "Intel(R) 25GbE 2P E810-XXV Adptr", + Serial: "MYFLMIT1A100S1", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 78, + 73, + 67, + 32, + 105, + 110, + 32, + 83, + 108, + 111, + 116, + 32, + 51, + 32, + 80, + 111, + 114, + 116, + 32, + 50, + 32, + 80, + 97, + 114, + 116, + 105, + 116, + 105, + 111, + 110, + 32, + 49, + 34, + 44, + 34, + 109, + 97, + 99, + 97, + 100, + 100, + 114, + 101, + 115, + 115, + 34, + 58, + 34, + 66, + 52, + 58, + 57, + 54, + 58, + 57, + 49, + 58, + 68, + 49, + 58, + 70, + 52, + 58, + 65, + 68, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 98, + 105, + 116, + 115, + 34, + 58, + 50, + 53, + 48, + 48, + 48, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 48, + 46, + 53, + 46, + 49, + 51, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 57, + 48, + 50, + 56, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 73, + 110, + 116, + 101, + 108, + 40, + 82, + 41, + 32, + 69, + 116, + 104, + 101, + 114, + 110, + 101, + 116, + 32, + 50, + 53, + 71, + 32, + 50, + 80, + 32, + 69, + 56, + 49, + 48, + 45, + 88, + 88, + 86, + 32, + 65, + 100, + 97, + 112, + 116, + 101, + 114, + 32, + 45, + 32, + 66, + 52, + 58, + 57, + 54, + 58, + 57, + 49, + 58, + 68, + 49, + 58, + 70, + 52, + 58, + 65, + 68, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "5850ede2-d6d6-4df7-89d6-eab9110a9113", + ComponentTypeName: "NIC", + ComponentTypeSlug: "nic", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "SAMSUNG", + Model: "MZ7LH480HBHQ0D3", + Serial: "S5YJNA0R800552", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 77, + 90, + 55, + 76, + 72, + 52, + 56, + 48, + 72, + 66, + 72, + 81, + 48, + 68, + 51, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 78, + 111, + 110, + 82, + 65, + 73, + 68, + 46, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 46, + 49, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 52, + 56, + 48, + 49, + 48, + 51, + 57, + 56, + 48, + 53, + 52, + 52, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 72, + 71, + 53, + 56, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 56, + 54, + 50, + 50, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "SAMSUNG", + Model: "MZ7LH480HBHQ0D3", + Serial: "S5YJNA0R800525", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 77, + 90, + 55, + 76, + 72, + 52, + 56, + 48, + 72, + 66, + 72, + 81, + 48, + 68, + 51, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 78, + 111, + 110, + 82, + 65, + 73, + 68, + 46, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 46, + 49, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 52, + 56, + 48, + 49, + 48, + 51, + 57, + 56, + 48, + 53, + 52, + 52, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 72, + 71, + 53, + 56, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 56, + 54, + 50, + 50, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 105, + 110, + 32, + 66, + 97, + 99, + 107, + 112, + 108, + 97, + 110, + 101, + 32, + 49, + 32, + 111, + 102, + 32, + 73, + 110, + 116, + 101, + 103, + 114, + 97, + 116, + 101, + 100, + 32, + 83, + 116, + 111, + 114, + 97, + 103, + 101, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "intel", + Model: "SSDSCKKB240G8R", + Serial: "PHYH124303Z1240J", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 83, + 67, + 75, + 75, + 66, + 50, + 52, + 48, + 71, + 56, + 82, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 65, + 72, + 67, + 73, + 46, + 83, + 108, + 111, + 116, + 46, + 50, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 50, + 52, + 48, + 48, + 53, + 55, + 52, + 48, + 57, + 53, + 51, + 54, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 68, + 76, + 54, + 82, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 56, + 51, + 49, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 48, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Drive", + Vendor: "intel", + Model: "SSDSCKKB240G8R", + Serial: "PHYH124302RL240J", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 44, + 34, + 112, + 114, + 111, + 100, + 117, + 99, + 116, + 95, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 83, + 67, + 75, + 75, + 66, + 50, + 52, + 48, + 71, + 56, + 82, + 34, + 44, + 34, + 100, + 114, + 105, + 118, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 83, + 83, + 68, + 34, + 44, + 34, + 115, + 116, + 111, + 114, + 97, + 103, + 101, + 95, + 99, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 34, + 58, + 34, + 65, + 72, + 67, + 73, + 46, + 83, + 108, + 111, + 116, + 46, + 50, + 45, + 49, + 34, + 44, + 34, + 112, + 114, + 111, + 116, + 111, + 99, + 111, + 108, + 34, + 58, + 34, + 83, + 65, + 84, + 65, + 34, + 44, + 34, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 50, + 52, + 48, + 48, + 53, + 55, + 52, + 48, + 57, + 53, + 51, + 54, + 44, + 34, + 98, + 108, + 111, + 99, + 107, + 95, + 115, + 105, + 122, + 101, + 95, + 98, + 121, + 116, + 101, + 115, + 34, + 58, + 53, + 49, + 50, + 44, + 34, + 99, + 97, + 112, + 97, + 98, + 108, + 101, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 44, + 34, + 110, + 101, + 103, + 111, + 116, + 105, + 97, + 116, + 101, + 100, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 68, + 76, + 54, + 82, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 56, + 51, + 49, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 105, + 115, + 107, + 32, + 49, + 32, + 111, + 110, + 32, + 65, + 72, + 67, + 73, + 32, + 67, + 111, + 110, + 116, + 114, + 111, + 108, + 108, + 101, + 114, + 32, + 105, + 110, + 32, + 115, + 108, + 111, + 116, + 32, + 50, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "3717d747-3cc3-4800-822c-4c7a9ac2c314", + ComponentTypeName: "Drive", + ComponentTypeSlug: "drive", + }, + serverservice.ServerComponent{ + Name: "Power-Supply", + Vendor: "dell", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0015C1STJ", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 80, + 83, + 49, + 32, + 83, + 116, + 97, + 116, + 117, + 115, + 34, + 44, + 34, + 112, + 111, + 119, + 101, + 114, + 95, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 119, + 97, + 116, + 116, + 115, + 34, + 58, + 53, + 53, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 48, + 48, + 46, + 48, + 67, + 46, + 55, + 68, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "d51b438b-a767-459e-8eda-fd0700a46686", + ComponentTypeName: "Power-Supply", + ComponentTypeSlug: "power-supply", + }, + serverservice.ServerComponent{ + Name: "Power-Supply", + Vendor: "dell", + Model: "PWR SPLY,550W,RDNT,DELTA", + Serial: "CNDED0015C1STY", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 80, + 83, + 50, + 32, + 83, + 116, + 97, + 116, + 117, + 115, + 34, + 44, + 34, + 112, + 111, + 119, + 101, + 114, + 95, + 99, + 97, + 112, + 97, + 99, + 105, + 116, + 121, + 95, + 119, + 97, + 116, + 116, + 115, + 34, + 58, + 53, + 53, + 48, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 48, + 48, + 46, + 48, + 67, + 46, + 55, + 68, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "d51b438b-a767-459e-8eda-fd0700a46686", + ComponentTypeName: "Power-Supply", + ComponentTypeSlug: "power-supply", + }, + serverservice.ServerComponent{ + Name: "CPU", + Vendor: "amd", + Model: "AMD EPYC 7443P 24-Core Processor", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 105, + 100, + 34, + 58, + 34, + 67, + 80, + 85, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 49, + 34, + 44, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 82, + 101, + 112, + 114, + 101, + 115, + 101, + 110, + 116, + 115, + 32, + 116, + 104, + 101, + 32, + 112, + 114, + 111, + 112, + 101, + 114, + 116, + 105, + 101, + 115, + 32, + 111, + 102, + 32, + 97, + 32, + 80, + 114, + 111, + 99, + 101, + 115, + 115, + 111, + 114, + 32, + 97, + 116, + 116, + 97, + 99, + 104, + 101, + 100, + 32, + 116, + 111, + 32, + 116, + 104, + 105, + 115, + 32, + 83, + 121, + 115, + 116, + 101, + 109, + 34, + 44, + 34, + 115, + 108, + 111, + 116, + 34, + 58, + 34, + 67, + 80, + 85, + 46, + 83, + 111, + 99, + 107, + 101, + 116, + 46, + 49, + 34, + 44, + 34, + 97, + 114, + 99, + 104, + 105, + 116, + 101, + 99, + 116, + 117, + 114, + 101, + 34, + 58, + 34, + 120, + 56, + 54, + 34, + 44, + 34, + 99, + 108, + 111, + 99, + 107, + 95, + 115, + 112, + 101, + 101, + 100, + 95, + 104, + 122, + 34, + 58, + 51, + 57, + 48, + 48, + 44, + 34, + 99, + 111, + 114, + 101, + 115, + 34, + 58, + 50, + 52, + 44, + 34, + 116, + 104, + 114, + 101, + 97, + 100, + 115, + 34, + 58, + 52, + 56, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 48, + 120, + 65, + 48, + 48, + 49, + 49, + 49, + 68, + 34, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "75fc736e-fe42-4495-8e62-02d46fd08528", + ComponentTypeName: "CPU", + ComponentTypeSlug: "cpu", + }, + serverservice.ServerComponent{ + Name: "TPM", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 105, + 110, + 116, + 101, + 114, + 102, + 97, + 99, + 101, + 95, + 116, + 121, + 112, + 101, + 34, + 58, + 34, + 84, + 80, + 77, + 50, + 95, + 48, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 46, + 51, + 46, + 50, + 46, + 56, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 57, + 54, + 55, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 84, + 80, + 77, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "79ad53a2-0c05-4912-a156-8311bd54017d", + ComponentTypeName: "TPM", + ComponentTypeSlug: "tpm", + }, + serverservice.ServerComponent{ + Name: "CPLD", + Vendor: "", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 83, + 121, + 115, + 116, + 101, + 109, + 32, + 67, + 80, + 76, + 68, + 34, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 46, + 48, + 46, + 55, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 50, + 55, + 55, + 54, + 51, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 83, + 121, + 115, + 116, + 101, + 109, + 32, + 67, + 80, + 76, + 68, + 34, + 125, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ce396912-210e-4f6e-902d-9f07a8efe092", + ComponentTypeName: "CPLD", + ComponentTypeSlug: "cpld", + }, + serverservice.ServerComponent{ + Name: "StorageController", + Vendor: "dell", + Model: "", + Serial: "0", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 68, + 101, + 108, + 108, + 32, + 72, + 66, + 65, + 51, + 51, + 48, + 32, + 77, + 105, + 110, + 105, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 49, + 50, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 49, + 54, + 46, + 49, + 55, + 46, + 48, + 49, + 46, + 48, + 48, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 49, + 48, + 52, + 50, + 57, + 56, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 68, + 101, + 108, + 108, + 32, + 72, + 66, + 65, + 51, + 51, + 48, + 32, + 77, + 105, + 110, + 105, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + }, + serverservice.ServerComponent{ + Name: "StorageController", + Vendor: "dell", + Model: "", + Serial: "1", + Attributes: []serverservice.Attributes{ + serverservice.Attributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 100, + 101, + 115, + 99, + 114, + 105, + 112, + 116, + 105, + 111, + 110, + 34, + 58, + 34, + 66, + 79, + 83, + 83, + 45, + 83, + 49, + 34, + 44, + 34, + 115, + 112, + 101, + 101, + 100, + 95, + 103, + 98, + 112, + 115, + 34, + 58, + 54, + 125, + }, + }, + }, + VersionedAttributes: []serverservice.VersionedAttributes{ + serverservice.VersionedAttributes{ + Namespace: "server.components", + Data: json.RawMessage{ + 123, + 34, + 102, + 105, + 114, + 109, + 119, + 97, + 114, + 101, + 34, + 58, + 123, + 34, + 105, + 110, + 115, + 116, + 97, + 108, + 108, + 101, + 100, + 34, + 58, + 34, + 50, + 46, + 53, + 46, + 49, + 51, + 46, + 51, + 48, + 50, + 52, + 34, + 44, + 34, + 115, + 111, + 102, + 116, + 119, + 97, + 114, + 101, + 95, + 105, + 100, + 34, + 58, + 34, + 48, + 34, + 44, + 34, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97, + 34, + 58, + 123, + 34, + 110, + 97, + 109, + 101, + 34, + 58, + 34, + 66, + 79, + 83, + 83, + 45, + 83, + 49, + 34, + 125, + 125, + 44, + 34, + 115, + 116, + 97, + 116, + 117, + 115, + 34, + 58, + 123, + 34, + 72, + 101, + 97, + 108, + 116, + 104, + 34, + 58, + 34, + 79, + 75, + 34, + 44, + 34, + 83, + 116, + 97, + 116, + 101, + 34, + 58, + 34, + 69, + 110, + 97, + 98, + 108, + 101, + 100, + 34, + 125, + 125, + }, + Tally: 0, + }, + }, + ComponentTypeID: "ef563926-8011-4985-bc4a-7ed7e9933971", + ComponentTypeName: "StorageController", + ComponentTypeSlug: "storagecontroller", + }, + } +) diff --git a/internal/fixtures/serverservice_components_fc167440.json b/internal/fixtures/serverservice_components_fc167440.json new file mode 100644 index 00000000..9ae1fe06 --- /dev/null +++ b/internal/fixtures/serverservice_components_fc167440.json @@ -0,0 +1 @@ +{"page_size":100,"page":1,"page_count":24,"_links":{"self":{"href":"/api/v1/servers/fc167440-18d3-4455-b5ee-1c8e347b3f36/components"},"first":{"href":"/api/v1/servers/fc167440-18d3-4455-b5ee-1c8e347b3f36/components?page=1"},"last":{"href":"/api/v1/servers/fc167440-18d3-4455-b5ee-1c8e347b3f36/components?page=0"}},"records":[{"uuid":"01aa5f37-9ba9-491c-b1c8-7419b78f9a31","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Mainboard","vendor":"","model":"","serial":"0","attributes":null,"versioned_attributes":null,"component_type_id":"1e0c3417-d63c-4fd5-88f7-4c525c70da12","component_type_name":"Mainboard","component_type_slug":"mainboard","created_at":"2022-07-11T13:31:09.637527Z","updated_at":"2022-07-11T13:31:09.637527Z"},{"uuid":"0246a343-9c3d-49f5-bdc3-16d74b31a38b","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"StorageController","vendor":"dell","model":"","serial":"0","attributes":[{"namespace":"server.components","data":{"description":"Dell HBA330 Mini","speed_gbps":12},"created_at":"2022-07-11T13:31:10.027391Z","updated_at":"2022-07-11T13:31:10.027391Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"16.17.01.00","metadata":{"name":"Dell HBA330 Mini"},"software_id":"104298"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:10.021901Z","created_at":"2022-07-11T13:31:10.021901Z"}],"component_type_id":"ef563926-8011-4985-bc4a-7ed7e9933971","component_type_name":"StorageController","component_type_slug":"storagecontroller","created_at":"2022-07-11T13:31:10.010047Z","updated_at":"2022-07-11T13:31:10.010047Z"},{"uuid":"09f9ca07-a0c9-4c51-98d4-0a6ab6ea16be","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"203902BC","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A5","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A5"},"created_at":"2022-07-11T13:31:09.702698Z","updated_at":"2022-07-11T13:31:09.702698Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.696258Z","created_at":"2022-07-11T13:31:09.696258Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.690571Z","updated_at":"2022-07-11T13:31:09.690571Z"},{"uuid":"2c34031f-78a9-4cb0-8adc-7ccb2b6a0556","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"2035D193","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A4","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A4"},"created_at":"2022-07-11T13:31:09.686309Z","updated_at":"2022-07-11T13:31:09.686309Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.679076Z","created_at":"2022-07-11T13:31:09.679076Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.673602Z","updated_at":"2022-07-11T13:31:09.673602Z"},{"uuid":"36edd6b0-62d2-4f62-8390-8dd049a11409","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"20390266","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A2","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A2"},"created_at":"2022-07-11T13:31:09.654605Z","updated_at":"2022-07-11T13:31:09.654605Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.649368Z","created_at":"2022-07-11T13:31:09.649368Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.644056Z","updated_at":"2022-07-11T13:31:09.644056Z"},{"uuid":"472b82bf-ec28-4df2-9c06-6217993de33c","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"NIC","vendor":"intel","model":"Intel(R) 25GbE 2P E810-XXV Adptr","serial":"MYFLMIT1A100S1","attributes":[{"namespace":"server.components","data":{"description":"NIC in Slot 3 Port 2 Partition 1","macaddress":"B4:96:91:D1:F4:AD","speed_bits":250006},"created_at":"2022-07-11T13:31:09.810188Z","updated_at":"2022-07-11T13:31:09.810188Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"20.5.13","metadata":{"name":"Intel(R) Ethernet 25G 2P E810-XXV Adapter - B4:96:91:D1:F4:AD"},"software_id":"109028"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.804706Z","created_at":"2022-07-11T13:31:09.804706Z"}],"component_type_id":"5850ede2-d6d6-4df7-89d6-eab9110a9113","component_type_name":"NIC","component_type_slug":"nic","created_at":"2022-07-11T13:31:09.799974Z","updated_at":"2022-07-11T13:31:09.799974Z"},{"uuid":"578d474f-230f-4a16-91a9-83bf91b7b853","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Power-Supply","vendor":"dell","model":"PWR SPLY,550W,RDNT,DELTA","serial":"CNDED0015C1STJ","attributes":[{"namespace":"server.components","data":{"description":"PS1 Status","power_capacity_watts":550},"created_at":"2022-07-11T13:31:09.898487Z","updated_at":"2022-07-11T13:31:09.898487Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"00.0C.7D"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.890766Z","created_at":"2022-07-11T13:31:09.890766Z"}],"component_type_id":"d51b438b-a767-459e-8eda-fd0700a46686","component_type_name":"Power-Supply","component_type_slug":"power-supply","created_at":"2022-07-11T13:31:09.884745Z","updated_at":"2022-07-11T13:31:09.884745Z"},{"uuid":"6917f1cf-570b-4731-9a46-5af66314abb0","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"20390296","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A1","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A1"},"created_at":"2022-07-11T13:31:09.668409Z","updated_at":"2022-07-11T13:31:09.668409Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.663997Z","created_at":"2022-07-11T13:31:09.663997Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.658847Z","updated_at":"2022-07-11T13:31:09.658847Z"},{"uuid":"6b6f55ea-93c5-485e-92ed-e4633951134e","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"2035D1E1","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A3","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A3"},"created_at":"2022-07-11T13:31:09.774645Z","updated_at":"2022-07-11T13:31:09.774645Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.767269Z","created_at":"2022-07-11T13:31:09.767269Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.760861Z","updated_at":"2022-07-11T13:31:09.760861Z"},{"uuid":"704829b4-e50a-44c2-b935-021903954169","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"CPLD","vendor":"","model":"","serial":"0","attributes":[{"namespace":"server.components","data":{"description":"System CPLD"},"created_at":"2022-07-11T13:31:10.001563Z","updated_at":"2022-07-11T13:31:10.001563Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"1.0.7","metadata":{"name":"System CPLD"},"software_id":"27763"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.986147Z","created_at":"2022-07-11T13:31:09.986147Z"}],"component_type_id":"ce396912-210e-4f6e-902d-9f07a8efe092","component_type_name":"CPLD","component_type_slug":"cpld","created_at":"2022-07-11T13:31:09.97959Z","updated_at":"2022-07-11T13:31:09.97959Z"},{"uuid":"76850f12-3001-4386-aa0f-89b3b9a0eaf3","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Drive","vendor":"intel","model":"SSDSCKKB240G8R","serial":"PHYH124302RL240J","attributes":[{"namespace":"server.components","data":{"block_size_bytes":512,"capable_speed_gbps":6,"capacity_bytes":240057409536,"description":"Disk 1 on AHCI Controller in slot 2","drive_type":"SSD","negotiated_speed_gbps":6,"product_name":"SSDSCKKB240G8R","protocol":"SATA","storage_controller":"AHCI.Slot.2-1"},"created_at":"2022-07-11T13:31:09.878307Z","updated_at":"2022-07-11T13:31:09.878307Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"DL6R","metadata":{"name":"Disk 1 on AHCI Controller in slot 2"},"software_id":"108313"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.873684Z","created_at":"2022-07-11T13:31:09.873684Z"}],"component_type_id":"3717d747-3cc3-4800-822c-4c7a9ac2c314","component_type_name":"Drive","component_type_slug":"drive","created_at":"2022-07-11T13:31:09.867861Z","updated_at":"2022-07-11T13:31:09.867861Z"},{"uuid":"7736edff-0060-4eb9-8f0c-ae456b4cb7d9","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"CPU","vendor":"amd","model":"AMD EPYC 7443P 24-Core Processor","serial":"0","attributes":[{"namespace":"server.components","data":{"architecture":"x86","clock_speed_hz":3900,"cores":24,"description":"Represents the properties of a Processor attached to this System","id":"CPU.Socket.1","slot":"CPU.Socket.1","threads":48},"created_at":"2022-07-11T13:31:09.939701Z","updated_at":"2022-07-11T13:31:09.939701Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"0xA00111D"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.933548Z","created_at":"2022-07-11T13:31:09.933548Z"}],"component_type_id":"75fc736e-fe42-4495-8e62-02d46fd08528","component_type_name":"CPU","component_type_slug":"cpu","created_at":"2022-07-11T13:31:09.923881Z","updated_at":"2022-07-11T13:31:09.923881Z"},{"uuid":"7969b537-d1f4-419c-895e-68187ffc3020","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Drive","vendor":"SAMSUNG","model":"MZ7LH480HBHQ0D3","serial":"S5YJNA0R800552","attributes":[{"namespace":"server.components","data":{"block_size_bytes":512,"capable_speed_gbps":6,"capacity_bytes":480103980544,"description":"Disk 1 in Backplane 1 of Integrated Storage Controller 1","drive_type":"SSD","negotiated_speed_gbps":6,"product_name":"MZ7LH480HBHQ0D3","protocol":"SATA","storage_controller":"NonRAID.Integrated.1-1"},"created_at":"2022-07-11T13:31:09.828701Z","updated_at":"2022-07-11T13:31:09.828701Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"HG58","metadata":{"name":"Disk 1 in Backplane 1 of Integrated Storage Controller 1"},"software_id":"108622"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.823289Z","created_at":"2022-07-11T13:31:09.823289Z"}],"component_type_id":"3717d747-3cc3-4800-822c-4c7a9ac2c314","component_type_name":"Drive","component_type_slug":"drive","created_at":"2022-07-11T13:31:09.815618Z","updated_at":"2022-07-11T13:31:09.815618Z"},{"uuid":"893a92ec-60bc-491e-a34c-b386693fbb57","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"NIC","vendor":"","model":"","serial":"0","attributes":[{"namespace":"server.components","data":{"description":"Embedded NIC 1 Port 1 Partition 1","speed_bits":6},"created_at":"2022-07-11T13:31:09.795693Z","updated_at":"2022-07-11T13:31:09.795693Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"21.81.3"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.78597Z","created_at":"2022-07-11T13:31:09.78597Z"}],"component_type_id":"5850ede2-d6d6-4df7-89d6-eab9110a9113","component_type_name":"NIC","component_type_slug":"nic","created_at":"2022-07-11T13:31:09.781314Z","updated_at":"2022-07-11T13:31:09.781314Z"},{"uuid":"8ed70f2b-592d-4454-9e37-e91454d2f732","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"StorageController","vendor":"dell","model":"","serial":"1","attributes":[{"namespace":"server.components","data":{"description":"BOSS-S1","speed_gbps":6},"created_at":"2022-07-11T13:31:10.043486Z","updated_at":"2022-07-11T13:31:10.043486Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"2.5.13.3024","metadata":{"name":"BOSS-S1"},"software_id":"0"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:10.039053Z","created_at":"2022-07-11T13:31:10.039053Z"}],"component_type_id":"ef563926-8011-4985-bc4a-7ed7e9933971","component_type_name":"StorageController","component_type_slug":"storagecontroller","created_at":"2022-07-11T13:31:10.033692Z","updated_at":"2022-07-11T13:31:10.033692Z"},{"uuid":"91fa9dfa-4272-43a6-8420-02b46fbacf45","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Drive","vendor":"intel","model":"SSDSCKKB240G8R","serial":"PHYH124303Z1240J","attributes":[{"namespace":"server.components","data":{"block_size_bytes":512,"capable_speed_gbps":6,"capacity_bytes":240057409536,"description":"Disk 0 on AHCI Controller in slot 2","drive_type":"SSD","negotiated_speed_gbps":6,"product_name":"SSDSCKKB240G8R","protocol":"SATA","storage_controller":"AHCI.Slot.2-1"},"created_at":"2022-07-11T13:31:09.862227Z","updated_at":"2022-07-11T13:31:09.862227Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"DL6R","metadata":{"name":"Disk 0 on AHCI Controller in slot 2"},"software_id":"108313"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.85761Z","created_at":"2022-07-11T13:31:09.85761Z"}],"component_type_id":"3717d747-3cc3-4800-822c-4c7a9ac2c314","component_type_name":"Drive","component_type_slug":"drive","created_at":"2022-07-11T13:31:09.851823Z","updated_at":"2022-07-11T13:31:09.851823Z"},{"uuid":"a0e6bca4-1779-4a1e-82bd-eabc388de07a","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"203902BB","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A8","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A8"},"created_at":"2022-07-11T13:31:09.747642Z","updated_at":"2022-07-11T13:31:09.747642Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.743383Z","created_at":"2022-07-11T13:31:09.743383Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.738621Z","updated_at":"2022-07-11T13:31:09.738621Z"},{"uuid":"a68418e4-8d77-4eb3-a022-17befbe9a0c4","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Drive","vendor":"SAMSUNG","model":"MZ7LH480HBHQ0D3","serial":"S5YJNA0R800525","attributes":[{"namespace":"server.components","data":{"block_size_bytes":512,"capable_speed_gbps":6,"capacity_bytes":480103980544,"description":"Disk 0 in Backplane 1 of Integrated Storage Controller 1","drive_type":"SSD","negotiated_speed_gbps":6,"product_name":"MZ7LH480HBHQ0D3","protocol":"SATA","storage_controller":"NonRAID.Integrated.1-1"},"created_at":"2022-07-11T13:31:09.845135Z","updated_at":"2022-07-11T13:31:09.845135Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"HG58","metadata":{"name":"Disk 0 in Backplane 1 of Integrated Storage Controller 1"},"software_id":"108622"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.840678Z","created_at":"2022-07-11T13:31:09.840678Z"}],"component_type_id":"3717d747-3cc3-4800-822c-4c7a9ac2c314","component_type_name":"Drive","component_type_slug":"drive","created_at":"2022-07-11T13:31:09.83362Z","updated_at":"2022-07-11T13:31:09.83362Z"},{"uuid":"a78f3b34-98d8-4703-82b3-505bf3d39d19","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"20390263","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A6","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A6"},"created_at":"2022-07-11T13:31:09.71841Z","updated_at":"2022-07-11T13:31:09.71841Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.713899Z","created_at":"2022-07-11T13:31:09.713899Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.707658Z","updated_at":"2022-07-11T13:31:09.707658Z"},{"uuid":"ab86effb-db03-49e3-990b-e324286a5cee","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"BMC","vendor":"dell","model":"PowerEdge R6515","serial":"0","attributes":[{"namespace":"server.components","data":{"description":"BMC"},"created_at":"2022-07-11T13:31:09.632315Z","updated_at":"2022-07-11T13:31:09.632315Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"5.10.00.00","metadata":{"name":"Integrated Dell Remote Access Controller"},"previous":[{"installed":"5.00.10.10","software_id":"25227"}],"software_id":"25227"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.626403Z","created_at":"2022-07-11T13:31:09.626403Z"}],"component_type_id":"e96c8557-4a71-4887-a3bb-28b6f90e5489","component_type_name":"BMC","component_type_slug":"bmc","created_at":"2022-07-11T13:31:09.62081Z","updated_at":"2022-07-11T13:31:09.62081Z"},{"uuid":"e7021f58-01fd-483e-8d72-8eab7c409e13","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"TPM","vendor":"","model":"","serial":"0","attributes":[{"namespace":"server.components","data":{"interface_type":"TPM2_0"},"created_at":"2022-07-11T13:31:09.965015Z","updated_at":"2022-07-11T13:31:09.965015Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"1.3.2.8","metadata":{"name":"TPM"},"software_id":"109673"},"status":{"Health":"","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.955515Z","created_at":"2022-07-11T13:31:09.955515Z"}],"component_type_id":"79ad53a2-0c05-4912-a156-8311bd54017d","component_type_name":"TPM","component_type_slug":"tpm","created_at":"2022-07-11T13:31:09.949057Z","updated_at":"2022-07-11T13:31:09.949057Z"},{"uuid":"e771adb0-1e92-440a-b250-15af0f8c5925","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"Power-Supply","vendor":"dell","model":"PWR SPLY,550W,RDNT,DELTA","serial":"CNDED0015C1STY","attributes":[{"namespace":"server.components","data":{"description":"PS2 Status","power_capacity_watts":550},"created_at":"2022-07-11T13:31:09.9159Z","updated_at":"2022-07-11T13:31:09.9159Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"00.0C.7D"},"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.908869Z","created_at":"2022-07-11T13:31:09.908869Z"}],"component_type_id":"d51b438b-a767-459e-8eda-fd0700a46686","component_type_name":"Power-Supply","component_type_slug":"power-supply","created_at":"2022-07-11T13:31:09.903271Z","updated_at":"2022-07-11T13:31:09.903271Z"},{"uuid":"f5eab555-ff12-4dfc-afa2-f38c79ddb42a","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"BIOS","vendor":"","model":"","serial":"0","attributes":[{"namespace":"server.components","data":{"description":"BIOS Configuration Current Settings"},"created_at":"2022-07-11T13:31:09.614961Z","updated_at":"2022-07-11T13:31:09.614961Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"firmware":{"installed":"2.2.4","metadata":{"name":"BIOS"},"previous":[{"installed":"2.3.6","software_id":"159"}],"software_id":"159"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.608324Z","created_at":"2022-07-11T13:31:09.608324Z"}],"component_type_id":"262e1a12-25a0-4d84-8c79-b3941603d48e","component_type_name":"BIOS","component_type_slug":"bios","created_at":"2022-07-11T13:31:09.601842Z","updated_at":"2022-07-11T13:31:09.601842Z"},{"uuid":"fef81b8f-6c95-4cc8-b6d5-edf2a8997ee6","server_uuid":"fc167440-18d3-4455-b5ee-1c8e347b3f36","name":"PhysicalMemory","vendor":"Samsung","model":"","serial":"2035D1D9","attributes":[{"namespace":"server.components","data":{"clock_speed_hz":3200,"description":"DIMM A7","part_number":"M393A1K43DB2-CWE","size_bytes":8192,"slot":"DIMM.Socket.A7"},"created_at":"2022-07-11T13:31:09.734624Z","updated_at":"2022-07-11T13:31:09.734624Z"}],"versioned_attributes":[{"namespace":"server.components","data":{"status":{"Health":"OK","State":"Enabled"}},"tally":0,"last_reported_at":"2022-07-11T13:31:09.729537Z","created_at":"2022-07-11T13:31:09.729537Z"}],"component_type_id":"5ac890cc-dd92-4609-9615-ca4b05b62a8e","component_type_name":"PhysicalMemory","component_type_slug":"physicalmemory","created_at":"2022-07-11T13:31:09.723557Z","updated_at":"2022-07-11T13:31:09.723557Z"}]} diff --git a/internal/model/errors.go b/internal/model/errors.go new file mode 100644 index 00000000..5b89b3c2 --- /dev/null +++ b/internal/model/errors.go @@ -0,0 +1,7 @@ +package model + +import "errors" + +var ( + ErrConfig = errors.New("configuration error") +) diff --git a/internal/model/model.go b/internal/model/model.go new file mode 100644 index 00000000..3901d270 --- /dev/null +++ b/internal/model/model.go @@ -0,0 +1,92 @@ +package model + +import ( + "net" + + "github.com/bmc-toolbox/common" +) + +var ( + // App logging level. + LogLevel int + + LogLevelInfo = 0 + LogLevelDebug = 1 + LogLevelTrace = 2 +) + +// Asset represents attributes of an asset retrieved from the asset store +type Asset struct { + // ID is the asset ID + ID string + // Vendor is the asset vendor + Vendor string + // Model is the asset model + Model string + // Username is the BMC login username + BMCUsername string + // Password is the BMC login password + BMCPassword string + // Address is the BMC IP address + BMCAddress net.IP +} + +// AssetDevice embeds a common device along with its Asset ID +type AssetDevice struct { + *common.Device + ID string +} + +// Config holds application configuration read from a YAML or set by env variables. +// +// nolint:govet // prefer readability over field alignment optimization for this case. +type Config struct { + // File is the configuration file path + File string + // LogLevel is the app verbose logging level. + LogLevel int + // AppKind is one of inband, outofband + AppKind string `mapstructure:"app_kind"` + + // Out of band collector configuration + CollectorOutofband struct { + Concurrency int `mapstructure:"concurrency"` + } `mapstructure:"collector_outofband"` + + // AssetGetter is where alloy looks up assets information like BMC credentials + // to collect inventory. + AssetGetter struct { + // supported parameters: csv OR emapi + Kind string `mapstructure:"kind"` + + // Csv is the CSV asset getter type configuration. + Csv struct { + File string `mapstructure:"file"` + } `mapstructure:"csv"` + + // Emapi is the EMAPI asset getter type configuration + Emapi struct { + AuthToken string `mapstructure:"auth_token"` + ConsumerToken string `mapstructure:"consumer_token"` + Endpoint string `mapstructure:"endpoint"` + Facility string `mapstructure:"facility"` + Concurrency int `mapstructure:"concurrency"` + BatchSize int `mapstructure:"batch_size"` + CustomHeaders map[string]string `mapstructure:"custom_headers"` + } `mapstructure:"emapi"` + } `mapstructure:"asset_getter"` + + // Publisher is the inventory store where alloy writes collected inventory data + InventoryPublisher struct { + // supported parameters: stdout, serverService + Kind string `mapstructure:"kind"` + + // ServerService is the Hollow server inventory store + // https://github.com/metal-toolbox/hollow-serverservice + ServerService struct { + Endpoint string `mapstructure:"endpoint"` + AuthToken string `mapstructure:"auth_token"` + Concurrency int `mapstructure:"concurrency"` + } `mapstructure:"serverService"` + } `mapstructure:"inventory_publisher"` +} diff --git a/internal/publish/errors.go b/internal/publish/errors.go new file mode 100644 index 00000000..4ffc924a --- /dev/null +++ b/internal/publish/errors.go @@ -0,0 +1,7 @@ +package publish + +import "errors" + +var ( + ErrCredentials = errors.New("errors in credentials") +) diff --git a/internal/publish/publisher.go b/internal/publish/publisher.go new file mode 100644 index 00000000..360d1c18 --- /dev/null +++ b/internal/publish/publisher.go @@ -0,0 +1,11 @@ +package publish + +import ( + "context" +) + +// Publisher defines an interface for device inventory to be published to remote endpoints. +type Publisher interface { + // Run spawns a device inventory publisher + Run(ctx context.Context) error +} diff --git a/internal/publish/serverservice.go b/internal/publish/serverservice.go new file mode 100644 index 00000000..818e0adc --- /dev/null +++ b/internal/publish/serverservice.go @@ -0,0 +1,468 @@ +package publish + +import ( + "context" + "net/url" + "os" + "reflect" + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/gammazero/workerpool" + "github.com/google/uuid" + "github.com/hashicorp/go-retryablehttp" + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + r3diff "github.com/r3labs/diff/v3" + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +const ( + KindServerService = "serverService" + namespace = "server.components" +) + +var ( + // concurrent requests + concurrency = 5 + + ErrSlugs = errors.New("slugs error") + ErrServerServiceQuery = errors.New("error in server service query") + ErrRegisterChanges = errors.New("error in server service register changes") + ErrAssetObjectConversion = errors.New("error converting asset object") + ErrChangeList = errors.New("error building change list") +) + +// serverServicePublisher publishes asset inventory to serverService +type serverServicePublisher struct { + logger *logrus.Entry + config *model.Config + syncWg *sync.WaitGroup + collectorCh <-chan *model.AssetDevice + termCh <-chan os.Signal + workers *workerpool.WorkerPool + client *serverservice.Client + slugs map[string]*serverservice.ServerComponentType +} + +// NewServerServicePublisher returns a serverService publisher to submit inventory data. +func NewServerServicePublisher(ctx context.Context, alloy *app.App) (Publisher, error) { + logger := app.NewLogrusEntryFromLogger(logrus.Fields{"component": "publisher.serverService"}, alloy.Logger) + + client, err := newServerServiceClient(alloy.Config, logger) + if err != nil { + return nil, err + } + + p := &serverServicePublisher{ + logger: logger, + config: alloy.Config, + syncWg: alloy.SyncWg, + collectorCh: alloy.CollectorCh, + termCh: alloy.TermCh, + workers: workerpool.New(concurrency), + client: client, + slugs: make(map[string]*serverservice.ServerComponentType), + } + + return p, nil +} + +// Run implements the Publisher interface to publish asset inventory +func (h *serverServicePublisher) Run(ctx context.Context) error { + // channel for routines spawned to indicate completion + doneCh := make(chan struct{}) + + // count of routines spawned to publish device inventory + var dispatched int32 + + // cache server component types for lookups + err := h.cacheServerComponentTypes(ctx) + if err != nil { + return err + } + + for device := range h.collectorCh { + if device == nil { + continue + } + + for h.workers.WaitingQueueSize() > 0 { + if ctx.Err() != nil { + break + } + + h.logger.WithFields(logrus.Fields{ + "component": "oob collector", + "queue size": h.workers.WaitingQueueSize(), + "concurrency": concurrency, + }).Debug("delay for queue size to drop..") + + // nolint:gomnd // delay is a magic number + time.Sleep(5 * time.Second) + } + + // increment wait group + h.syncWg.Add(1) + + // increment spawned count + atomic.AddInt32(&dispatched, 1) + + // submit inventory collection to worker pool + h.workers.Submit( + func() { + defer h.syncWg.Done() + defer func() { doneCh <- struct{}{} }() + + h.publish(ctx, device) + }, + ) + } + + // wait for dispatched routines to complete + for dispatched > 0 { + <-doneCh + atomic.AddInt32(&dispatched, ^int32(0)) + } + + return nil +} + +// publish device information with hollow server service +func (h *serverServicePublisher) publish(ctx context.Context, device *model.AssetDevice) { + if device == nil { + h.logger.Warn("nil device ignored") + + return + } + + id, err := uuid.Parse(device.ID) + if err != nil { + h.logger.WithField("err", err).Warn("invalid device ID") + + return + } + + // 1. retrieve server object, no action if the server doesn't exist + server, hr, err := h.client.Get(ctx, id) + if err != nil { + h.logger.WithFields( + logrus.Fields{ + "err": err, + "id": id, + "response": hr, + }).Warn("server service server query returned error") + + return + } + + if server == nil { + h.logger.WithFields( + logrus.Fields{ + "id": id, + "hr": hr, + }).Warn("server service server query returned nil object") + + return + } + + err = h.registerChanges(ctx, server.UUID, device) + if err != nil { + h.logger.WithFields( + logrus.Fields{ + "id": server.UUID.String(), + "err": err, + }).Warn("error converting device object") + } +} + +// registerChanges compares the current object in serverService with the device data and registers changes. +// +// nolint:gocyclo // the method caries out all steps to have device data compared and registered, for now its accepted as cyclomatic. +func (h *serverServicePublisher) registerChanges(ctx context.Context, serverID uuid.UUID, device *model.AssetDevice) error { + // convert model.AssetDevice to server service component slice + newInventory, err := h.toComponentSlice(serverID, device) + if err != nil { + return errors.Wrap(ErrAssetObjectConversion, err.Error()) + } + + // retrieve current inventory from server service + currentInventory, _, err := h.client.GetComponents(ctx, serverID, &serverservice.PaginationParams{}) + if err != nil { + return errors.Wrap(ErrServerServiceQuery, err.Error()) + } + + // identify changes to be applied + add, update, remove, err := serverServiceChangeList(componentPtrSlice(currentInventory), newInventory) + if err != nil { + return errors.Wrap(ErrRegisterChanges, err.Error()) + } + + if len(add) == 0 && len(update) == 0 && len(remove) == 0 { + h.logger.Debug("no changes identified to register.") + + return nil + } + + h.logger.WithFields( + logrus.Fields{ + "id": serverID, + "items-added": len(add), + "items-updated": len(update), + "items-removed": len(remove), + }).Info("device inventory changes to be registered") + + // apply added component changes + if len(add) > 0 { + _, err = h.client.CreateComponents(ctx, serverID, add) + if err != nil { + return errors.Wrap(ErrRegisterChanges, err.Error()) + } + } + + // apply updated component changes + if len(update) > 0 { + _, err = h.client.UpdateComponents(ctx, serverID, update) + if err != nil { + return errors.Wrap(ErrRegisterChanges, err.Error()) + } + } + + if len(remove) > 0 { + return errors.Wrap(ErrRegisterChanges, "component deletion not implemented") + } + + h.logger.WithFields( + logrus.Fields{ + "id": serverID, + "added": len(add), + "removed": len(remove), + }).Debug("registered inventory changes with server service") + + return nil +} + +// diffFilter is a filter passed to the r3 diff filter method for comparing structs +// +// nolint:gocritic // r3diff requires the field attribute to be passed by value +func diffFilter(path []string, parent reflect.Type, field reflect.StructField) bool { + switch field.Name { + case "CreatedAt", "UpdatedAt", "LastReportedAt": + return false + default: + return true + } +} + +// serverServiceChangeList compares the current vs newer slice of server components +// and returns 3 lists - add, update, remove. +func serverServiceChangeList(currentObjs, newObjs []*serverservice.ServerComponent) (add, update, remove serverservice.ServerComponentSlice, err error) { + // 1. list updated and removed objects + for _, currentObj := range currentObjs { + // changeObj is the component changes to be registered + changeObj := componentBySlugSerial(currentObj.ComponentTypeSlug, currentObj.Serial, newObjs) + + // component not found - add to remove list + if changeObj == nil { + remove = append(remove, *currentObj) + continue + } + + updated, err := serverServiceComponentsUpdated(currentObj, changeObj) + if err != nil { + return add, update, remove, err + } + + // no objects with changes identified + if updated == nil { + continue + } + + // updates identified, include object as an update + update = append(update, *updated) + } + + // 2. list new objects + for _, newObj := range newObjs { + changeObj := componentBySlugSerial(newObj.ComponentTypeSlug, newObj.Serial, currentObjs) + + if changeObj == nil { + add = append(add, *newObj) + } + } + + return add, update, remove, nil +} + +func serverServiceComponentsUpdated(currentObj, newObj *serverservice.ServerComponent) (*serverservice.ServerComponent, error) { + differ, err := r3diff.NewDiffer(r3diff.Filter(diffFilter)) + if err != nil { + return nil, err + } + + objChanges, err := differ.Diff(currentObj, newObj) + if err != nil { + return nil, err + } + + // no changes in object + if len(objChanges) == 0 { + return nil, nil + } + + // compare attributes, versioned attributes + attributes, versionedAttributes, err := diffComponentObjectsAttributes(currentObj, newObj) + if err != nil { + return nil, err + } + + // no changes in attributes, versioned attributes + if len(attributes) == 0 && len(versionedAttributes) == 0 { + return nil, err + } + + newObj.Attributes = nil + newObj.VersionedAttributes = nil + + if len(attributes) > 0 { + newObj.Attributes = attributes + } + + if len(versionedAttributes) > 0 { + newObj.VersionedAttributes = versionedAttributes + } + + newObj.UUID = currentObj.UUID + + return newObj, nil +} + +func diffComponentObjectsAttributes(currentObj, changeObj *serverservice.ServerComponent) ([]serverservice.Attributes, []serverservice.VersionedAttributes, error) { + var attributes []serverservice.Attributes + + var versionedAttributes []serverservice.VersionedAttributes + + differ, err := r3diff.NewDiffer(r3diff.Filter(diffFilter)) + if err != nil { + return attributes, versionedAttributes, err + } + + // compare attribute changes + attributeObjChanges, err := differ.Diff(currentObj.Attributes, changeObj.Attributes) + if err != nil { + return attributes, versionedAttributes, err + } + + if len(attributeObjChanges) > 0 { + attributes = changeObj.Attributes + } + + // compare versioned attributes + // + // the returned versioned attribute is to be included in the change object. + vAttributeObjChange, err := diffVersionedAttributes(currentObj.VersionedAttributes, changeObj.VersionedAttributes) + if err != nil { + return attributes, versionedAttributes, err + } + + if vAttributeObjChange != nil { + versionedAttributes = append(versionedAttributes, *vAttributeObjChange) + } + + return attributes, versionedAttributes, nil +} + +// diffVersionedAttributes compares the current latest (created_at) versioned attribute +// with the newer versioned attribute (from the inventory collection) +// returning the versioned attribute to be registered with serverService. +// +// In the case that no changes are to be registered, a nil object is returned. +func diffVersionedAttributes(currentObjs, newObjs []serverservice.VersionedAttributes) (*serverservice.VersionedAttributes, error) { + // no newObjects + if len(newObjs) == 0 { + return nil, nil + } + + // no versioned attributes in current + if len(newObjs) > 0 && len(currentObjs) == 0 { + return &newObjs[0], nil + } + + // identify current latest versioned attribute (sorted by created_at) + var currentObj serverservice.VersionedAttributes + + sort.Slice(currentObjs, func(i, j int) bool { + return currentObjs[i].CreatedAt.After( + currentObjs[j].CreatedAt, + ) + }) + + currentObj = currentObjs[0] + + // differ currentObj with newObj + differ, err := r3diff.NewDiffer(r3diff.Filter(diffFilter)) + if err != nil { + return nil, err + } + + changes, err := differ.Diff(currentObj, newObjs[0]) + if err != nil { + return nil, err + } + + if len(changes) > 0 { + return &newObjs[0], err + } + + return nil, nil +} + +func newServerServiceClient(cfg *model.Config, logger *logrus.Entry) (*serverservice.Client, error) { + // env var auth token + if authToken := os.Getenv("SERVERSERVICE_AUTH_TOKEN"); authToken != "" { + cfg.InventoryPublisher.ServerService.AuthToken = authToken + } + + if cfg.InventoryPublisher.ServerService.AuthToken == "" { + return nil, errors.Wrap(model.ErrConfig, "expected serverService auth token, got empty") + } + + // env var serverService endpoint + if endpoint := os.Getenv("SERVERSERVICE_ENDPOINT"); endpoint != "" { + cfg.InventoryPublisher.ServerService.Endpoint = endpoint + } + + if cfg.InventoryPublisher.ServerService.Endpoint == "" { + return nil, errors.Wrap(model.ErrConfig, "expected serverService endpoint, got empty") + } + + endpoint, err := url.Parse(cfg.InventoryPublisher.ServerService.Endpoint) + if err != nil { + return nil, errors.Wrap(model.ErrConfig, "error in serverService endpoint URL: "+err.Error()) + } + + if cfg.InventoryPublisher.ServerService.Concurrency == 0 { + cfg.InventoryPublisher.ServerService.Concurrency = concurrency + } + + // init retryable http client + retryableClient := retryablehttp.NewClient() + + // disable default debug logging on the retryable client + if logger.Level < logrus.DebugLevel { + retryableClient.Logger = nil + } else { + retryableClient.Logger = logger + } + + return serverservice.NewClientWithToken( + cfg.InventoryPublisher.ServerService.AuthToken, + endpoint.String(), + retryableClient.StandardClient(), + ) +} diff --git a/internal/publish/serverservice_components.go b/internal/publish/serverservice_components.go new file mode 100644 index 00000000..c3eeadb8 --- /dev/null +++ b/internal/publish/serverservice_components.go @@ -0,0 +1,759 @@ +package publish + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/bmc-toolbox/common" + "github.com/google/uuid" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +// componentBySlugSerial returns a pointer to a component that matches the given slug, serial attributes +func componentBySlugSerial(slug, serial string, components []*serverservice.ServerComponent) *serverservice.ServerComponent { + for _, c := range components { + if strings.EqualFold(slug, c.ComponentTypeSlug) && strings.EqualFold(serial, c.Serial) { + return c + } + } + + return nil +} + +func (h *serverServicePublisher) cacheServerComponentTypes(ctx context.Context) error { + serverComponentTypes, _, err := h.client.ListServerComponentTypes(ctx, nil) + if err != nil { + return err + } + + for _, ct := range serverComponentTypes { + h.slugs[ct.Slug] = ct + } + + return nil +} + +// componentPtrSlice returns a slice of pointers to serverservice.ServerComponent. +// +// The hollow client methods require component slice objects to be passed as values +// these tend to be large objects. +// +// This helper method is to reduce the amount of copying of component objects (~240 bytes each) when passed around between methods and range loops, +// while it seems like a minor optimization, it also keeps the linter happy. +func componentPtrSlice(components serverservice.ServerComponentSlice) []*serverservice.ServerComponent { + s := make([]*serverservice.ServerComponent, 0, len(components)) + + // nolint:gocritic // the copying has to be done somewhere + for _, c := range components { + c := c + s = append(s, &c) + } + + return s +} + +// toComponentSlice converts an model.AssetDevice object to the server service component slice object +func (h *serverServicePublisher) toComponentSlice(serverID uuid.UUID, device *model.AssetDevice) ([]*serverservice.ServerComponent, error) { + componentsTmp := []*serverservice.ServerComponent{} + componentsTmp = append(componentsTmp, + h.bios(device.BIOS), + h.bmc(device.BMC), + h.mainboard(device.Mainboard), + ) + + componentsTmp = append(componentsTmp, h.dimms(device.Memory)...) + componentsTmp = append(componentsTmp, h.nics(device.NICs)...) + componentsTmp = append(componentsTmp, h.drives(device.Drives)...) + componentsTmp = append(componentsTmp, h.psus(device.PSUs)...) + componentsTmp = append(componentsTmp, h.cpus(device.CPUs)...) + componentsTmp = append(componentsTmp, h.tpms(device.TPMs)...) + componentsTmp = append(componentsTmp, h.cplds(device.CPLDs)...) + componentsTmp = append(componentsTmp, h.gpus(device.GPUs)...) + componentsTmp = append(componentsTmp, h.storageControllers(device.StorageControllers)...) + + components := []*serverservice.ServerComponent{} + + for _, component := range componentsTmp { + if component == nil { + continue + } + + component.ServerUUID = serverID + components = append(components, component) + } + + return components, nil +} + +func (h *serverServicePublisher) newComponent(slug, cvendor, cmodel, cserial string) (*serverservice.ServerComponent, error) { + // lower case slug to changeObj how its stored in server service + slug = strings.ToLower(slug) + + // component slug lookup map is expected + if len(h.slugs) == 0 { + return nil, errors.Wrap(ErrSlugs, "component slugs lookup map empty") + } + + // component slug is part of the lookup map + _, exists := h.slugs[slug] + if !exists { + return nil, errors.Wrap(ErrSlugs, "unknown component slug: "+slug) + } + + return &serverservice.ServerComponent{ + Name: h.slugs[slug].Name, + Vendor: common.FormatVendorName(cvendor), + Model: cmodel, + Serial: cserial, + ComponentTypeID: h.slugs[slug].ID, + ComponentTypeName: h.slugs[slug].Name, + ComponentTypeSlug: slug, + }, nil +} + +func (h *serverServicePublisher) gpus(gpus []*common.GPU) []*serverservice.ServerComponent { + if gpus == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(gpus)) + + for idx, c := range gpus { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugGPU, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) cplds(cplds []*common.CPLD) []*serverservice.ServerComponent { + if cplds == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(cplds)) + + for idx, c := range cplds { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugCPLD, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) tpms(tpms []*common.TPM) []*serverservice.ServerComponent { + if tpms == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(tpms)) + + for idx, c := range tpms { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugTPM, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Metadata: c.Metadata, + InterfaceType: c.InterfaceType, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) cpus(cpus []*common.CPU) []*serverservice.ServerComponent { + if cpus == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(cpus)) + + for idx, c := range cpus { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugCPU, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + ID: c.ID, + Description: c.Description, + ProductName: c.ProductName, + Metadata: c.Metadata, + Slot: c.Slot, + Architecture: c.Architecture, + ClockSpeedHz: c.ClockSpeedHz, + Cores: c.Cores, + Threads: c.Threads, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) storageControllers(controllers []*common.StorageController) []*serverservice.ServerComponent { + if controllers == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(controllers)) + + for idx, c := range controllers { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugStorageController, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + ID: c.ID, + Description: c.Description, + ProductName: c.ProductName, + Oem: c.Oem, + SupportedControllerProtocols: c.SupportedControllerProtocols, + SupportedDeviceProtocols: c.SupportedDeviceProtocols, + SupportedRAIDTypes: c.SupportedRAIDTypes, + PhysicalID: c.PhysicalID, + BusInfo: c.BusInfo, + SpeedGbps: c.SpeedGbps, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) psus(psus []*common.PSU) []*serverservice.ServerComponent { + if psus == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(psus)) + + for idx, c := range psus { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugPSU, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + ID: c.ID, + Description: c.Description, + ProductName: c.ProductName, + PowerCapacityWatts: c.PowerCapacityWatts, + Oem: c.Oem, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) drives(drives []*common.Drive) []*serverservice.ServerComponent { + if drives == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(drives)) + + for idx, c := range drives { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugDrive, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Oem: c.Oem, + Metadata: c.Metadata, + BusInfo: c.BusInfo, + OemID: c.OemID, + StorageController: c.StorageController, + Protocol: c.Protocol, + SmartErrors: c.SmartErrors, + DriveType: c.Type, + WWN: c.WWN, + CapacityBytes: c.CapacityBytes, + BlockSizeBytes: c.BlockSizeBytes, + CapableSpeedGbps: c.CapableSpeedGbps, + NegotiatedSpeedGbps: c.NegotiatedSpeedGbps, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) nics(nics []*common.NIC) []*serverservice.ServerComponent { + if nics == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(nics)) + + for idx, c := range nics { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugNIC, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Oem: c.Oem, + Metadata: c.Metadata, + PhysicalID: c.PhysicalID, + BusInfo: c.BusInfo, + MacAddress: c.MacAddress, + SpeedBits: c.SpeedBits, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) dimms(dimms []*common.Memory) []*serverservice.ServerComponent { + if dimms == nil { + return nil + } + + components := make([]*serverservice.ServerComponent, 0, len(dimms)) + + for idx, c := range dimms { + if strings.TrimSpace(c.Serial) == "" { + c.Serial = strconv.Itoa(idx) + } + + sc, err := h.newComponent(common.SlugPhysicalMem, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Oem: c.Oem, + Slot: c.Slot, + ClockSpeedHz: c.ClockSpeedHz, + FormFactor: c.FormFactor, + PartNumber: c.PartNumber, + Metadata: c.Metadata, + SizeBytes: c.SizeBytes, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + components = append(components, sc) + } + + return components +} + +func (h *serverServicePublisher) mainboard(c *common.Mainboard) *serverservice.ServerComponent { + if c == nil { + return nil + } + + if strings.TrimSpace(c.Serial) == "" { + c.Serial = "0" + } + + sc, err := h.newComponent(common.SlugMainboard, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Oem: c.Oem, + PhysicalID: c.PhysicalID, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + return sc +} + +func (h *serverServicePublisher) bmc(c *common.BMC) *serverservice.ServerComponent { + if c == nil { + return nil + } + + if strings.TrimSpace(c.Serial) == "" { + c.Serial = "0" + } + + sc, err := h.newComponent(common.SlugBMC, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + Oem: c.Oem, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + return sc +} + +func (h *serverServicePublisher) bios(c *common.BIOS) *serverservice.ServerComponent { + if c == nil { + return nil + } + + if strings.TrimSpace(c.Serial) == "" { + c.Serial = "0" + } + + sc, err := h.newComponent(common.SlugBIOS, c.Vendor, c.Model, c.Serial) + if err != nil { + h.logger.Error(err) + + return nil + } + + h.setAttributes( + sc, + &attributes{ + Description: c.Description, + ProductName: c.ProductName, + SizeBytes: c.SizeBytes, + CapacityBytes: c.CapacityBytes, + Oem: c.Oem, + Metadata: c.Metadata, + }, + ) + + h.setVersionedAttributes( + sc, + &versionedAttributes{ + Firmware: c.Firmware, + Status: c.Status, + }, + ) + + return sc +} + +// attributes are generic component attributes +type attributes struct { + Metadata map[string]string `json:"metadata,omitempty"` + ID string `json:"id,omitempty"` + ChassisType string `json:"chassis_type,omitempty"` + Description string `json:"description,omitempty"` + ProductName string `json:"product_name,omitempty"` + InterfaceType string `json:"interface_type,omitempty"` + Slot string `json:"slot,omitempty"` + Architecture string `json:"architecture,omitempty"` + MacAddress string `json:"macaddress,omitempty"` + SupportedControllerProtocols string `json:"supported_controller_protocol,omitempty"` + SupportedDeviceProtocols string `json:"supported_device_protocol,omitempty"` + SupportedRAIDTypes string `json:"supported_raid_types,omitempty"` + PhysicalID string `json:"physid,omitempty"` + FormFactor string `json:"form_factor,omitempty"` + PartNumber string `json:"part_number,omitempty"` + OemID string `json:"oem_id,omitempty"` + DriveType string `json:"drive_type,omitempty"` + StorageController string `json:"storage_controller,omitempty"` + BusInfo string `json:"bus_info,omitempty"` + WWN string `json:"wwn,omitempty"` + Protocol string `json:"protocol,omitempty"` + SmartErrors []string `json:"smart_errors,omitempty"` + PowerCapacityWatts int64 `json:"power_capacity_watts,omitempty"` + SizeBytes int64 `json:"size_bytes,omitempty"` + CapacityBytes int64 `json:"capacity_bytes,omitempty" diff:"immutable"` + ClockSpeedHz int64 `json:"clock_speed_hz,omitempty"` + Cores int `json:"cores,omitempty"` + Threads int `json:"threads,omitempty"` + SpeedBits int64 `json:"speed_bits,omitempty"` + SpeedGbps int64 `json:"speed_gbps,omitempty"` + BlockSizeBytes int64 `json:"block_size_bytes,omitempty"` + CapableSpeedGbps int64 `json:"capable_speed_gbps,omitempty"` + NegotiatedSpeedGbps int64 `json:"negotiated_speed_gbps,omitempty"` + Oem bool `json:"oem,omitempty"` +} + +// versionedAttributes are component attributes to be versioned in server service +type versionedAttributes struct { + Firmware *common.Firmware `json:"firmware,omitempty"` + Status *common.Status `json:"status,omitempty"` + SmartStatus string `json:"smart_status,omitempty"` +} + +func (h *serverServicePublisher) setAttributes(component *serverservice.ServerComponent, attr *attributes) { + // convert attributes to raw json + data, err := json.Marshal(attr) + if err != nil { + h.logger.WithFields( + logrus.Fields{ + "slug": component.ComponentTypeSlug, + "kind": fmt.Sprintf("%T", data), + "err": err, + }).Warn("error in conversion of versioned attributes to raw data") + } + + // skip min sized json data containing just the braces `{}` + min := 2 + if len(data) == min { + return + } + + if component.Attributes == nil { + component.Attributes = []serverservice.Attributes{} + } + + component.Attributes = append( + component.Attributes, + serverservice.Attributes{ + Namespace: namespace, + Data: data, + }, + ) +} + +func (h *serverServicePublisher) setVersionedAttributes(component *serverservice.ServerComponent, vattr *versionedAttributes) { + // convert versioned attributes to raw json + data, err := json.Marshal(vattr) + if err != nil { + h.logger.WithFields( + logrus.Fields{ + "slug": component.ComponentTypeSlug, + "kind": fmt.Sprintf("%T", data), + "err": err, + }).Warn("error in conversion of versioned attributes to raw data") + } + + // skip empty json data containing just the braces `{}` + min := 2 + if len(data) == min { + return + } + + if component.VersionedAttributes == nil { + component.VersionedAttributes = []serverservice.VersionedAttributes{} + } + + component.VersionedAttributes = append( + component.VersionedAttributes, + serverservice.VersionedAttributes{ + Namespace: namespace, + Data: data, + }, + ) +} diff --git a/internal/publish/serverservice_components_test.go b/internal/publish/serverservice_components_test.go new file mode 100644 index 00000000..f1d07f18 --- /dev/null +++ b/internal/publish/serverservice_components_test.go @@ -0,0 +1,66 @@ +package publish + +import ( + "testing" + + "github.com/google/uuid" + "github.com/metal-toolbox/alloy/internal/fixtures" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +func Test_ToComponentSlice(t *testing.T) { + h := serverServicePublisher{ + logger: logrus.NewEntry(logrus.New()), + slugs: fixtures.ServerServiceSlugMap(), + } + + testcases := []struct { + name string + device *model.AssetDevice + expected []*serverservice.ServerComponent + }{ + { + "E3C246D4INL", + &model.AssetDevice{Device: fixtures.CopyDevice(fixtures.E3C246D4INL)}, + componentPtrSlice(fixtures.ServerServiceE3C246D4INLcomponents), + }, + { + "R6515_A", + &model.AssetDevice{Device: fixtures.CopyDevice(fixtures.R6515_f0c8e4ac)}, + componentPtrSlice(fixtures.ServerServiceR6515Components_f0c8e4ac), + }, + { + "R6515_B", + &model.AssetDevice{Device: fixtures.CopyDevice(fixtures.R6515_fc167440)}, + componentPtrSlice(fixtures.ServerServiceR6515Components_fc167440), + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + sc, err := h.toComponentSlice(uuid.Nil, tc.device) + if err != nil { + t.Fatal(err) + } + + // + // left commented out here for future reference + // + // filterFunc := func(f reflect.StructField, v reflect.Value) bool { + // switch f.Name { + // case "ServerUUID", "UUID", "CreatedAt", "UpdatedAt", "LastReportedAt": + // return false + // default: + // return true + // } + // } + // l := litter.Options{FieldFilter: filterFunc} + // l.Dump(sc) + + assert.Equal(t, tc.expected, sc) + }) + } +} diff --git a/internal/publish/serverservice_test.go b/internal/publish/serverservice_test.go new file mode 100644 index 00000000..401175c0 --- /dev/null +++ b/internal/publish/serverservice_test.go @@ -0,0 +1,523 @@ +package publish + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" + + "github.com/bmc-toolbox/common" + "github.com/google/uuid" + "github.com/hashicorp/go-retryablehttp" + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/fixtures" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + serverservice "go.hollow.sh/serverservice/pkg/api/v1" +) + +func assertComponentAttributes(t *testing.T, obj *serverservice.ServerComponent, expectedVersion string) { + t.Helper() + + assert.NotNil(t, obj) + assert.NotNil(t, obj.ServerUUID) + assert.NotNil(t, obj.UUID) + assert.NotNil(t, obj.ComponentTypeSlug) + assert.NotEmpty(t, obj.VersionedAttributes[0].Data) + assert.True(t, rawVersionAttributeFirmwareEquals(t, expectedVersion, obj.VersionedAttributes[0].Data)) +} + +// rawVersionAttributeKVEquals returns a bool value when the given key and value is equal +func rawVersionAttributeFirmwareEquals(t *testing.T, expectedVersion string, rawVA []byte) bool { + t.Helper() + + va := &versionedAttributes{} + + err := json.Unmarshal(rawVA, va) + if err != nil { + t.Fatal(err) + } + + return va.Firmware.Installed == expectedVersion +} + +func Test_ServerServiceChangeList(t *testing.T) { + components := fixtures.CopyServerServiceComponentSlice(fixtures.ServerServiceR6515Components_fc167440) + + // nolint:govet // struct alignment kept for readability + testcases := []struct { + name string // test name + current []*serverservice.ServerComponent + expectedUpdate int + expectedAdd int + expectedRemove int + slug string // the component slug + vaUpdates *versionedAttributes + aUpdates *attributes + addComponent bool // adds a new component into the new slice before comparison + removeComponent bool // removes a component from the new slice + }{ + { + "no changes in component lists", + componentPtrSlice(fixtures.CopyServerServiceComponentSlice(components)), + 0, + 0, + 0, + "", + nil, + nil, + false, + false, + }, + { + "updated component part of update slice", + componentPtrSlice(fixtures.CopyServerServiceComponentSlice(components)), + 1, + 0, + 0, + common.SlugBIOS, + &versionedAttributes{Firmware: &common.Firmware{Installed: "2.2.6"}}, + nil, + false, + false, + }, + { + "added component part of add slice", + componentPtrSlice(fixtures.CopyServerServiceComponentSlice(components)), + 0, + 1, + 0, + common.SlugNIC, + &versionedAttributes{Firmware: &common.Firmware{Installed: "1.3.3"}}, + nil, + true, + false, + }, + { + "component removed from slice", + componentPtrSlice(fixtures.CopyServerServiceComponentSlice(components)), + 0, + 0, + 1, + "", + nil, + nil, + false, + true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + newObjs := componentPtrSlice(fixtures.CopyServerServiceComponentSlice(fixtures.ServerServiceR6515Components_fc167440)) + + switch { + case tc.expectedAdd > 0: + newObjs = addcomponent(newObjs, t, tc.slug, tc.vaUpdates) + case tc.expectedUpdate > 0: + newObjs = updateComponentVA(newObjs, t, tc.slug, tc.vaUpdates) + case tc.expectedRemove > 0: + newObjs = newObjs[:len(newObjs)-1] + default: + } + + gotAdd, gotUpdate, gotRemove, err := serverServiceChangeList(tc.current, newObjs) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, tc.expectedAdd, len(gotAdd), "add list differs") + assert.Equal(t, tc.expectedUpdate, len(gotUpdate), "update list differs") + assert.Equal(t, tc.expectedRemove, len(gotRemove), "remove list differs") + }) + } +} + +func Test_DiffVersionedAttributes(t *testing.T) { + now := time.Now() + + // current versioned attributes fixture for data read from serverService + fixtureCurrentVA := []serverservice.VersionedAttributes{ + { + Namespace: "server.components", + Data: []byte(`{"firmware":{"installed":"2.2.5","software_id":"159"}`), + CreatedAt: now.Add(-24 * time.Hour), // 24 hours earlier + }, + { + Namespace: "server.components", + Data: []byte(`{"firmware":{"installed":"2.2.4","software_id":"159"}`), + CreatedAt: now.Add(-48 * time.Hour), // 48 hours earlier + }, + } + + // new versioned attributes fixture for data read from the BMC + fixtureNewVA := []serverservice.VersionedAttributes{ + { + Namespace: "server.components", + Data: []byte(`{"firmware":{"installed":"2.2.6","software_id":"159"}`), + CreatedAt: now, + }, + } + + // current versioned attribute fixture which includes data from newer, unsorted + fixtureCurrentWithNewerVA := []serverservice.VersionedAttributes{ + fixtureCurrentVA[0], + fixtureCurrentVA[1], + fixtureNewVA[0], + } + + testcases := []struct { + name string + expectedErr error + expectedObj *serverservice.VersionedAttributes + currentObjs []serverservice.VersionedAttributes + newObjs []serverservice.VersionedAttributes + }{ + { + "with no new versioned objects, the method returns nil", + nil, + nil, + fixtureCurrentVA, + []serverservice.VersionedAttributes{}, + }, + { + "with no new versioned objects, and no current versioned objects the method returns nil", + nil, + nil, + []serverservice.VersionedAttributes{}, + []serverservice.VersionedAttributes{}, + }, + { + "with an empty current versioned attribute object, the method returns the newer object", + nil, + &fixtureNewVA[0], + []serverservice.VersionedAttributes{}, + fixtureNewVA, + }, + { + "latest current versioned attribute is compared with the newer, newer is returend", + nil, + &fixtureNewVA[0], + fixtureCurrentVA, + fixtureNewVA, + }, + { + "latest current versioned attribute is equal to newer, nil is returned", + nil, + nil, + fixtureCurrentWithNewerVA, + fixtureNewVA, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + v, err := diffVersionedAttributes(tc.currentObjs, tc.newObjs) + if tc.expectedErr != nil { + assert.NotNil(t, err) + assert.Equal(t, tc.expectedErr, err) + return + } + + assert.Equal(t, tc.expectedObj, v) + }) + } +} + +// addVA +func addcomponent(sc []*serverservice.ServerComponent, t *testing.T, slug string, va *versionedAttributes) []*serverservice.ServerComponent { + t.Helper() + + data, err := json.Marshal(va) + if err != nil { + t.Error(err) + } + + component := &serverservice.ServerComponent{ + UUID: uuid.New(), + Name: slug, + Vendor: "", + VersionedAttributes: []serverservice.VersionedAttributes{ + { + Data: data, + Namespace: "foo.bar", + }, + }, + } + + sc = append(sc, component) + + return sc +} + +func updateComponentVA(sc []*serverservice.ServerComponent, t *testing.T, slug string, va *versionedAttributes) []*serverservice.ServerComponent { + t.Helper() + + var component *serverservice.ServerComponent + + for _, c := range sc { + if strings.EqualFold(c.ComponentTypeSlug, strings.ToLower(slug)) { + component = c + break + } + } + + if component == nil { + t.Fatal("component with slug not found:" + slug) + } + + newVA := newVersionAttributes(t, component.VersionedAttributes[0].Data, va.Firmware.Installed) + + newVAData, err := json.Marshal(newVA) + if err != nil { + t.Fatal(err) + } + + component.VersionedAttributes[0].Data = newVAData + + return sc +} + +func newVersionAttributes(t *testing.T, data json.RawMessage, value string) *versionedAttributes { + t.Helper() + + va := &versionedAttributes{} + + err := json.Unmarshal(data, va) + if err != nil { + t.Fatal(err) + } + + va.Firmware.Installed = value + + return va +} + +func Test_ServerService_RegisterChanges_ObjectsEqual(t *testing.T) { + serverID, _ := uuid.Parse(fixtures.TestserverID_Dell_fc167440) + handler := http.NewServeMux() + // get components query + handler.HandleFunc( + fmt.Sprintf("/api/v1/servers/%s/components", serverID.String()), + func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + resp, err := os.ReadFile("../fixtures/serverservice_components_fc167440.json") + if err != nil { + t.Fatal(err) + } + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(resp) + default: + t.Fatal("expected GET request, got: " + r.Method) + } + }, + ) + + mock := httptest.NewServer(handler) + cr := retryablehttp.NewClient() + // nil logger to prevent debug logs + cr.Logger = nil + + c, err := serverservice.NewClientWithToken( + "hunter2", + mock.URL, + cr.StandardClient(), + ) + + if err != nil { + t.Fatal(err) + } + + serverService := serverServicePublisher{ + logger: app.NewLogrusEntryFromLogger(logrus.Fields{"component": "publisher"}, logrus.New()), + slugs: fixtures.ServerServiceSlugMap(), + client: c, + } + + device := &model.AssetDevice{ID: serverID.String(), Device: fixtures.CopyDevice(fixtures.R6515_fc167440)} + + err = serverService.registerChanges(context.TODO(), serverID, device) + if err != nil { + t.Fatal(err) + } +} + +func Test_ServerService_RegisterChanges_ObjectsUpdated(t *testing.T) { + serverID, _ := uuid.Parse(fixtures.TestserverID_Dell_fc167440) + newBIOSFWVersion := "2.6.7" + + newBMCFWVersion := "5.12.00.00" + + handler := http.NewServeMux() + + // get components query + handler.HandleFunc( + fmt.Sprintf("/api/v1/servers/%s/components", serverID.String()), + func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + resp, err := os.ReadFile("../fixtures/serverservice_components_fc167440.json") + if err != nil { + t.Fatal(err) + } + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(resp) + case http.MethodPut: + b, err := io.ReadAll(r.Body) + if err != nil { + t.Fatal(err) + } + + gotUpdate := []*serverservice.ServerComponent{} + if err := json.Unmarshal(b, &gotUpdate); err != nil { + t.Fatal(err) + } + + assert.Equal(t, 2, len(gotUpdate)) + + gotObj := componentBySlugSerial(common.SlugBIOS, "0", gotUpdate) + assertComponentAttributes(t, gotObj, newBIOSFWVersion) + + gotObj = componentBySlugSerial(common.SlugBMC, "0", gotUpdate) + assertComponentAttributes(t, gotObj, newBMCFWVersion) + + _, _ = w.Write([]byte(`{}`)) + default: + t.Fatal("expected GET request, got: " + r.Method) + } + }, + ) + + mock := httptest.NewServer(handler) + cr := retryablehttp.NewClient() + // nil logger to prevent debug logs + cr.Logger = nil + + c, err := serverservice.NewClientWithToken( + "hunter2", + mock.URL, + cr.StandardClient(), + ) + + if err != nil { + t.Fatal(err) + } + + serverService := serverServicePublisher{ + logger: app.NewLogrusEntryFromLogger(logrus.Fields{"component": "publisher"}, logrus.New()), + slugs: fixtures.ServerServiceSlugMap(), + client: c, + } + + // asset device fixture returned by the inventory collector + device := &model.AssetDevice{ + ID: serverID.String(), + Device: fixtures.CopyDevice(fixtures.R6515_fc167440), + } + + // bump version on BIOS and BMC components + device.BIOS.Firmware.Installed = newBIOSFWVersion + device.BMC.Firmware.Installed = newBMCFWVersion + + err = serverService.registerChanges(context.TODO(), serverID, device) + if err != nil { + t.Fatal(err) + } +} + +func Test_ServerService_RegisterChanges_ObjectsAdded(t *testing.T) { + serverID, _ := uuid.Parse(fixtures.TestserverID_Dell_fc167440) + + fixtureNICSerial := "c00l" + + handler := http.NewServeMux() + // get components query + handler.HandleFunc( + fmt.Sprintf("/api/v1/servers/%s/components", serverID.String()), + func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + resp, err := os.ReadFile("../fixtures/serverservice_components_fc167440.json") + if err != nil { + t.Fatal(err) + } + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(resp) + case http.MethodPost: + b, err := io.ReadAll(r.Body) + if err != nil { + t.Fatal(err) + } + + gotAdded := []*serverservice.ServerComponent{} + if err := json.Unmarshal(b, &gotAdded); err != nil { + t.Fatal(err) + } + + assert.Equal(t, 1, len(gotAdded)) + + gotObj := componentBySlugSerial(common.SlugNIC, fixtureNICSerial, gotAdded) + assert.NotNil(t, gotObj) + + _, _ = w.Write([]byte(`{}`)) + default: + t.Fatal("expected GET request, got: " + r.Method) + } + }, + ) + + mock := httptest.NewServer(handler) + cr := retryablehttp.NewClient() + cr.Logger = nil + + c, err := serverservice.NewClientWithToken( + "hunter2", + mock.URL, + cr.StandardClient(), + ) + + if err != nil { + t.Fatal(err) + } + + serverService := serverServicePublisher{ + logger: app.NewLogrusEntryFromLogger(logrus.Fields{"component": "publisher"}, logrus.New()), + slugs: fixtures.ServerServiceSlugMap(), + client: c, + } + + // asset device fixture returned by the inventory collector + device := &model.AssetDevice{ + ID: serverID.String(), + Device: fixtures.CopyDevice(fixtures.R6515_fc167440), + } + + device.NICs = append( + device.NICs, + &common.NIC{ + ID: "NEW NIC!", + Description: "Just added!, totally incompatible", + Common: common.Common{ + Vendor: "noname", + Model: "noname", + Serial: fixtureNICSerial, + }, + }, + ) + + err = serverService.registerChanges(context.TODO(), serverID, device) + if err != nil { + t.Fatal(err) + } +} diff --git a/internal/publish/stdout.go b/internal/publish/stdout.go new file mode 100644 index 00000000..61ed6fb3 --- /dev/null +++ b/internal/publish/stdout.go @@ -0,0 +1,55 @@ +package publish + +import ( + "context" + "encoding/json" + "log" + "os" + "sync" + + "github.com/metal-toolbox/alloy/internal/app" + "github.com/metal-toolbox/alloy/internal/model" + "github.com/sirupsen/logrus" +) + +const ( + KindStdout = "stdout" +) + +// stdoutPublisher publishes asset inventory to stdout +type stdoutPublisher struct { + logger *logrus.Entry + syncWg *sync.WaitGroup + collectorCh <-chan *model.AssetDevice + termCh <-chan os.Signal +} + +func NewStdoutPublisher(ctx context.Context, alloy *app.App) (Publisher, error) { + logger := app.NewLogrusEntryFromLogger(logrus.Fields{"component": "publisher.stdout"}, alloy.Logger) + + p := &stdoutPublisher{ + logger: logger, + syncWg: alloy.SyncWg, + collectorCh: alloy.CollectorCh, + termCh: alloy.TermCh, + } + + return p, nil +} + +func (p *stdoutPublisher) Run(ctx context.Context) error { + for device := range p.collectorCh { + if device == nil { + continue + } + + out, err := json.MarshalIndent(device, "", " ") + if err != nil { + return err + } + + log.Println(string(out)) + } + + return nil +} diff --git a/main.go b/main.go index ffee7e43..e659b000 100644 --- a/main.go +++ b/main.go @@ -1,5 +1,7 @@ package main +import "github.com/metal-toolbox/alloy/cmd" + func main() { - run() + cmd.Run() }