From b6af4b2b37cc6867d154aa19716e1667146b98cc Mon Sep 17 00:00:00 2001 From: Maxime VISONNEAU Date: Tue, 6 Nov 2018 20:57:18 +0000 Subject: [PATCH] Added support for nitro instances with NVMe block devices (#38) --- Gopkg.lock | 9 + Gopkg.toml | 4 + attach.go | 4 +- ebs.go | 13 +- filesystem/drive.go | 37 +++- .../mvisonneau/go-ebsnvme/.gitignore | 2 + .../mvisonneau/go-ebsnvme/.travis.yml | 11 + .../mvisonneau/go-ebsnvme/CHANGELOG.md | 19 ++ .../mvisonneau/go-ebsnvme/Gopkg.lock | 17 ++ .../mvisonneau/go-ebsnvme/Gopkg.toml | 3 + .../github.com/mvisonneau/go-ebsnvme/LICENSE | 191 ++++++++++++++++++ .../github.com/mvisonneau/go-ebsnvme/Makefile | 75 +++++++ .../mvisonneau/go-ebsnvme/README.md | 61 ++++++ .../github.com/mvisonneau/go-ebsnvme/cli.go | 64 ++++++ .../mvisonneau/go-ebsnvme/cli_test.go | 12 ++ .../github.com/mvisonneau/go-ebsnvme/main.go | 9 + .../mvisonneau/go-ebsnvme/main_test.go | 10 + .../go-ebsnvme/pkg/ebsnvme/ebsnvme.go | 155 ++++++++++++++ 18 files changed, 690 insertions(+), 6 deletions(-) create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/.gitignore create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/.travis.yml create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/CHANGELOG.md create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.lock create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.toml create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/LICENSE create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/Makefile create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/README.md create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/cli.go create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/cli_test.go create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/main.go create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/main_test.go create mode 100644 vendor/github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme/ebsnvme.go diff --git a/Gopkg.lock b/Gopkg.lock index 44c58287..bc16e33d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -62,6 +62,14 @@ revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" version = "v1.0.1" +[[projects]] + digest = "1:ddaa9cb9044da5acde71439da578a11393b443248cf7ca8a88dbb8aef80668c9" + name = "github.com/mvisonneau/go-ebsnvme" + packages = ["pkg/ebsnvme"] + pruneopts = "" + revision = "712c1897902b6de1e94be54d0261430064247fe0" + version = "0.1.0" + [[projects]] branch = "master" digest = "1:c0959eb8fc519d62edc717e2997032859688c0e1ddd9d4463512ead83576d247" @@ -99,6 +107,7 @@ "github.com/aws/aws-sdk-go/aws/ec2metadata", "github.com/aws/aws-sdk-go/aws/session", "github.com/aws/aws-sdk-go/service/ec2", + "github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme", "github.com/sirupsen/logrus", ] solver-name = "gps-cdcl" diff --git a/Gopkg.toml b/Gopkg.toml index 2a92d748..c9f4213d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -5,3 +5,7 @@ [[constraint]] branch = "master" name = "github.com/sirupsen/logrus" + +[[constraint]] + name = "github.com/mvisonneau/go-ebsnvme" + version = "0.1.0" diff --git a/attach.go b/attach.go index 695571b8..828283b9 100644 --- a/attach.go +++ b/attach.go @@ -35,14 +35,14 @@ func (e *EC2Instance) AttachEbsVolumes() { volLogger.Fatalf("Couldn't attach: %v", err) } volLogger.Info(volAttachments) - volume.AttachedName = deviceName if !filesystem.DoesDriveExistWithTimeout(deviceName, 10) { volLogger.Fatalf("Drive %s doesn't exist after attaching - checked with stat 10 times", deviceName) } + + volume.AttachedName = deviceName localVolumes[key] = append(localVolumes[key], volume) } - } } e.Vols = localVolumes diff --git a/ebs.go b/ebs.go index 2660420e..3a678bce 100644 --- a/ebs.go +++ b/ebs.go @@ -55,7 +55,11 @@ func prepAndMountDrives(volName string, vols []EbsVol) { var err error if len(vols) == 1 { driveLogger.Info("Single drive, no RAID") - driveName = vols[0].AttachedName + driveName, err = filesystem.GetActualBlockDeviceName(vols[0].AttachedName) + if err != nil { + driveLogger.Fatalf("Block device is not available %s : %v", vols[0].AttachedName, err) + } + driveLogger.Infof("Using %s as local block device", driveName) } else { if raidLevel == -1 { driveLogger.Info("Raid level not provided, not performing further actions") @@ -64,7 +68,12 @@ func prepAndMountDrives(volName string, vols []EbsVol) { driveLogger.Info("Creating RAID array") driveNames := []string{} for _, vol := range vols { - driveNames = append(driveNames, vol.AttachedName) + driveName, err = filesystem.GetActualBlockDeviceName(vol.AttachedName) + if err != nil { + driveLogger.Fatalf("Block device is not available %s : %v", vol.AttachedName, err) + } + driveLogger.Infof("Using %s as local block device", driveName) + driveNames = append(driveNames, driveName) } if driveName, err = filesystem.CreateRaidArray(driveNames, volName, raidLevel); err != nil { driveLogger.Fatalf("Error when creating reaid array: %v", err) diff --git a/filesystem/drive.go b/filesystem/drive.go index 2eb448f8..5321d465 100644 --- a/filesystem/drive.go +++ b/filesystem/drive.go @@ -1,9 +1,12 @@ package filesystem import ( + "os" + "path/filepath" + "regexp" "time" - "github.com/sevagh/goat/execute" + "github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme" ) //DoesDriveExistWithTimeout makes 10 attempts, 2 second sleep between each, to stat a drive to check for its existence @@ -21,8 +24,38 @@ func DoesDriveExistWithTimeout(driveName string, maxAttempts int) bool { //DoesDriveExist does a single stat call to check if a drive exists func DoesDriveExist(driveName string) bool { - if _, err := execute.Command("stat", []string{driveName}, ""); err != nil { + if _, err := os.Stat(driveName); os.IsNotExist(err) { + for _, file := range listNvmeBlockDevices() { + if d, _ := ebsnvme.ScanDevice(file); d.Name == driveName { + return true + } + } return false } return true } + +// GetLocalBlockDeviceName returns the actual name of the block device seen +// within the instance (useful for nitros) +func GetActualBlockDeviceName(name string) (string, error) { + for _, device := range listNvmeBlockDevices() { + if d, _ := ebsnvme.ScanDevice(device); d.Name == name { + return device, nil + } + } + if _, err := os.Stat(name); os.IsNotExist(err) { + return "", err + } + return name, nil +} + +func listNvmeBlockDevices() (devices []string) { + re := regexp.MustCompile("(^\\/dev\\/nvme[0-9]+n1$)") + f, _ := filepath.Glob("/dev/nvme*") + for _, d := range f { + if re.Match([]byte(d)) { + devices = append(devices, d) + } + } + return +} diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/.gitignore b/vendor/github.com/mvisonneau/go-ebsnvme/.gitignore new file mode 100644 index 00000000..8940ecb4 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/.gitignore @@ -0,0 +1,2 @@ +dist +vendor diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/.travis.yml b/vendor/github.com/mvisonneau/go-ebsnvme/.travis.yml new file mode 100644 index 00000000..486d3eed --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/.travis.yml @@ -0,0 +1,11 @@ +--- +language: go +go: + - '1.11' +go_import_path: github.com/mvisonneau/go-ebsnvme +install: + - make deps + - go get -u github.com/mattn/goveralls +script: + - make all + - goveralls -service=travis-ci -coverprofile=coverage.out diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/CHANGELOG.md b/vendor/github.com/mvisonneau/go-ebsnvme/CHANGELOG.md new file mode 100644 index 00000000..35c07ba6 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] - 2018-11-06 +### FEATURES +- Working state of the app and lib +- got some tests in place +- Makefile +- LICENSE +- README + +[Unreleased]: https://github.com/mvisonneau/go-ebsnvme/compare/0.1.0...HEAD +[0.1.0]: https://github.com/mvisonneau/go-ebsnvme/tree/0.1.0 diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.lock b/vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.lock new file mode 100644 index 00000000..5eefd969 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.lock @@ -0,0 +1,17 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:b24d38b282bacf9791408a080f606370efa3d364e4b5fd9ba0f7b87786d3b679" + name = "github.com/urfave/cli" + packages = ["."] + pruneopts = "UT" + revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" + version = "v1.20.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = ["github.com/urfave/cli"] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.toml b/vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.toml new file mode 100644 index 00000000..5c879c7d --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/Gopkg.toml @@ -0,0 +1,3 @@ +[prune] + go-tests = true + unused-packages = true diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/LICENSE b/vendor/github.com/mvisonneau/go-ebsnvme/LICENSE new file mode 100644 index 00000000..790b7e55 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2018 - Maxime VISONNEAU and Amazon Web Services Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/Makefile b/vendor/github.com/mvisonneau/go-ebsnvme/Makefile new file mode 100644 index 00000000..1cbaba93 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/Makefile @@ -0,0 +1,75 @@ +NAME := go-ebsnvme +VERSION := $(shell git describe --tags --abbrev=1) +FILES := $(shell git ls-files '*.go') +LDFLAGS := -w -extldflags "-static" -X 'main.version=$(VERSION)' +REGISTRY := mvisonneau/$(NAME) +.DEFAULT_GOAL := help + +.PHONY: fmt +fmt: ## Format source code + @command -v goimports 2>&1 >/dev/null || go get -u golang.org/x/tools/cmd/goimports + goimports -w $(FILES) + +.PHONY: lint +lint: ## Run golint and go vet against the codebase + @command -v golint 2>&1 >/dev/null || go get -u github.com/golang/lint/golint + golint -set_exit_status . + go vet ./... + +.PHONY: test +test: ## Run the tests against the codebase + go test -v ./... + +.PHONY: install +install: ## Build and install locally the binary (dev purpose) + go install . + +.PHONY: build +build: ## Build the binary + @command -v gox 2>&1 >/dev/null || go get -u github.com/mitchellh/gox + mkdir -p dist; rm -rf dist/* + CGO_ENABLED=0 gox -osarch "linux/386 linux/amd64" -ldflags "$(LDFLAGS)" -output dist/$(NAME)_{{.OS}}_{{.Arch}} + strip dist/*_linux_* + +.PHONY: build-docker +build-docker: + CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" . + strip $(NAME) + +.PHONY: publish-github +publish-github: ## Send the binaries onto the GitHub release + @command -v ghr 2>&1 >/dev/null || go get -u github.com/tcnksm/ghr + ghr -u mvisonneau -replace $(VERSION) dist + +.PHONY: deps +deps: ## Fetch all dependencies + @command -v dep 2>&1 >/dev/null || go get -u github.com/golang/dep/cmd/dep + @dep ensure -v + +.PHONY: imports +imports: ## Fixes the syntax (linting) of the codebase + goimports -d $(FILES) + +.PHONY: clean +clean: ## Remove binary if it exists + rm -f $(NAME) + +.PHONY: coverage +coverage: ## Generates coverage report + rm -rf *.out + go test -coverprofile=coverage.out + +.PHONY: dev-env +dev-env: ## Build a local development environment using Docker + @docker run -it --rm \ + -v $(shell pwd):/go/src/github.com/mvisonneau/$(NAME) \ + -w /go/src/github.com/mvisonneau/$(NAME) \ + golang:1.11 \ + /bin/bash -c 'make deps; make install; bash' + +.PHONY: all +all: lint imports test coverage build ## Test, builds and ship package for all supported platforms + +.PHONY: help +help: ## Displays this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/README.md b/vendor/github.com/mvisonneau/go-ebsnvme/README.md new file mode 100644 index 00000000..99300eee --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/README.md @@ -0,0 +1,61 @@ +# mvisonneau/go-ebsnvme + +[![GoDoc](https://godoc.org/github.com/mvisonneau/go-ebsnvme?status.svg)](https://godoc.org/github.com/mvisonneau/go-ebsnvme/app) +[![Go Report Card](https://goreportcard.com/badge/github.com/mvisonneau/go-ebsnvme)](https://goreportcard.com/report/github.com/mvisonneau/go-ebsnvme) +[![Build Status](https://travis-ci.org/mvisonneau/go-ebsnvme.svg?branch=master)](https://travis-ci.org/mvisonneau/go-ebsnvme) +[![Coverage Status](https://coveralls.io/repos/github/mvisonneau/go-ebsnvme/badge.svg?branch=master)](https://coveralls.io/github/mvisonneau/go-ebsnvme?branch=master) + +`go-ebsnvme` is a [golang](https://golang.org/) version of the [AWS ebsnvme-id python script](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html) + +## TL:DR + +```bash +~$ go-ebsvnme /dev/nvme0n1 +vol-99cff4881d00c56a8 +/dev/sda1 + +~$ go-ebsvnme --volume-id /dev/nvme1n1 +vol-80dfffbbee880a72c + +~$ go-ebsvnme --device-name /dev/nvme1n1 +/dev/xvdf +``` + +## Usage + +``` +~$ go-ebsnvme -h +NAME: + go-ebsnvme - Fetch information about AWS EBS NVMe volumes + +USAGE: + go-ebsnvme [--volume-id|--device-name] + +VERSION: + + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --volume-id, -i only print the EBS volume-id + --device-name, -n only print the name of the block device + --help, -h show help + --version, -v print the version +``` + + +## Develop / Test + +If you use docker, you can easily get started using : + +```bash +~$ make dev-env +# You should then be able to use go commands to work onto the project, eg: +~docker$ make install +~docker$ go-ebsnvme +``` + +## Contribute + +Contributions are more than welcome! Feel free to submit a [PR](https://github.com/mvisonneau/go-ebsnvme/pulls). diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/cli.go b/vendor/github.com/mvisonneau/go-ebsnvme/cli.go new file mode 100644 index 00000000..c6d83591 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/cli.go @@ -0,0 +1,64 @@ +package main + +import ( + "fmt" + "github.com/urfave/cli" + + "github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme" +) + +var version = "" + +const ( + usage = "go-ebsnvme [--volume-id|--device-name]" +) + +// runCli : Generates cli configuration for the application +func runCli() (c *cli.App) { + c = cli.NewApp() + c.Name = "go-ebsnvme" + c.Version = version + c.Usage = "Fetch information about AWS EBS NVMe volumes" + c.UsageText = usage + c.EnableBashCompletion = true + + c.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "volume-id, i", + Usage: "only print the EBS volume-id", + }, + cli.BoolFlag{ + Name: "device-name, n", + Usage: "only print the name of the block device", + }, + } + + c.Action = func(ctx *cli.Context) error { + if len(ctx.Args()) != 1 || + (ctx.Bool("volume-id") && ctx.Bool("device-name")) { + return cli.NewExitError("Usage: "+usage, 1) + } + + d, err := ebsnvme.ScanDevice(ctx.Args().First()) + if err != nil { + fmt.Printf("error: %v\n", err) + return cli.NewExitError("", 1) + } + + if ctx.Bool("volume-id") { + fmt.Println(d.VolumeID) + return nil + } + + if ctx.Bool("device-name") { + fmt.Println(d.Name) + return nil + } + + fmt.Println(d.VolumeID) + fmt.Println(d.Name) + return nil + } + + return +} diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/cli_test.go b/vendor/github.com/mvisonneau/go-ebsnvme/cli_test.go new file mode 100644 index 00000000..dc80efc3 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/cli_test.go @@ -0,0 +1,12 @@ +package main + +import ( + "testing" +) + +func TestRunCli(t *testing.T) { + c := runCli() + if c.Name != "go-ebsnvme" { + t.Fatalf("Expected c.Name to be go-ebsnvme, got '%v'", c.Name) + } +} diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/main.go b/vendor/github.com/mvisonneau/go-ebsnvme/main.go new file mode 100644 index 00000000..b7063557 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "os" +) + +func main() { + runCli().Run(os.Args) +} diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/main_test.go b/vendor/github.com/mvisonneau/go-ebsnvme/main_test.go new file mode 100644 index 00000000..3b7d16cc --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/main_test.go @@ -0,0 +1,10 @@ +package main + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} diff --git a/vendor/github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme/ebsnvme.go b/vendor/github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme/ebsnvme.go new file mode 100644 index 00000000..1b9a50e4 --- /dev/null +++ b/vendor/github.com/mvisonneau/go-ebsnvme/pkg/ebsnvme/ebsnvme.go @@ -0,0 +1,155 @@ +package ebsnvme + +import ( + "fmt" + "strings" + "syscall" + "unsafe" +) + +const ( + nvmeAdminIdentify = 0x06 + nvmeIoctlAdminCmd uintptr = 0xC0484E41 + awsNvmeVolumeID = 0x1D0F + awsNvmeEbsMn = "Amazon Elastic Block Store" +) + +type Device struct { + VolumeID string + Name string +} + +type nvmeIdentifyController struct { + vid uint16 + ssvid uint16 + sn [20]byte + mn [40]byte + fr [8]byte + rab uint8 + ieee []byte + mic uint8 + mdts uint8 + reserved0 [(256 - 78)]uint8 + oacs uint16 + acl uint8 + aerl uint8 + frmw uint8 + lpa uint8 + elpe uint8 + npss uint8 + avscc uint8 + reserved1 [(512 - 265)]uint8 + sqes uint8 + cqes uint8 + reserved2 uint16 + nn uint32 + oncs uint16 + fuses uint16 + fna uint8 + vwc uint8 + awun uint16 + awupf uint16 + nvscc uint8 + reserved3 [(704 - 531)]uint8 + reserved4 [(2048 - 704)]uint8 + psd [996]byte + vs struct { + bdev [32]byte + reserved0 [(1024 - 32)]byte + } +} + +type nvmeAdminCommand struct { + opcode uint8 + flags uint8 + cid uint16 + nsid uint32 + reserved0 uint64 + mptr uint64 + addr uintptr + mlen uint32 + alen uint32 + cdw10 uint32 + cdw11 uint32 + cdw12 uint32 + cdw13 uint32 + cdw14 uint32 + cdw15 uint32 + reserved1 uint64 +} + +func ScanDevice(device string) (d Device, e error) { + f, err := open(device) + if err != nil { + e = err + return + } + + idCtrl := nvmeIdentifyController{} + adminCmd := nvmeAdminCommand{ + opcode: nvmeAdminIdentify, + addr: uintptr(unsafe.Pointer(&idCtrl)), + alen: uint32(unsafe.Sizeof(idCtrl)), + cdw10: 1, + } + + err = ioctl(f, nvmeIoctlAdminCmd, uintptr(unsafe.Pointer(&adminCmd))) + if err != nil { + e = err + return + } + + if idCtrl.getVendorId() != awsNvmeVolumeID { + e = fmt.Errorf("Volume ID not matching an AWS EBS one") + return + } + + if idCtrl.getModuleNumber() != awsNvmeEbsMn { + e = fmt.Errorf("Module number not matching an AWS EBS one") + return + } + + d.VolumeID = idCtrl.getVolumeID() + d.Name = idCtrl.getDeviceName() + return +} + +func open(device string) (uintptr, error) { + f, err := syscall.Open(device, syscall.O_RDWR, 0660) + if err != nil { + return 0, err + } + return uintptr(f), nil +} + +func ioctl(fd, cmd, ptr uintptr) error { + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr) + if errno != syscall.Errno(0x0) { + return errno + } + return nil +} + +func (i *nvmeIdentifyController) getVolumeID() string { + s := strings.TrimSpace(string(i.sn[:])) + if s[:3] != "vol-" { + return "vol-" + s[3:] + } + return s +} + +func (i *nvmeIdentifyController) getDeviceName() string { + s := strings.TrimSpace(string(i.vs.bdev[:])) + if len(s) < 5 || s[:5] != "/dev/" { + return "/dev/" + s + } + return s +} + +func (i *nvmeIdentifyController) getVendorId() uint16 { + return i.vid +} + +func (i *nvmeIdentifyController) getModuleNumber() string { + return strings.TrimSpace(string(i.mn[:])) +}