Repository: ekalinin/awsping
Branch: master
Commit: 31ac8c5f70aa
Files: 19
Total size: 34.2 KB
Directory structure:
gitextract_d_wjxg93/
├── .dockerignore
├── .github/
│ └── workflows/
│ ├── goreleaser.yml
│ └── pr.yml
├── .gitignore
├── .goreleaser.yml
├── Dockerfile
├── Dockerfile.goreleaser
├── LICENSE
├── Makefile
├── README.md
├── aws.go
├── aws_test.go
├── cmd/
│ └── awsping/
│ └── main.go
├── go.mod
├── request.go
├── request_test.go
├── target.go
├── utils.go
└── utils_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
.git
LICENSE
README.md
Dockerfile
awsping
================================================
FILE: .github/workflows/goreleaser.yml
================================================
name: goreleaser
on:
push:
branches:
- "!*"
tags:
- "v*.*.*"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GH_PACKAGE_TOKEN }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/pr.yml
================================================
name: pr/push checks
on:
push:
branches:
- master
paths-ignore:
- '**/*.md'
- 'Makefile'
pull_request:
branches:
- master
paths-ignore:
- '**/*.md'
- 'Makefile'
jobs:
build:
name: Build, Test, Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Lint
uses: golangci/golangci-lint-action@v2
- name: Build
run: go build -v ./...
- name: Test & Coverage
run: go test -v -coverprofile=coverage.out -covermode=atomic
- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash)
================================================
FILE: .gitignore
================================================
*.swp
TODO.md
dist/
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
coverage.out
================================================
FILE: .goreleaser.yml
================================================
project_name: awsping
builds:
- env:
- CGO_ENABLED=0
main: ./cmd/awsping
goos:
- linux
- windows
- darwin
goarch:
- amd64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^Makefile:'
- '^README:'
- '^gitignore:'
- '^goreleaser:'
dockers:
- image_templates:
- "docker.io/evkalinin/{{.ProjectName}}:{{ .Tag }}"
- "docker.io/evkalinin/{{.ProjectName}}:latest"
- "ghcr.io/ekalinin/{{.ProjectName}}:{{ .Tag }}"
- "ghcr.io/ekalinin/{{.ProjectName}}:latest"
dockerfile: Dockerfile.goreleaser
build_flag_templates:
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.name={{.ProjectName}}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.source={{.GitURL}}"
- "--platform=linux/amd64"
================================================
FILE: Dockerfile
================================================
FROM golang:1.17-bullseye as build
COPY . /build
WORKDIR /build
RUN make
FROM gcr.io/distroless/base
COPY --from=build /build/awsping /
ENTRYPOINT ["/awsping"]
================================================
FILE: Dockerfile.goreleaser
================================================
FROM gcr.io/distroless/base
COPY awsping /
ENTRYPOINT ["/awsping"]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2016 Eugene Kalinin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
NAME=awsping
EXEC=${NAME}
BUILD_DIR=build
BUILD_OS="windows darwin freebsd linux"
BUILD_ARCH="amd64 386"
BUILD_DIR=build
SRC_CMD=cmd/awsping/main.go
VERSION=`grep "Version" utils.go | grep -o -E '[0-9]\.[0-9]\.[0-9]{1,2}'`
build:
go build -race -o ${EXEC} ${SRC_CMD}
clean:
@rm -f ${EXEC}
@rm -f ${BUILD_DIR}/*
@go clean
#
# Tests, linters
#
lint:
golint
# make run ARGS="-h"
run:
go run cmd/awsping/main.go $(ARGS)
test: lint
@go test -cover .
#
# Release
#
check-version:
ifdef VERSION
@echo Current version: $(VERSION)
else
$(error VERSION is not set)
endif
check-master:
ifneq ($(shell git rev-parse --abbrev-ref HEAD),master)
$(error You're not on the "master" branch)
endif
release: check-master check-version
git tag v${VERSION} && \
git push origin v${VERSION}
release-test: check-master check-version
goreleaser release --snapshot --rm-dist
buildall: clean
@mkdir -p ${BUILD_DIR}
@for os in "${BUILD_OS}" ; do \
for arch in "${BUILD_ARCH}" ; do \
echo " * build $$os for $$arch"; \
GOOS=$$os GOARCH=$$arch go build -ldflags "-s" -o ${BUILD_DIR}/${EXEC} ${SRC_CMD}; \
cd ${BUILD_DIR}; \
tar czf ${EXEC}.$$os.$$arch.tgz ${EXEC}; \
cd - ; \
done done
@rm ${BUILD_DIR}/${EXEC}
#
# Docker
#
docker:
docker build -t awsping .
docker-run: docker
docker run awsping -verbose 2 -repeats 2
================================================
FILE: README.md
================================================
# awsping
Console tool to check the latency to each AWS region
[](https://goreportcard.com/report/github.com/ekalinin/awsping)
[](https://codecov.io/gh/ekalinin/awsping)
[](https://pkg.go.dev/github.com/ekalinin/awsping)
[](https://opensource.org/licenses/MIT)

# ToC
* [Usage](#usage)
* [Test via TCP](#test-via-tcp)
* [Test via HTTP](#test-via-http)
* [Test via HTTPS](#test-via-https)
* [Test several times](#test-several-times)
* [Verbose mode](#verbose-mode)
* [Get Help](#get-help)
* [Get binary file](#get-binary-file)
* [Build from sources](#build-from-sources)
* [Use with Docker](#use-with-docker)
* [Build a Docker image](#build-a-docker-image)
* [Run the Docker image](#run-the-docker-image)
# Usage
## Test via TCP
```bash
➥ ./awsping
Europe (Frankfurt) 51.86 ms
Europe (Ireland) 62.86 ms
US-East (Virginia) 126.39 ms
US-East (Ohio) 154.81 ms
Asia Pacific (Mumbai) 181.09 ms
US-West (California) 194.27 ms
US-West (Oregon) 211.87 ms
South America (São Paulo) 246.20 ms
Asia Pacific (Tokyo) 309.27 ms
Asia Pacific (Seoul) 322.76 ms
Asia Pacific (Sydney) 346.37 ms
Asia Pacific (Singapore) 407.91 ms
```
## Test via HTTP
```bash
➥ ./awsping -http
Europe (Frankfurt) 222.56 ms
Europe (Ireland) 226.76 ms
US-East (Virginia) 349.17 ms
US-West (California) 488.12 ms
US-East (Ohio) 513.69 ms
Asia Pacific (Mumbai) 528.51 ms
US-West (Oregon) 532.05 ms
South America (São Paulo) 599.36 ms
Asia Pacific (Seoul) 715.92 ms
Asia Pacific (Sydney) 721.47 ms
Asia Pacific (Tokyo) 745.24 ms
Asia Pacific (Singapore) 847.36 ms
```
## Test via HTTPS
```bash
➥ ./awsping -https
Europe (Stockholm) 216.67 ms
Europe (Frankfurt) 263.20 ms
Europe (Paris) 284.32 ms
Europe (Milan) 305.63 ms
Europe (Ireland) 327.34 ms
Europe (London) 332.17 ms
Middle East (Bahrain) 590.74 ms
US-East (N. Virginia) 595.13 ms
Canada (Central) 628.44 ms
US-East (Ohio) 635.32 ms
Asia Pacific (Mumbai) 755.56 ms
Asia Pacific (Hong Kong) 843.90 ms
US-West (N. California) 870.65 ms
Asia Pacific (Singapore) 899.50 ms
Africa (Cape Town) 912.06 ms
US-West (Oregon) 919.34 ms
South America (São Paulo) 985.93 ms
Asia Pacific (Tokyo) 1122.67 ms
Asia Pacific (Seoul) 1138.76 ms
Asia Pacific (Osaka) 1167.40 ms
Asia Pacific (Sydney) 1328.90 ms
```
## Test several times
```bash
➥ ./awsping -repeats 3
Europe (Frankfurt) 50.13 ms
Europe (Ireland) 62.67 ms
US-East (Virginia) 126.88 ms
US-East (Ohio) 155.37 ms
US-West (California) 195.75 ms
US-West (Oregon) 206.19 ms
Asia Pacific (Mumbai) 222.34 ms
South America (São Paulo) 254.28 ms
Asia Pacific (Tokyo) 308.52 ms
Asia Pacific (Seoul) 325.93 ms
Asia Pacific (Sydney) 349.62 ms
Asia Pacific (Singapore) 378.53 ms
```
## Verbose mode
```bash
➥ ./awsping -repeats 3 -verbose 1
Code Region Latency
0 eu-central-1 Europe (Frankfurt) 47.39 ms
1 eu-west-1 Europe (Ireland) 62.28 ms
2 us-east-1 US-East (Virginia) 128.45 ms
3 us-east-2 US-East (Ohio) 155.53 ms
4 us-west-1 US-West (California) 194.37 ms
5 us-west-2 US-West (Oregon) 208.91 ms
6 ap-south-1 Asia Pacific (Mumbai) 226.59 ms
7 sa-east-1 South America (São Paulo) 254.67 ms
8 ap-northeast-1 Asia Pacific (Tokyo) 301.97 ms
9 ap-northeast-2 Asia Pacific (Seoul) 323.10 ms
10 ap-southeast-2 Asia Pacific (Sydney) 341.26 ms
11 ap-southeast-1 Asia Pacific (Singapore) 397.47 ms
```
```bash
➥ ./awsping -repeats 3 -verbose 2
Code Region Try #1 Try #2 Try #3 Avg Latency
0 eu-central-1 Europe (Frankfurt) 45.18 ms 45.46 ms 45.68 ms 45.44 ms
1 eu-west-1 Europe (Ireland) 61.89 ms 62.99 ms 62.98 ms 62.62 ms
2 us-east-1 US-East (Virginia) 125.15 ms 126.75 ms 126.49 ms 126.13 ms
3 us-east-2 US-East (Ohio) 154.05 ms 154.28 ms 153.53 ms 153.96 ms
4 us-west-1 US-West (California) 196.20 ms 195.05 ms 193.76 ms 195.00 ms
5 us-west-2 US-West (Oregon) 204.04 ms 203.97 ms 203.84 ms 203.95 ms
6 ap-south-1 Asia Pacific (Mumbai) 175.27 ms 300.68 ms 172.18 ms 216.05 ms
7 sa-east-1 South America (São Paulo) 243.48 ms 247.12 ms 248.32 ms 246.31 ms
8 ap-northeast-1 Asia Pacific (Tokyo) 324.78 ms 312.70 ms 319.02 ms 318.83 ms
9 ap-northeast-2 Asia Pacific (Seoul) 328.96 ms 327.65 ms 326.17 ms 327.59 ms
10 ap-southeast-2 Asia Pacific (Sydney) 388.17 ms 347.74 ms 393.58 ms 376.50 ms
11 ap-southeast-1 Asia Pacific (Singapore) 409.53 ms 403.61 ms 405.84 ms 406.33 ms
```
## Get Help
```bash
➜ ./awsping -h
Usage of ./awsping:
-http
Use http transport (default is tcp)
-https
Use https transport (default is tcp)
-list-regions
Show list of regions
-repeats int
Number of repeats (default 1)
-service string
AWS Service: ec2, sdb, sns, sqs, ... (default "dynamodb")
-v Show version
-verbose int
Verbosity level
```
# Get binary file
```bash
$ wget https://github.com/ekalinin/awsping/releases/download/0.5.2/awsping.linux.amd64.tgz
$ tar xzvf awsping.linux.amd64.tgz
$ chmod +x awsping
$ ./awsping -v
0.5.2
```
# Build from sources
```bash
➥ make build
```
# Use with Docker
## Build a Docker image
```
$ docker build -t awsping .
```
## Run the Docker image
```
$ docker run --rm awsping
```
Arguments can be used as mentioned in the _Usage_ section.
i.e.:
```
$ docker run --rm awsping -repeats 3 -verbose 2
```
================================================
FILE: aws.go
================================================
package awsping
import (
"fmt"
"sync"
"time"
)
// CheckType describes a type for a check
type CheckType int
const (
// CheckTypeTCP is TCP type of check
CheckTypeTCP CheckType = iota
// CheckTypeHTTP is HTTP type of check
CheckTypeHTTP
// CheckTypeHTTPS is HTTPS type of check
CheckTypeHTTPS
)
// --------------------------------------------
// AWSRegion description of the AWS EC2 region
type AWSRegion struct {
Name string
Code string
Service string
Latencies []time.Duration
Error error
CheckType CheckType
Target Targetter
Request Requester
}
// NewRegion creates a new region with a name and code
func NewRegion(name, code string) AWSRegion {
return AWSRegion{
Name: name,
Code: code,
CheckType: CheckTypeTCP,
Request: NewAWSRequest(),
}
}
// CheckLatency does a latency check for a region
func (r *AWSRegion) CheckLatency(wg *sync.WaitGroup) {
defer wg.Done()
if r.CheckType == CheckTypeHTTP || r.CheckType == CheckTypeHTTPS {
r.checkLatencyHTTP(r.CheckType == CheckTypeHTTPS)
} else {
r.checkLatencyTCP()
}
}
// checkLatencyHTTP Test Latency via HTTP
func (r *AWSRegion) checkLatencyHTTP(https bool) {
url := r.Target.GetURL()
l, err := r.Request.Do(useragent, url, RequestTypeHTTP)
if err != nil {
r.Error = err
return
}
r.Latencies = append(r.Latencies, l)
}
// checkLatencyTCP Test Latency via TCP
func (r *AWSRegion) checkLatencyTCP() {
tcpAddr, err := r.Target.GetIP()
if err != nil {
r.Error = err
return
}
l, err := r.Request.Do(useragent, tcpAddr.String(), RequestTypeTCP)
if err != nil {
r.Error = err
return
}
r.Latencies = append(r.Latencies, l)
}
// GetLatency returns Latency in ms
func (r *AWSRegion) GetLatency() float64 {
sum := float64(0)
for _, l := range r.Latencies {
sum += Duration2ms(l)
}
return sum / float64(len(r.Latencies))
}
// GetLatencyStr returns Latency in string
func (r *AWSRegion) GetLatencyStr() string {
if r.Error != nil {
return r.Error.Error()
}
return fmt.Sprintf("%.2f ms", r.GetLatency())
}
// --------------------------------------------
// AWSRegions slice of the AWSRegion
type AWSRegions []AWSRegion
// Len returns a count of regions
func (rs AWSRegions) Len() int {
return len(rs)
}
// Less return a result of latency compare between two regions
func (rs AWSRegions) Less(i, j int) bool {
return rs[i].GetLatency() < rs[j].GetLatency()
}
// Swap two regions by index
func (rs AWSRegions) Swap(i, j int) {
rs[i], rs[j] = rs[j], rs[i]
}
// SetService sets service for all regions
func (rs AWSRegions) SetService(service string) {
for i := range rs {
rs[i].Service = service
}
}
// SetCheckType sets Check Type for all regions
func (rs AWSRegions) SetCheckType(checkType CheckType) {
for i := range rs {
rs[i].CheckType = checkType
}
}
// SetDefaultTarget sets default target instance
func (rs AWSRegions) SetDefaultTarget() {
rs.SetTarget(func(r *AWSRegion) {
r.Target = &AWSTarget{
HTTPS: r.CheckType == CheckTypeHTTPS,
Code: r.Code,
Service: r.Service,
Rnd: mkRandomString(13),
}
})
}
// SetTarget sets default target instance for all regions
func (rs AWSRegions) SetTarget(fn func(r *AWSRegion)) {
for i := range rs {
fn(&rs[i])
}
}
================================================
FILE: aws_test.go
================================================
package awsping
import (
"errors"
"fmt"
"net"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
)
func TestAWSRegionError(t *testing.T) {
AWSErr := errors.New("something bad")
r := AWSRegion{Error: AWSErr}
got := r.GetLatencyStr()
want := AWSErr.Error()
if got != want {
t.Errorf("failed:\ngot=%q\nwant=%q", got, want)
}
}
type testTarget struct {
URL string
IP *net.TCPAddr
}
func (r *testTarget) GetURL() string {
return r.URL
}
// GetIP return IP for AWS target
func (r *testTarget) GetIP() (*net.TCPAddr, error) {
return r.IP, nil
}
func TestAWSRegionCheckLatencyHTTP(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(15 * time.Millisecond)
fmt.Fprintln(w, "X")
}))
defer ts.Close()
tt := testTarget{URL: ts.URL}
regions := GetRegions()
service := "ec2"
checkType := CheckTypeHTTP
regions.SetService(service)
regions.SetCheckType(checkType)
regions.SetTarget(func(r *AWSRegion) {
r.Target = &tt
})
var wg sync.WaitGroup
wg.Add(1)
regions[0].CheckLatency(&wg)
got := regions[0].GetLatency()
want := 15.0
if got < want || got > want*2 {
t.Errorf("failed:\ngot=%f\nwant=%f", got, want)
}
// check "error"
errTxt := "something bad"
regions[0].Request = &testRequest{err: errors.New(errTxt)}
wg.Add(1)
regions[0].CheckLatency(&wg)
if regions[0].Error == nil {
t.Errorf("failed: error should not be empty")
}
if regions[0].Error.Error() != errTxt {
t.Errorf("failed: error should be empty=%s", errTxt)
}
}
type testRequest struct {
duration time.Duration
err error
}
func (d *testRequest) Do(_, _ string, _ RequestType) (time.Duration, error) {
if d.err != nil {
return 0, d.err
}
return d.duration, nil
}
func TestAWSRegionCheckLatencyTCP(t *testing.T) {
// just random local IP
tt := testTarget{IP: &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 67890,
}}
regions := GetRegions()
service := "ec2"
checkType := CheckTypeTCP
regions.SetService(service)
regions.SetCheckType(checkType)
regions.SetTarget(func(r *AWSRegion) {
r.Target = &tt
})
regions[0].Request = &testRequest{duration: 15 * time.Millisecond}
var wg sync.WaitGroup
wg.Add(1)
regions[0].CheckLatency(&wg)
got := regions[0].GetLatency()
want := 15.0
if got < want || got > want+1 {
t.Errorf("failed:\ngot=%f\nwant=%f\nregion=%q", got, want, regions[0])
}
if regions[0].Error != nil {
t.Errorf("failed: error should be empty")
}
// check "error"
errTxt := "something bad"
regions[0].Request = &testRequest{err: errors.New(errTxt)}
wg.Add(1)
regions[0].CheckLatency(&wg)
if regions[0].Error == nil {
t.Errorf("failed: error should not be empty")
}
if regions[0].Error.Error() != errTxt {
t.Errorf("failed: error should be empty=%s", errTxt)
}
}
// ---------------------------------------------
func TestAWSRegionsLen(t *testing.T) {
regions := GetRegions()
got := regions.Len()
want := len(regions)
if got != want {
t.Errorf("failed:\ngot=%q\nwant=%q", got, want)
}
}
func TestAWSRegionsLess(t *testing.T) {
regions := GetRegions()
regions[0].Latencies = []time.Duration{15 * time.Millisecond}
regions[1].Latencies = []time.Duration{25 * time.Millisecond}
if !regions.Less(0, 1) {
t.Errorf("failed: not less, regions=%q", regions)
}
}
func TestAWSRegionsSwap(t *testing.T) {
regions := GetRegions()
regions[0].Latencies = []time.Duration{15 * time.Millisecond}
regions[1].Latencies = []time.Duration{25 * time.Millisecond}
regions.Swap(0, 3)
if len(regions[0].Latencies) != 0 {
t.Errorf("failed: not swapped, regions=%q", regions)
}
}
func TestAWSRegionsSetService(t *testing.T) {
regions := GetRegions()
service := "ec2"
regions.SetService(service)
if regions[0].Service != service || regions[len(regions)-1].Service != service {
t.Errorf("failed: not set, regions=%q, service=%s", regions, service)
}
}
func TestAWSRegionsSetCheckType(t *testing.T) {
regions := GetRegions()
checkType := CheckTypeHTTP
regions.SetCheckType(checkType)
if regions[0].CheckType != checkType || regions[len(regions)-1].CheckType != checkType {
t.Errorf("failed: not set, regions=%q, checkType=%d", regions, checkType)
}
}
func TestAWSRegionsSetDefaultTarget(t *testing.T) {
regions := GetRegions()
service := "ec2"
checkType := CheckTypeHTTPS
regions.SetService(service)
regions.SetCheckType(checkType)
regions.SetDefaultTarget()
got := regions[0].Target.GetURL()
want := fmt.Sprintf("https://ec2.%s.amazonaws.com/ping?x=", regions[0].Code)
if !strings.HasPrefix(got, want) {
t.Errorf("failed: wrong url\ngot=%s\nneed=%s", got, want)
}
}
================================================
FILE: cmd/awsping/main.go
================================================
package main
import (
"flag"
"fmt"
"math/rand"
"os"
"time"
"github.com/ekalinin/awsping"
)
var (
repeats = flag.Int("repeats", 1, "Number of repeats")
useHTTP = flag.Bool("http", false, "Use http transport (default is tcp)")
useHTTPS = flag.Bool("https", false, "Use https transport (default is tcp)")
showVer = flag.Bool("v", false, "Show version")
verbose = flag.Int("verbose", 0, "Verbosity level (0: name-latency); 1: code-name-latency; 2: code-name-tries-avg")
service = flag.String("service", "dynamodb", "AWS Service: ec2, sdb, sns, sqs, ...")
listRegions = flag.Bool("list-regions", false, "Show list of regions")
)
func main() {
flag.Parse()
if *showVer {
fmt.Println(awsping.Version)
os.Exit(0)
}
regions := awsping.GetRegions()
if *listRegions {
lo := awsping.NewOutput(awsping.ShowOnlyRegions, 0)
lo.Show(®ions)
os.Exit(0)
}
rand.Seed(time.Now().UnixNano())
awsping.CalcLatency(regions, *repeats, *useHTTP, *useHTTPS, *service)
lo := awsping.NewOutput(*verbose, *repeats)
lo.Show(®ions)
}
================================================
FILE: go.mod
================================================
module github.com/ekalinin/awsping
go 1.17
================================================
FILE: request.go
================================================
package awsping
import (
"net"
"net/http"
"time"
)
// RequestType describes a type for a request type
type RequestType int
const (
// RequestTypeHTTP is HTTP type of request
RequestTypeHTTP RequestType = iota
// RequestTypeTCP is TCP type of request
RequestTypeTCP
)
// Requester is an interface to do a network request
type Requester interface {
Do(ua, url string, reqType RequestType) (time.Duration, error)
}
// AWSHTTPRequester is an interface for HTTP requests
type AWSHTTPRequester interface {
Do(req *http.Request) (*http.Response, error)
}
// AWSTCPRequester is an interface for TCP requests
type AWSTCPRequester interface {
Dial(network, address string) (net.Conn, error)
}
// AWSRequest implements Requester interface
type AWSRequest struct {
httpClient AWSHTTPRequester
tcpClient AWSTCPRequester
}
// NewAWSRequest creates a new instance of AWSRequest
func NewAWSRequest() *AWSRequest {
return &AWSRequest{
httpClient: &http.Client{},
tcpClient: &net.Dialer{},
}
}
// DoHTTP does HTTP request for a URL by User-Agent (ua)
func (r *AWSRequest) DoHTTP(ua, url string) (time.Duration, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return 0, err
}
req.Header.Set("User-Agent", ua)
start := time.Now()
resp, err := r.httpClient.Do(req)
latency := time.Since(start)
if err != nil {
return 0, err
}
defer resp.Body.Close()
return latency, nil
}
// DoTCP does TCP request to the Addr
func (r *AWSRequest) DoTCP(_, addr string) (time.Duration, error) {
start := time.Now()
conn, err := r.tcpClient.Dial("tcp", addr)
if err != nil {
return 0, err
}
l := time.Since(start)
defer conn.Close()
return l, nil
}
// Do does a request. Type of request depends on reqType
func (r *AWSRequest) Do(ua, url string, reqType RequestType) (time.Duration, error) {
if reqType == RequestTypeHTTP {
return r.DoHTTP(ua, url)
}
return r.DoTCP(ua, url)
}
================================================
FILE: request_test.go
================================================
package awsping
import (
"errors"
"net"
"net/http"
"testing"
)
type testTCPClient struct {
err error
}
func (c *testTCPClient) Dial(n, a string) (net.Conn, error) {
if c.err != nil {
return nil, c.err
}
var con net.Conn
return con, nil
}
type testHTTPClient struct {
err error
}
func (c *testHTTPClient) Do(r *http.Request) (*http.Response, error) {
if c.err != nil {
return nil, c.err
}
return &http.Response{}, nil
}
func TestRequestDoTCPError(t *testing.T) {
r := &AWSRequest{
tcpClient: &testTCPClient{
err: net.ErrWriteToConnected,
},
}
l, err := r.DoTCP("net", "some-addr")
if err == nil {
t.Errorf("Error should not be empty")
}
if !errors.Is(err, net.ErrWriteToConnected) {
t.Errorf("Want=%v, got=%v", net.ErrWriteToConnected, err)
}
if l != 0 {
t.Errorf("Latency for error should be 0, but got=%d", l)
}
}
func TestDoErr(t *testing.T) {
errTCP := errors.New("error from tcp")
errHTTP := errors.New("error from http")
r := &AWSRequest{
tcpClient: &testTCPClient{
err: errTCP,
},
httpClient: &testHTTPClient{
err: errHTTP,
},
}
l, err := r.Do("ua", "addr", RequestTypeTCP)
if err == nil {
t.Errorf("Error should not be empty")
}
if !errors.Is(err, errTCP) {
t.Errorf("Want=%v, got=%v", errTCP, err)
}
if l != 0 {
t.Errorf("Latency for error should be 0, but got=%d", l)
}
l, err = r.Do("ua", "addr", RequestTypeHTTP)
if err == nil {
t.Errorf("Error should not be empty")
}
if !errors.Is(err, errHTTP) {
t.Errorf("Want=%v, got=%v", errHTTP, err)
}
if l != 0 {
t.Errorf("Latency for error should be 0, but got=%d", l)
}
}
================================================
FILE: target.go
================================================
package awsping
import (
"fmt"
"net"
)
// Targetter is an interface to get target's IP or URL
type Targetter interface {
GetURL() string
GetIP() (*net.TCPAddr, error)
}
// AWSTarget implements Targetter for AWS
type AWSTarget struct {
HTTPS bool
Code string
Service string
Rnd string
}
// GetURL return URL for AWS target
func (r *AWSTarget) GetURL() string {
proto := "http"
if r.HTTPS {
proto = "https"
}
hostname := fmt.Sprintf("%s.%s.amazonaws.com", r.Service, r.Code)
url := fmt.Sprintf("%s://%s/ping?x=%s", proto, hostname, r.Rnd)
return url
}
// GetIP return IP for AWS target
func (r *AWSTarget) GetIP() (*net.TCPAddr, error) {
tcpURI := fmt.Sprintf("%s.%s.amazonaws.com:80", r.Service, r.Code)
return net.ResolveTCPAddr("tcp4", tcpURI)
}
================================================
FILE: utils.go
================================================
package awsping
import (
"fmt"
"io"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"
)
var (
// Version describes application version
Version = "2.0.0"
github = "https://github.com/ekalinin/awsping"
useragent = fmt.Sprintf("AwsPing/%s (+%s)", Version, github)
)
const (
// ShowOnlyRegions describes a type of output when only region's name and code printed out
ShowOnlyRegions = -1
)
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
// Duration2ms converts time.Duration to ms (float64)
func Duration2ms(d time.Duration) float64 {
return float64(d.Nanoseconds()) / 1000 / 1000
}
// mkRandomString returns random string
func mkRandomString(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
// LatencyOutput prints data into console
type LatencyOutput struct {
Level int
Repeats int
w io.Writer
}
// NewOutput creates a new LatencyOutput instance
func NewOutput(level, repeats int) *LatencyOutput {
return &LatencyOutput{
Level: level,
Repeats: repeats,
w: os.Stdout,
}
}
func (lo *LatencyOutput) show(regions *AWSRegions) {
for _, r := range *regions {
fmt.Fprintf(lo.w, "%-15s %-s\n", r.Code, r.Name)
}
}
func (lo *LatencyOutput) show0(regions *AWSRegions) {
for _, r := range *regions {
fmt.Fprintf(lo.w, "%-25s %20s\n", r.Name, r.GetLatencyStr())
}
}
func (lo *LatencyOutput) show1(regions *AWSRegions) {
outFmt := "%5v %-15s %-30s %20s\n"
fmt.Fprintf(lo.w, outFmt, "", "Code", "Region", "Latency")
for i, r := range *regions {
fmt.Fprintf(lo.w, outFmt, i, r.Code, r.Name, r.GetLatencyStr())
}
}
func (lo *LatencyOutput) show2(regions *AWSRegions) {
// format
outFmt := "%5v %-15s %-25s"
outFmt += strings.Repeat(" %15s", lo.Repeats) + " %15s\n"
// header
outStr := []interface{}{"", "Code", "Region"}
for i := 0; i < lo.Repeats; i++ {
outStr = append(outStr, "Try #"+strconv.Itoa(i+1))
}
outStr = append(outStr, "Avg Latency")
// show header
fmt.Fprintf(lo.w, outFmt, outStr...)
// each region stats
for i, r := range *regions {
outData := []interface{}{strconv.Itoa(i), r.Code, r.Name}
for n := 0; n < lo.Repeats; n++ {
outData = append(outData, fmt.Sprintf("%.2f ms",
Duration2ms(r.Latencies[n])))
}
outData = append(outData, fmt.Sprintf("%.2f ms", r.GetLatency()))
fmt.Fprintf(lo.w, outFmt, outData...)
}
}
// Show print data
func (lo *LatencyOutput) Show(regions *AWSRegions) {
switch lo.Level {
case ShowOnlyRegions:
lo.show(regions)
case 0:
lo.show0(regions)
case 1:
lo.show1(regions)
case 2:
lo.show2(regions)
}
}
// GetRegions returns a list of regions
func GetRegions() AWSRegions {
return AWSRegions{
NewRegion("Africa (Cape Town)", "af-south-1"),
NewRegion("Asia Pacific (Hong Kong)", "ap-east-1"),
NewRegion("Asia Pacific (Tokyo)", "ap-northeast-1"),
NewRegion("Asia Pacific (Seoul)", "ap-northeast-2"),
NewRegion("Asia Pacific (Osaka)", "ap-northeast-3"),
NewRegion("Asia Pacific (Mumbai)", "ap-south-1"),
NewRegion("Asia Pacific (Hyderabad)", "ap-south-2"),
NewRegion("Asia Pacific (Singapore)", "ap-southeast-1"),
NewRegion("Asia Pacific (Sydney)", "ap-southeast-2"),
NewRegion("Asia Pacific (Jakarta)", "ap-southeast-3"),
NewRegion("Asia Pacific (Melbourne)", "ap-southeast-4"),
NewRegion("Canada (Central)", "ca-central-1"),
NewRegion("Europe (Frankfurt)", "eu-central-1"),
NewRegion("Europe (Zurich)", "eu-central-2"),
NewRegion("Europe (Stockholm)", "eu-north-1"),
NewRegion("Europe (Milan)", "eu-south-1"),
NewRegion("Europe (Spain)", "eu-south-2"),
NewRegion("Europe (Ireland)", "eu-west-1"),
NewRegion("Europe (London)", "eu-west-2"),
NewRegion("Europe (Paris)", "eu-west-3"),
NewRegion("Middle East (UAE)", "me-central-1"),
NewRegion("Middle East (Bahrain)", "me-south-1"),
NewRegion("South America (Sao Paulo)", "sa-east-1"),
NewRegion("US East (N. Virginia)", "us-east-1"),
NewRegion("US East (Ohio)", "us-east-2"),
NewRegion("US West (N. California)", "us-west-1"),
NewRegion("US West (Oregon)", "us-west-2"),
NewRegion("Israel (Tel Aviv)", "il-central-1"),
}
}
// CalcLatency returns list of aws regions sorted by Latency
func CalcLatency(regions AWSRegions, repeats int, useHTTP bool, useHTTPS bool, service string) {
regions.SetService(service)
switch {
case useHTTP:
regions.SetCheckType(CheckTypeHTTP)
case useHTTPS:
regions.SetCheckType(CheckTypeHTTPS)
default:
regions.SetCheckType(CheckTypeTCP)
}
regions.SetDefaultTarget()
var wg sync.WaitGroup
for n := 1; n <= repeats; n++ {
wg.Add(len(regions))
for i := range regions {
go regions[i].CheckLatency(&wg)
}
wg.Wait()
}
sort.Sort(regions)
}
================================================
FILE: utils_test.go
================================================
package awsping
import (
"bytes"
"testing"
"time"
)
func TestDuration(t *testing.T) {
input := time.Duration(1 * time.Second)
want := 1000.0
got := Duration2ms(input)
if got != want {
t.Errorf("Duration was incorrect, got: %f, want: %f.", got, want)
}
}
func TestRandomString(t *testing.T) {
tests := []struct {
n int
res string
}{
{1, ""},
{5, ""},
{10, ""},
}
for idx, test := range tests {
test.res = mkRandomString(test.n)
if len(test.res) != test.n {
t.Errorf("Try %d: n=%d, got: %s (len=%d), want: %d.",
idx, test.n, test.res, len(test.res), test.n)
}
}
}
func TestOutputShowOnlyRegions(t *testing.T) {
var b bytes.Buffer
lo := NewOutput(ShowOnlyRegions, 0)
lo.w = &b
regions := GetRegions()[:2]
lo.Show(®ions)
got := b.String()
want := "af-south-1 Africa (Cape Town)\n" +
"ap-east-1 Asia Pacific (Hong Kong)\n"
if got != want {
t.Errorf("Show:\ngot =%q\nwant=%q", got, want)
}
}
func TestOutputShow0(t *testing.T) {
var b bytes.Buffer
lo := NewOutput(0, 0)
lo.w = &b
regions := GetRegions()[:2]
regions[0].Latencies = []time.Duration{15 * time.Millisecond}
regions[1].Latencies = []time.Duration{25 * time.Millisecond}
lo.Show(®ions)
want := "Africa (Cape Town) 15.00 ms\n" +
"Asia Pacific (Hong Kong) 25.00 ms\n"
got := b.String()
if got != want {
t.Errorf("Show0 failed:\ngot =%q\nwant=%q", got, want)
}
}
func TestOutputShow1(t *testing.T) {
var b bytes.Buffer
lo := NewOutput(1, 0)
lo.w = &b
regions := GetRegions()[:2]
regions[0].Latencies = []time.Duration{15 * time.Millisecond}
regions[1].Latencies = []time.Duration{25 * time.Millisecond}
lo.Show(®ions)
got := b.String()
want := " Code Region Latency\n" +
" 0 af-south-1 Africa (Cape Town) 15.00 ms\n" +
" 1 ap-east-1 Asia Pacific (Hong Kong) 25.00 ms\n"
if got != want {
t.Errorf("Show1 failed:\ngot =%q\nwant=%q", got, want)
}
}
func TestOutputShow2(t *testing.T) {
var b bytes.Buffer
lo := NewOutput(2, 2)
lo.w = &b
regions := GetRegions()[:2]
regions[0].Latencies = []time.Duration{15 * time.Millisecond, 17 * time.Millisecond}
regions[1].Latencies = []time.Duration{25 * time.Millisecond, 26 * time.Millisecond}
lo.Show(®ions)
got := b.String()
want := " Code Region Try #1 Try #2 Avg Latency\n" +
" 0 af-south-1 Africa (Cape Town) 15.00 ms 17.00 ms 16.00 ms\n" +
" 1 ap-east-1 Asia Pacific (Hong Kong) 25.00 ms 26.00 ms 25.50 ms\n"
if got != want {
t.Errorf("Show2 failed:\ngot =%q\nwant=%q", got, want)
}
}
func TestCalcLatency(t *testing.T) {
regions := GetRegions()[:3]
regions[0].Request = &testRequest{duration: 30 * time.Millisecond}
regions[1].Request = &testRequest{duration: 7 * time.Millisecond}
regions[2].Request = &testRequest{duration: 15 * time.Millisecond}
regionsStats := make(AWSRegions, regions.Len())
checkSort := func(origIndex, sortedIdx int) {
got := regionsStats[sortedIdx].Name
want := regions[origIndex].Name
if got != want {
t.Errorf("CalcLatency failed:\ngot=%q\nwant=%q\norig=%d\nsorted=%d",
got, want, origIndex, sortedIdx)
}
}
for i := 1; i < 4; i++ {
copy(regionsStats, regions)
switch i {
case 1:
CalcLatency(regionsStats, 1, false, false, "ec2")
case 2:
CalcLatency(regionsStats, 1, true, false, "ec2")
default:
CalcLatency(regionsStats, 1, true, true, "ec2")
}
checkSort(0, 2)
checkSort(1, 0)
checkSort(2, 1)
}
}
gitextract_d_wjxg93/ ├── .dockerignore ├── .github/ │ └── workflows/ │ ├── goreleaser.yml │ └── pr.yml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── Dockerfile.goreleaser ├── LICENSE ├── Makefile ├── README.md ├── aws.go ├── aws_test.go ├── cmd/ │ └── awsping/ │ └── main.go ├── go.mod ├── request.go ├── request_test.go ├── target.go ├── utils.go └── utils_test.go
SYMBOL INDEX (74 symbols across 8 files)
FILE: aws.go
type CheckType (line 10) | type CheckType
constant CheckTypeTCP (line 14) | CheckTypeTCP CheckType = iota
constant CheckTypeHTTP (line 16) | CheckTypeHTTP
constant CheckTypeHTTPS (line 18) | CheckTypeHTTPS
type AWSRegion (line 24) | type AWSRegion struct
method CheckLatency (line 47) | func (r *AWSRegion) CheckLatency(wg *sync.WaitGroup) {
method checkLatencyHTTP (line 58) | func (r *AWSRegion) checkLatencyHTTP(https bool) {
method checkLatencyTCP (line 69) | func (r *AWSRegion) checkLatencyTCP() {
method GetLatency (line 85) | func (r *AWSRegion) GetLatency() float64 {
method GetLatencyStr (line 94) | func (r *AWSRegion) GetLatencyStr() string {
function NewRegion (line 37) | func NewRegion(name, code string) AWSRegion {
type AWSRegions (line 104) | type AWSRegions
method Len (line 107) | func (rs AWSRegions) Len() int {
method Less (line 112) | func (rs AWSRegions) Less(i, j int) bool {
method Swap (line 117) | func (rs AWSRegions) Swap(i, j int) {
method SetService (line 122) | func (rs AWSRegions) SetService(service string) {
method SetCheckType (line 129) | func (rs AWSRegions) SetCheckType(checkType CheckType) {
method SetDefaultTarget (line 136) | func (rs AWSRegions) SetDefaultTarget() {
method SetTarget (line 148) | func (rs AWSRegions) SetTarget(fn func(r *AWSRegion)) {
FILE: aws_test.go
function TestAWSRegionError (line 15) | func TestAWSRegionError(t *testing.T) {
type testTarget (line 27) | type testTarget struct
method GetURL (line 32) | func (r *testTarget) GetURL() string {
method GetIP (line 37) | func (r *testTarget) GetIP() (*net.TCPAddr, error) {
function TestAWSRegionCheckLatencyHTTP (line 41) | func TestAWSRegionCheckLatencyHTTP(t *testing.T) {
type testRequest (line 87) | type testRequest struct
method Do (line 92) | func (d *testRequest) Do(_, _ string, _ RequestType) (time.Duration, e...
function TestAWSRegionCheckLatencyTCP (line 99) | func TestAWSRegionCheckLatencyTCP(t *testing.T) {
function TestAWSRegionsLen (line 150) | func TestAWSRegionsLen(t *testing.T) {
function TestAWSRegionsLess (line 161) | func TestAWSRegionsLess(t *testing.T) {
function TestAWSRegionsSwap (line 172) | func TestAWSRegionsSwap(t *testing.T) {
function TestAWSRegionsSetService (line 185) | func TestAWSRegionsSetService(t *testing.T) {
function TestAWSRegionsSetCheckType (line 196) | func TestAWSRegionsSetCheckType(t *testing.T) {
function TestAWSRegionsSetDefaultTarget (line 207) | func TestAWSRegionsSetDefaultTarget(t *testing.T) {
FILE: cmd/awsping/main.go
function main (line 23) | func main() {
FILE: request.go
type RequestType (line 10) | type RequestType
constant RequestTypeHTTP (line 14) | RequestTypeHTTP RequestType = iota
constant RequestTypeTCP (line 16) | RequestTypeTCP
type Requester (line 20) | type Requester interface
type AWSHTTPRequester (line 25) | type AWSHTTPRequester interface
type AWSTCPRequester (line 30) | type AWSTCPRequester interface
type AWSRequest (line 35) | type AWSRequest struct
method DoHTTP (line 49) | func (r *AWSRequest) DoHTTP(ua, url string) (time.Duration, error) {
method DoTCP (line 69) | func (r *AWSRequest) DoTCP(_, addr string) (time.Duration, error) {
method Do (line 82) | func (r *AWSRequest) Do(ua, url string, reqType RequestType) (time.Dur...
function NewAWSRequest (line 41) | func NewAWSRequest() *AWSRequest {
FILE: request_test.go
type testTCPClient (line 10) | type testTCPClient struct
method Dial (line 14) | func (c *testTCPClient) Dial(n, a string) (net.Conn, error) {
type testHTTPClient (line 22) | type testHTTPClient struct
method Do (line 26) | func (c *testHTTPClient) Do(r *http.Request) (*http.Response, error) {
function TestRequestDoTCPError (line 33) | func TestRequestDoTCPError(t *testing.T) {
function TestDoErr (line 53) | func TestDoErr(t *testing.T) {
FILE: target.go
type Targetter (line 9) | type Targetter interface
type AWSTarget (line 15) | type AWSTarget struct
method GetURL (line 23) | func (r *AWSTarget) GetURL() string {
method GetIP (line 34) | func (r *AWSTarget) GetIP() (*net.TCPAddr, error) {
FILE: utils.go
constant ShowOnlyRegions (line 24) | ShowOnlyRegions = -1
function Duration2ms (line 30) | func Duration2ms(d time.Duration) float64 {
function mkRandomString (line 35) | func mkRandomString(n int) string {
type LatencyOutput (line 44) | type LatencyOutput struct
method show (line 59) | func (lo *LatencyOutput) show(regions *AWSRegions) {
method show0 (line 65) | func (lo *LatencyOutput) show0(regions *AWSRegions) {
method show1 (line 71) | func (lo *LatencyOutput) show1(regions *AWSRegions) {
method show2 (line 79) | func (lo *LatencyOutput) show2(regions *AWSRegions) {
method Show (line 106) | func (lo *LatencyOutput) Show(regions *AWSRegions) {
function NewOutput (line 51) | func NewOutput(level, repeats int) *LatencyOutput {
function GetRegions (line 120) | func GetRegions() AWSRegions {
function CalcLatency (line 154) | func CalcLatency(regions AWSRegions, repeats int, useHTTP bool, useHTTPS...
FILE: utils_test.go
function TestDuration (line 9) | func TestDuration(t *testing.T) {
function TestRandomString (line 18) | func TestRandomString(t *testing.T) {
function TestOutputShowOnlyRegions (line 37) | func TestOutputShowOnlyRegions(t *testing.T) {
function TestOutputShow0 (line 55) | func TestOutputShow0(t *testing.T) {
function TestOutputShow1 (line 75) | func TestOutputShow1(t *testing.T) {
function TestOutputShow2 (line 96) | func TestOutputShow2(t *testing.T) {
function TestCalcLatency (line 117) | func TestCalcLatency(t *testing.T) {
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (39K chars).
[
{
"path": ".dockerignore",
"chars": 42,
"preview": ".git\nLICENSE\nREADME.md\nDockerfile\nawsping\n"
},
{
"path": ".github/workflows/goreleaser.yml",
"chars": 971,
"preview": "name: goreleaser\n\non:\n push:\n branches:\n - \"!*\"\n tags:\n - \"v*.*.*\"\n\njobs:\n goreleaser:\n runs-on: ub"
},
{
"path": ".github/workflows/pr.yml",
"chars": 727,
"preview": "name: pr/push checks\n\non:\n push:\n branches:\n - master\n paths-ignore:\n - '**/*.md'\n - 'Makefile'\n "
},
{
"path": ".gitignore",
"chars": 300,
"preview": "*.swp\nTODO.md\ndist/\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test"
},
{
"path": ".goreleaser.yml",
"chars": 1048,
"preview": "project_name: awsping\nbuilds:\n- env:\n - CGO_ENABLED=0\n main: ./cmd/awsping\n goos:\n - linux\n - windows\n - dar"
},
{
"path": "Dockerfile",
"chars": 162,
"preview": "FROM golang:1.17-bullseye as build\nCOPY . /build\nWORKDIR /build\nRUN make\n\nFROM gcr.io/distroless/base\nCOPY --from=build "
},
{
"path": "Dockerfile.goreleaser",
"chars": 68,
"preview": "FROM gcr.io/distroless/base\nCOPY awsping /\n\nENTRYPOINT [\"/awsping\"]\n"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2016 Eugene Kalinin\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "Makefile",
"chars": 1340,
"preview": "NAME=awsping\nEXEC=${NAME}\nBUILD_DIR=build\nBUILD_OS=\"windows darwin freebsd linux\"\nBUILD_ARCH=\"amd64 386\"\nBUILD_DIR=build"
},
{
"path": "README.md",
"chars": 7431,
"preview": "# awsping\nConsole tool to check the latency to each AWS region\n\n[\n\n// CheckType describes a type for a check\ntype CheckType int\n\nconst "
},
{
"path": "aws_test.go",
"chars": 4679,
"preview": "package awsping\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\""
},
{
"path": "cmd/awsping/main.go",
"chars": 1072,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/ekalinin/awsping\"\n)\n\nvar (\n\trepeats ="
},
{
"path": "go.mod",
"chars": 44,
"preview": "module github.com/ekalinin/awsping\n\ngo 1.17\n"
},
{
"path": "request.go",
"chars": 1924,
"preview": "package awsping\n\nimport (\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n)\n\n// RequestType describes a type for a request type\ntype RequestT"
},
{
"path": "request_test.go",
"chars": 1622,
"preview": "package awsping\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"testing\"\n)\n\ntype testTCPClient struct {\n\terr error\n}\n\nfunc (c *"
},
{
"path": "target.go",
"chars": 779,
"preview": "package awsping\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\n// Targetter is an interface to get target's IP or URL\ntype Targetter interfa"
},
{
"path": "utils.go",
"chars": 4763,
"preview": "package awsping\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"os\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\t// Ver"
},
{
"path": "utils_test.go",
"chars": 3697,
"preview": "package awsping\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestDuration(t *testing.T) {\n\tinput := time.Duration(1 * t"
}
]
About this extraction
This page contains the full source code of the ekalinin/awsping GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (34.2 KB), approximately 10.8k tokens, and a symbol index with 74 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.