Compare commits

..

45 commits

Author SHA1 Message Date
Jess Frazelle
efad427c69
update generation
Signed-off-by: Jess Frazelle <jess@oxide.computer>
2020-09-17 13:15:13 -07:00
Jess Frazelle
0216665023
Merge branch 'master' of https://github.com/genuinetools/reg
* 'master' of https://github.com/genuinetools/reg:
  docs: Update TOC
2019-11-19 22:47:31 -08:00
Jess Frazelle
4203e559f3
Bump version to v0.16.1
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-11-19 22:47:08 -08:00
github-actions[bot]
a89166537a
docs: Update TOC 2019-11-20 06:42:33 +00:00
Jess Frazelle
6d5526bfa4
update to github actiions
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-11-19 22:41:52 -08:00
Jess Frazelle
f180c93a39
update vendor
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-09-16 13:36:44 -07:00
Jess Frazelle
eaee914121
update dockerfile
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-09-16 12:31:08 -07:00
Jess Frazelle
a711cf7c60
update certs
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-07-24 17:44:42 -07:00
Tomoya Amachi
e29a4fdc2e Fix GCR errors (#182)
* change accepts header

* change accepts header

* skip gcr ping check

* update go.sum

* fix lint errors

* fix lint errors

* update go.mod
2019-06-11 00:57:48 +08:00
Jess Frazelle
2bd71359e7 fix tests
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-05-25 18:01:15 -04:00
Jess Frazelle
f640b83d43 update travis
Signed-off-by: Jess Frazelle <acidburn@jessfraz.com>
2019-05-25 16:51:11 -04:00
Jess Frazelle
d959057b30
fix staticcheck
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2019-01-02 11:55:23 -05:00
Jess Frazelle
067711f90e
fix getting official registry manifest
closes #164

Signed-off-by: Jess Frazelle <acidburn@github.com>
2019-01-02 11:19:56 -05:00
Jessica Tracy
32589e90be Passing context (#163)
* passing context in layer calls

* more contexting

* clair folder and context in handlers

* fixed token transport to reuse request context

* tests

* taking out context pass in server handlers
2018-12-29 12:09:10 -05:00
toc-me[bot]
5635f17ffc Update ToC for README.md 2018-12-29 16:10:26 +00:00
Jess Frazelle
e4bfdb3734
toc
Signed-off-by: Jess Frazelle <acidburn@github.com>
2018-12-29 11:10:19 -05:00
Jess Frazelle
3f77609b96
switch to 11
Signed-off-by: Jess Frazelle <acidburn@github.com>
2018-12-28 22:28:30 -05:00
Jess Frazelle
e7be5585cc
update dev dockerfile
Signed-off-by: Jess Frazelle <acidburn@github.com>
2018-12-28 22:06:13 -05:00
Jess Frazelle
63a4646318
update travis
Signed-off-by: Jess Frazelle <acidburn@github.com>
2018-12-28 21:54:59 -05:00
Pirmin Tapken
56104f3f9b Fix token transport memory leak (#165)
* Test for TokenTransport proving memory leak

* Fix memory leak on wrong auth challenge header

* Add test for memory leak during authentication

The body of the first request must be closed before doing the request to
the authentication service

* Fix memory leak during authentication
2018-12-11 15:12:21 -05:00
Jess Frazelle
4a4d0e5d10
fix api;
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-11-14 10:18:18 -05:00
Jess Frazelle
45c0a0c5f9
Bump version to v0.16.0
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-11-14 10:09:54 -05:00
Jess Frazelle
3b848ce686
updates and fixes
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-11-14 10:09:47 -05:00
alexander zillion
db513b737e add --auth-url parameter (#90) (#105)
Signed-off-by: Alex Zillion <alex@alexzillion.com>
2018-11-14 10:06:30 -05:00
Julien
383e4f7d48 Add force-non-ssl to registry operations (#102) 2018-11-14 10:02:41 -05:00
Jamie
83c621c4a7 support 'access_token' for compatibility with OAuth 2.0 https://docs.docker.com/registry/spec/auth/token/#requesting-a-token (#124) 2018-11-14 10:01:36 -05:00
Darrian
478c4dadc6 confirm the requested schema version matches the returned (#148) 2018-11-14 09:51:57 -05:00
Matt Brown
cbabe28c68 handle AuthConfigs with empty ServerAddress (#160)
fixes #159
2018-11-14 09:49:49 -05:00
Darrian
3033420c34 method for closing of gRPC connection (#155) 2018-11-14 09:32:03 -05:00
Darrian
67d1af130d instead of using a bool to check for v2, just test the type of the response interface. fixes #132 (#156) 2018-10-29 08:32:59 -07:00
Mansour Rahimi
d6e3a0aa00 list: gracefully fail in case of invalid registry (#158)
If the given domain is not a valid registry and try to get the
repository list, it fails in unmarshalling and show the JSON syntax
error.

This will catch the error and show a better error message.

Fixes: #157
2018-10-29 08:27:22 -07:00
Darrian
23cd8e4025 Change the naming of the way Clair V3 reports are stored (#150)
* changed the naming of the way clair reports are stored

* revert changes to clair v2 method
2018-10-22 14:32:15 -07:00
alexander zillion
938012b555 Fix golint import path (#153) 2018-10-22 14:07:55 -07:00
Knut Ahlers
eb4e1cc686 Fix vuln scanning on GCR (#152)
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2018-10-17 00:53:20 -07:00
Jess Frazelle
1ea91e7493
fix vulns.json
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-10-04 14:41:48 -04:00
Jess Frazelle
780a52e149
fix vulns.json
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-10-04 14:41:40 -04:00
Jess Frazelle
7bca357605
fixup template
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-10-04 14:28:38 -04:00
Jess Frazelle
51215f9997
fixup template
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-10-04 14:20:41 -04:00
Jess Frazelle
4f449c580a
fixup template
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-10-04 14:18:28 -04:00
Jess Frazelle
b57209439c
cleanup vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 11:35:16 -04:00
Jess Frazelle
7e55384ffa
cleanup vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 11:32:39 -04:00
Jess Frazelle
d021aeb0b9
Bump version to v0.15.8
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 10:40:12 -04:00
Jess Frazelle
75eea3d0e8
update makefile
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 10:15:33 -04:00
Jess Frazelle
729aebcabb
update makefile
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 10:06:59 -04:00
Jess Frazelle
d8fc58386d
update makefile
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 09:33:37 -04:00
1069 changed files with 110019 additions and 165402 deletions

13
.github/workflows/make-all.yml vendored Normal file
View file

@ -0,0 +1,13 @@
on: push
name: make all
jobs:
makeall:
name: make all
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: make all
uses: docker://golang:latest
with:
entrypoint: bash
args: -c "go get honnef.co/go/tools/cmd/staticcheck && go get golang.org/x/lint/golint && make fmt lint staticcheck vet install"

11
.github/workflows/make-image.yml vendored Normal file
View file

@ -0,0 +1,11 @@
on: push
name: make image
jobs:
makeimage:
name: make image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: make image
run: make image
shell: bash

29
.github/workflows/make-release.yml vendored Normal file
View file

@ -0,0 +1,29 @@
on:
push:
tags:
- v*
name: make release
jobs:
makerelease:
name: make release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: make release
uses: docker://golang:latest
with:
args: make release
- name: upload assets
uses: docker://r.j3ss.co/github-dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
entrypoint: bash
args: -c "upload-assets ./cross/*"
- name: update release body
uses: docker://r.j3ss.co/pepper
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
entrypoint: sh
args: -c "pepper release --repo $GITHUB_REPOSITORY -d --nouser"

11
.github/workflows/make-test.yml vendored Normal file
View file

@ -0,0 +1,11 @@
on: push
name: make test
jobs:
maketest:
name: make test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: make test
run: DOCKER_API_VERSION=1.39 make dind dtest
shell: bash

15
.github/workflows/toc.yml vendored Normal file
View file

@ -0,0 +1,15 @@
on:
push:
branches:
- master # Push events on master branch
name: Table of Contents Generator
jobs:
generateTOC:
name: TOC Generator
runs-on: ubuntu-latest
steps:
- name: TOC Generator
uses: technote-space/toc-generator@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TARGET_PATHS: "README.md"

11
.gitignore vendored
View file

@ -43,12 +43,15 @@ Icon
.Trashes
reg
testreg
.certs
cross/
server/static/index.html
server/static/repo/
# Go coverage results
coverage.txt
profile.out
!go.mod
testreg
.certs
server/static/index.html
server/static/repo/

View file

@ -1,56 +0,0 @@
language: go
sudo: true
services:
- docker
go:
- 1.10.x
before_install:
- go get github.com/golang/lint/golint
- go get honnef.co/go/tools/cmd/staticcheck
jobs:
include:
- script:
- make fmt lint staticcheck vet install
- DOCKER_API_VERSION=1.38 make dind dtest
- stage: Build Release
script:
- make release
- echo "Deploying to GitHub releases ..."
deploy:
provider: releases
file:
- cross/reg-darwin-amd64
- cross/reg-darwin-amd64.md5
- cross/reg-darwin-amd64.sha256
- cross/reg-darwin-386
- cross/reg-darwin-386.md5
- cross/reg-darwin-386.sha256
- cross/reg-freebsd-amd64
- cross/reg-freebsd-amd64.md5
- cross/reg-freebsd-amd64.sha256
- cross/reg-freebsd-386
- cross/reg-freebsd-386.md5
- cross/reg-freebsd-386.sha256
- cross/reg-linux-arm
- cross/reg-linux-arm.md5
- cross/reg-linux-arm.sha256
- cross/reg-linux-arm64
- cross/reg-linux-arm64.md5
- cross/reg-linux-arm64.sha256
- cross/reg-linux-amd64
- cross/reg-linux-amd64.md5
- cross/reg-linux-amd64.sha256
- cross/reg-linux-386
- cross/reg-linux-386.md5
- cross/reg-linux-386.sha256
- cross/reg-windows-amd64
- cross/reg-windows-amd64.md5
- cross/reg-windows-amd64.sha256
- cross/reg-windows-386
- cross/reg-windows-386.md5
- cross/reg-windows-386.sha256
skip_cleanup: true
on:
tags: true
api_key:
secure: "AULDRJQ8olD4R3v35sCnSZx136DnJnqMkX3ANNC/gosQhI+sFViW7BT0kaQBcIqBjF3jrik4Zm4BAW0VSZruwahD+pxjf/uroYAraMaFohw5SCL+CIY4qgNM/kkkZXTAP5DvPIOKEmXY8FfL1ZD0C6B8OKabRjh2rxuoh+enjflrA/B6B98yqo/NufCgfqfhwSK7xnh7kY4DCwfpQ80fQFzwj1BQQ5bBpP7tTVpZVqnbfSTSUoV8pX+2Ef/+t3KgYWx11+zkML9GbLevf7SbaMP9qceLvSW2npjzK1vCdQEtKp5OEuvaqsHXpdV58EdQx01zt5RSKSj/EcVaASwt2dbR10kTV7hBTvNqmvStjjKavCKzyohHG5s8VOrTXnIezuxXe1l9P9teIktY+uFClsC9t8jkQZZlVYn8brWMp+oU1VtVgYQRiF4p08IxUUS5DtZ1ZML9Yqh0am1lo2EFRydOBiIVoG2cSO8IbvDYYhIUrfc+pZ01vBV+sp+5EGmNp/7wgLwNSzoAgjh6BRLZO7irICcd579L4ZLWYX8k7/oXFDb8ABjzl0/Fk4K6EL1TTQzJaabxVAT1SI7b2PnqhJx7f5xPbNdNIQAQ/+I0sN7hpIJ0o/VaqObGKIdJeZSWISQ5TTlK+TtbucAtNaSgw0rhNYrIslK2bfgODa6MfJc="

View file

@ -1 +0,0 @@
AULDRJQ8olD4R3v35sCnSZx136DnJnqMkX3ANNC/gosQhI+sFViW7BT0kaQBcIqBjF3jrik4Zm4BAW0VSZruwahD+pxjf/uroYAraMaFohw5SCL+CIY4qgNM/kkkZXTAP5DvPIOKEmXY8FfL1ZD0C6B8OKabRjh2rxuoh+enjflrA/B6B98yqo/NufCgfqfhwSK7xnh7kY4DCwfpQ80fQFzwj1BQQ5bBpP7tTVpZVqnbfSTSUoV8pX+2Ef/+t3KgYWx11+zkML9GbLevf7SbaMP9qceLvSW2npjzK1vCdQEtKp5OEuvaqsHXpdV58EdQx01zt5RSKSj/EcVaASwt2dbR10kTV7hBTvNqmvStjjKavCKzyohHG5s8VOrTXnIezuxXe1l9P9teIktY+uFClsC9t8jkQZZlVYn8brWMp+oU1VtVgYQRiF4p08IxUUS5DtZ1ZML9Yqh0am1lo2EFRydOBiIVoG2cSO8IbvDYYhIUrfc+pZ01vBV+sp+5EGmNp/7wgLwNSzoAgjh6BRLZO7irICcd579L4ZLWYX8k7/oXFDb8ABjzl0/Fk4K6EL1TTQzJaabxVAT1SI7b2PnqhJx7f5xPbNdNIQAQ/+I0sN7hpIJ0o/VaqObGKIdJeZSWISQ5TTlK+TtbucAtNaSgw0rhNYrIslK2bfgODa6MfJc=

View file

@ -5,6 +5,7 @@ ENV PATH /go/bin:/usr/local/go/bin:$PATH
ENV GOPATH /go
RUN apk add --no-cache \
bash \
ca-certificates
COPY . /go/src/github.com/genuinetools/reg

View file

@ -1,6 +1,7 @@
FROM golang:alpine
RUN apk add --no-cache \
bash \
build-base \
ca-certificates \
git

541
Gopkg.lock generated
View file

@ -1,541 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:81f8c061c3d18ed1710957910542bc17d2b789c6cd19e0f654c30b35fd255ca5"
name = "github.com/Azure/go-ansiterm"
packages = [
".",
"winterm",
]
pruneopts = "NUT"
revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
[[projects]]
digest = "1:2be791e7b333ff7c06f8fb3dc18a7d70580e9399dbdffd352621d067ff260b6e"
name = "github.com/Microsoft/go-winio"
packages = ["."]
pruneopts = "NUT"
revision = "97e4973ce50b2ff5f09635a57e2b88a037aae829"
version = "v0.4.11"
[[projects]]
branch = "master"
digest = "1:7da1a26e347165227c79dfb10b90b3b5dedb6cbae50e88cdb81f5b6259b5b951"
name = "github.com/Nvveen/Gotty"
packages = ["."]
pruneopts = "NUT"
revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
[[projects]]
branch = "master"
digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "NUT"
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
branch = "master"
digest = "1:4a029051269e04c040c092eb4ddd92732f8f3a3921a8b43b82b30804e00f3357"
name = "github.com/containerd/continuity"
packages = ["pathdriver"]
pruneopts = "NUT"
revision = "c2ac4ecc959316e616c37fd95143e972811bd12e"
[[projects]]
branch = "master"
digest = "1:2b7e45c96a900225df4288dd1a8a2a1c34477027274cbba6ff5320110aa7a02a"
name = "github.com/coreos/clair"
packages = [
"api/v3/clairpb",
"database",
"ext/versionfmt",
"pkg/pagination",
]
pruneopts = "NUT"
revision = "d0a3fe9206309b34e562887f886d908f51e0821a"
[[projects]]
branch = "master"
digest = "1:3d253a76fba28c8ed28b517fd89b362b4e0116d05833023bcc987111b5aa9b15"
name = "github.com/docker/cli"
packages = [
"cli/config/configfile",
"cli/config/credentials",
"opts",
]
pruneopts = "NUT"
revision = "b4180e8757e9d5f9b38813e10c4f4a1c0f6643b5"
[[projects]]
branch = "master"
digest = "1:1aed2b27277a9c4a2b3b40abf3e0484704a35482f24a804e7791c5172dcbf323"
name = "github.com/docker/distribution"
packages = [
".",
"digestset",
"manifest",
"manifest/manifestlist",
"manifest/schema1",
"manifest/schema2",
"metrics",
"reference",
"registry/api/errcode",
"registry/api/v2",
"registry/client",
"registry/client/auth",
"registry/client/auth/challenge",
"registry/client/transport",
"registry/storage/cache",
"registry/storage/cache/memory",
]
pruneopts = "NUT"
revision = "b089e91688254eb5aef187f188c8ee0ebe4e4675"
[[projects]]
branch = "master"
digest = "1:29566f3fa9f66bf12978baba01329b29da05526cace5c15a74e1a61f7f3a4381"
name = "github.com/docker/docker"
packages = [
"api",
"api/types",
"api/types/blkiodev",
"api/types/container",
"api/types/events",
"api/types/filters",
"api/types/image",
"api/types/mount",
"api/types/network",
"api/types/registry",
"api/types/strslice",
"api/types/swarm",
"api/types/swarm/runtime",
"api/types/time",
"api/types/versions",
"api/types/volume",
"client",
"errdefs",
"pkg/homedir",
"pkg/idtools",
"pkg/ioutils",
"pkg/jsonmessage",
"pkg/longpath",
"pkg/mount",
"pkg/stringid",
"pkg/system",
"pkg/tarsum",
"pkg/term",
"pkg/term/windows",
"registry",
"registry/resumable",
]
pruneopts = "NUT"
revision = "ed392603acbf7849aa68d760c67d9c645ac035d8"
[[projects]]
branch = "master"
digest = "1:961fe5dd401b2284aff5141c1076e6a5cce10fcf2e6440b79bb7e2ea353d1c01"
name = "github.com/docker/docker-ce"
packages = ["components/cli/cli/config"]
pruneopts = "NUT"
revision = "d156a93844295ae887439d37031e71737db7b5e0"
[[projects]]
digest = "1:8866486038791fe65ea1abf660041423954b1f3fb99ea6a0ad8424422e943458"
name = "github.com/docker/docker-credential-helpers"
packages = [
"client",
"credentials",
]
pruneopts = "NUT"
revision = "5241b46610f2491efdf9d1c85f1ddf5b02f6d962"
version = "v0.6.1"
[[projects]]
branch = "master"
digest = "1:42cfcc9461365aacb806b9cfe929c926f0d3fb0f692bfc10c375872f2881b2f2"
name = "github.com/docker/go-connections"
packages = [
"nat",
"sockets",
"tlsconfig",
]
pruneopts = "NUT"
revision = "97c2040d34dfae1d1b1275fa3a78dbdd2f41cf7e"
[[projects]]
branch = "master"
digest = "1:8e5e04894d9d663ac464d6e73803d602f85fe679ec5a54de35378b5d073efd64"
name = "github.com/docker/go-metrics"
packages = ["."]
pruneopts = "NUT"
revision = "399ea8c73916000c64c2c76e8da00ca82f8387ab"
[[projects]]
branch = "master"
digest = "1:4340101f42556a9cb2f7a360a0e95a019bfef6247d92e6c4c46f2433cf86a482"
name = "github.com/docker/go-units"
packages = ["."]
pruneopts = "NUT"
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
[[projects]]
branch = "master"
digest = "1:ce43438a8204a4259b4461153a392bc3e504bef7e4785a8192344f002c7bd935"
name = "github.com/docker/libtrust"
packages = ["."]
pruneopts = "NUT"
revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20"
[[projects]]
branch = "master"
digest = "1:e101620a1bea7755f45d9cca76bfd505e3f4678b47f37209838ddab235341d1c"
name = "github.com/fernet/fernet-go"
packages = ["."]
pruneopts = "NUT"
revision = "9eac43b88a5efb8651d24de9b68e87567e029736"
[[projects]]
branch = "master"
digest = "1:bd228d1a02fffcaf3abca973886870113d843ea7a67d5b6f70a5169950499ea9"
name = "github.com/genuinetools/pkg"
packages = ["cli"]
pruneopts = "NUT"
revision = "1c141f661797b5fb22033c9a7a5ab004994a0404"
[[projects]]
digest = "1:38e684375ef5b55e812332266d63f9fc5b6329ab303067c4cdda051db6d29ca4"
name = "github.com/gogo/protobuf"
packages = ["proto"]
pruneopts = "NUT"
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
version = "v1.1.1"
[[projects]]
digest = "1:9b117ac202b9de210a7093bca0c29d0d2424e4c9235c9e025ae0d6ef6b121c82"
name = "github.com/golang/protobuf"
packages = [
"jsonpb",
"proto",
"protoc-gen-go/descriptor",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/struct",
"ptypes/timestamp",
]
pruneopts = "NUT"
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0"
[[projects]]
digest = "1:2e3c336fc7fde5c984d2841455a658a6d626450b1754a854b3b32e7a8f49a07a"
name = "github.com/google/go-cmp"
packages = [
"cmp",
"cmp/internal/diff",
"cmp/internal/function",
"cmp/internal/value",
]
pruneopts = "NUT"
revision = "3af367b6b30c263d47e8895973edcca9a49cf029"
version = "v0.2.0"
[[projects]]
digest = "1:c01767916c59f084bb7c41a7d5877c0f3099b1595cfa066e84ec6ad6b084dd89"
name = "github.com/gorilla/context"
packages = ["."]
pruneopts = "NUT"
revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42"
version = "v1.1.1"
[[projects]]
digest = "1:bf5cf1d53d703332e9bd8984c69784645b73a938317bf5ace9aadf20ac49379a"
name = "github.com/gorilla/mux"
packages = ["."]
pruneopts = "NUT"
revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]]
digest = "1:228dfa88f068306d5d23fb3efac8e61b868e1d327aac6eebc21f0f15b03e44e4"
name = "github.com/grpc-ecosystem/grpc-gateway"
packages = [
"runtime",
"runtime/internal",
"utilities",
]
pruneopts = "NUT"
revision = "8558711daa6c2853489043207b563dceacbc19cf"
version = "v1.5.0"
[[projects]]
digest = "1:5985ef4caf91ece5d54817c11ea25f182697534f8ae6521eadcd628c142ac4b6"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "NUT"
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
digest = "1:9f63926f52745d1f9d6053f8fbbb3bd3983c2c97c0565957e682b8d2f7aad3af"
name = "github.com/mitchellh/go-wordwrap"
packages = ["."]
pruneopts = "NUT"
revision = "9e67c67572bc5dd02aef930e2b0ae3c02a4b5a5c"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:55c0cfaa11248731dfb2070a4a5b81330117a6f3bc288bfedd9feff9946b907a"
name = "github.com/opencontainers/go-digest"
packages = ["."]
pruneopts = "NUT"
revision = "c9281466c8b2f606084ac71339773efd177436e7"
[[projects]]
digest = "1:11db38d694c130c800d0aefb502fb02519e514dc53d9804ce51d1ad25ec27db6"
name = "github.com/opencontainers/image-spec"
packages = [
"specs-go",
"specs-go/v1",
]
pruneopts = "NUT"
revision = "d60099175f88c47cd379c4738d158884749ed235"
version = "v1.0.1"
[[projects]]
digest = "1:918dbd46ad099418ce9760291bb156ebf23d441aad4cb682dd9bf09e2d3c0c7b"
name = "github.com/opencontainers/runc"
packages = ["libcontainer/user"]
pruneopts = "NUT"
revision = "baf6536d6259209c3edfa2b22237af82942d3dfa"
version = "v0.1.1"
[[projects]]
digest = "1:014d3763649dd2f071fcf4acb03f3a2964c0c6dcf5581f4585fdd0d492235518"
name = "github.com/peterhellberg/link"
packages = ["."]
pruneopts = "NUT"
revision = "d1cebc7ea14a5fc0de7cb4a45acae773161642c6"
version = "v1.0.0"
[[projects]]
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "NUT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
branch = "master"
digest = "1:cfa8aa49949ef9e612512736234abd8023cffd7bc76865afcfeec9846b246daf"
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/internal",
"prometheus/promhttp",
]
pruneopts = "NUT"
revision = "e637cec7d9c8990247098639ebc6d43dd34ddd49"
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "NUT"
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
[[projects]]
branch = "master"
digest = "1:fad5a35eea6a1a33d6c8f949fbc146f24275ca809ece854248187683f52cc30b"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model",
]
pruneopts = "NUT"
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
[[projects]]
branch = "master"
digest = "1:6621142cd60b7150ab66f38ff36303ca55843dc5a635c1f9a28f95ecddab72b4"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs",
]
pruneopts = "NUT"
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
[[projects]]
branch = "master"
digest = "1:25eadade1fa09cbada860b0f17e559a40b97feea133ff73d8d6451be5df6d9a4"
name = "github.com/shurcooL/httpfs"
packages = [
"html/vfstemplate",
"path/vfspath",
"vfsutil",
]
pruneopts = "NUT"
revision = "809beceb23714880abc4a382a00c05f89d13b1cc"
[[projects]]
branch = "master"
digest = "1:43c44ab8b001881225afe48c6aedcf2dd4f93495efd4586d1649c9dc2c7c7eaa"
name = "github.com/shurcooL/vfsgen"
packages = ["."]
pruneopts = "NUT"
revision = "33ae1944be3fe078a54c597f107e0867da19c713"
[[projects]]
digest = "1:b2339e83ce9b5c4f79405f949429a7f68a9a904fed903c672aac1e7ceb7f5f02"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "NUT"
revision = "3e01752db0189b9157070a0e1668a620f9a85da2"
version = "v1.0.6"
[[projects]]
branch = "master"
digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
pruneopts = "NUT"
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c"
[[projects]]
branch = "master"
digest = "1:8f96a3abd7277a1f433c5b7036c00f21188fd6a36ff1e8c9c1681ab14476e192"
name = "golang.org/x/net"
packages = [
"context",
"context/ctxhttp",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/socks",
"internal/timeseries",
"proxy",
"trace",
]
pruneopts = "NUT"
revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2"
[[projects]]
branch = "master"
digest = "1:581828cee9f0d31195c1ae52ea8935dfaec395cee6c23adc02109b892b391c2e"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "NUT"
revision = "d0be0721c37eeb5299f245a996a483160fc36940"
[[projects]]
digest = "1:e7071ed636b5422cc51c0e3a6cebc229d6c9fffc528814b519a980641422d619"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = "NUT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:42448f154e7d40907ae03cbdf99dbcfa3feb927c4521e0f01c52c6142e4891d5"
name = "google.golang.org/genproto"
packages = [
"googleapis/api/annotations",
"googleapis/rpc/status",
]
pruneopts = "NUT"
revision = "4b56f30a1fd96a133a036b62cdd2a249883dd89b"
[[projects]]
digest = "1:5b805b8e03b29399b344655cac16873f026e54dc0a7c17b381f6f4d4c7b6d741"
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"balancer/base",
"balancer/roundrobin",
"codes",
"connectivity",
"credentials",
"encoding",
"encoding/proto",
"grpclog",
"internal",
"internal/backoff",
"internal/channelz",
"internal/envconfig",
"internal/grpcrand",
"internal/transport",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"resolver/dns",
"resolver/passthrough",
"stats",
"status",
"tap",
]
pruneopts = "NUT"
revision = "8dea3dc473e90c8179e519d91302d0597c0ca1d1"
version = "v1.15.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/coreos/clair/api/v3/clairpb",
"github.com/docker/distribution",
"github.com/docker/distribution/manifest/manifestlist",
"github.com/docker/distribution/manifest/schema1",
"github.com/docker/distribution/manifest/schema2",
"github.com/docker/distribution/reference",
"github.com/docker/docker-ce/components/cli/cli/config",
"github.com/docker/docker/api/types",
"github.com/docker/docker/api/types/container",
"github.com/docker/docker/client",
"github.com/docker/docker/pkg/jsonmessage",
"github.com/docker/docker/pkg/term",
"github.com/genuinetools/pkg/cli",
"github.com/google/go-cmp/cmp",
"github.com/gorilla/mux",
"github.com/mitchellh/go-wordwrap",
"github.com/opencontainers/go-digest",
"github.com/peterhellberg/link",
"github.com/shurcooL/httpfs/html/vfstemplate",
"github.com/shurcooL/vfsgen",
"github.com/sirupsen/logrus",
"google.golang.org/grpc",
]
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,73 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[prune]
non-go = true
go-tests = true
unused-packages = true
[[override]]
name = "github.com/Sirupsen/logrus"
source = "github.com/sirupsen/logrus"
[[constraint]]
name = "github.com/docker/distribution"
branch = "master"
[[constraint]]
name = "github.com/docker/docker"
branch = "master"
[[constraint]]
name = "github.com/docker/docker-ce"
branch = "master"
[[override]]
name = "github.com/docker/cli"
branch = "master"
[[override]]
name = "github.com/docker/docker-credentials-helpers"
branch = "master"
[[override]]
name = "github.com/docker/go-connections"
branch = "master"
[[override]]
name = "github.com/docker/go-units"
branch = "master"
[[constraint]]
name = "github.com/opencontainers/go-digest"
branch = "master"
[[override]]
name = "github.com/prometheus/client_golang"
branch = "master"
[[constraint]]
name = "github.com/shurcooL/vfsgen"
branch = "master"
[[constraint]]
name = "github.com/shurcooL/httpfs"
branch = "master"

View file

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2018 The Genuinetools Authors
Copyright (c) 2020 The Genuinetools Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

184
Makefile
View file

@ -1,174 +1,26 @@
# Set an output prefix, which is the local directory if not specified
PREFIX?=$(shell pwd)
# Setup name variables for the package/tool
NAME := reg
PKG := github.com/genuinetools/$(NAME)
# Set any default go build tags
CGO_ENABLED := 0
# Set any default go build tags.
BUILDTAGS :=
# Set the build dir, where built cross-compiled binaries will be output
BUILDDIR := ${PREFIX}/cross
include basic.mk
# Populate version variables
# Add to compile time flags
VERSION := $(shell cat VERSION.txt)
GITCOMMIT := $(shell git rev-parse --short HEAD)
GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no)
ifneq ($(GITUNTRACKEDCHANGES),)
GITCOMMIT := $(GITCOMMIT)-dirty
endif
CTIMEVAR=-X $(PKG)/version.GITCOMMIT=$(GITCOMMIT) -X $(PKG)/version.VERSION=$(VERSION)
GO_LDFLAGS=-ldflags "-w $(CTIMEVAR)"
GO_LDFLAGS_STATIC=-ldflags "-w $(CTIMEVAR) -extldflags -static"
# Set our default go compiler
GO := go
# List the GOOS and GOARCH to build
GOOSARCHES = $(shell cat .goosarch)
.PHONY: build
build: $(NAME) ## Builds a dynamic executable or package
$(NAME): $(wildcard *.go) $(wildcard */*.go) VERSION.txt
@echo "+ $@"
$(GO) build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o $(NAME) .
.PHONY: static
static: ## Builds a static executable
@echo "+ $@"
CGO_ENABLED=0 $(GO) build \
-tags "$(BUILDTAGS) static_build" \
${GO_LDFLAGS_STATIC} -o $(NAME) .
all: clean build fmt lint test staticcheck vet install ## Runs a clean, build, fmt, lint, test, staticcheck, vet and install
.PHONY: fmt
fmt: ## Verifies all files have been `gofmt`ed
@echo "+ $@"
@gofmt -s -l . | grep -v '.pb.go:' | grep -v vendor | tee /dev/stderr
.PHONY: lint
lint: ## Verifies `golint` passes
@echo "+ $@"
@golint ./... | grep -v '.pb.go:' | grep -v vendor | grep -v internal | tee /dev/stderr
.PHONY: test
test: ## Runs the go tests
@echo "+ $@"
@$(GO) test -v -tags "$(BUILDTAGS) cgo" $(shell $(GO) list ./... | grep -v vendor)
.PHONY: vet
vet: ## Verifies `go vet` passes
@echo "+ $@"
@$(GO) vet $(shell $(GO) list ./... | grep -v vendor) | grep -v '.pb.go:' | tee /dev/stderr
.PHONY: staticcheck
staticcheck: ## Verifies `staticcheck` passes
@echo "+ $@"
@staticcheck $(shell $(GO) list ./... | grep -v vendor) | grep -v '.pb.go:' | tee /dev/stderr
.PHONY: cover
cover: ## Runs go test with coverage
@echo "" > coverage.txt
@for d in $(shell $(GO) list ./... | grep -v vendor); do \
$(GO) test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
if [ -f profile.out ]; then \
cat profile.out >> coverage.txt; \
rm profile.out; \
fi; \
done;
.PHONY: install
install: ## Installs the executable or package
@echo "+ $@"
$(GO) install -a -tags "$(BUILDTAGS)" ${GO_LDFLAGS} .
define buildpretty
mkdir -p $(BUILDDIR)/$(1)/$(2);
GOOS=$(1) GOARCH=$(2) CGO_ENABLED=0 $(GO) build \
-o $(BUILDDIR)/$(1)/$(2)/$(NAME) \
-a -tags "$(BUILDTAGS) static_build netgo" \
-installsuffix netgo ${GO_LDFLAGS_STATIC} .;
md5sum $(BUILDDIR)/$(1)/$(2)/$(NAME) > $(BUILDDIR)/$(1)/$(2)/$(NAME).md5;
sha256sum $(BUILDDIR)/$(1)/$(2)/$(NAME) > $(BUILDDIR)/$(1)/$(2)/$(NAME).sha256;
endef
.PHONY: cross
cross: *.go VERSION.txt ## Builds the cross-compiled binaries, creating a clean directory structure (eg. GOOS/GOARCH/binary)
@echo "+ $@"
$(foreach GOOSARCH,$(GOOSARCHES), $(call buildpretty,$(subst /,,$(dir $(GOOSARCH))),$(notdir $(GOOSARCH))))
define buildrelease
GOOS=$(1) GOARCH=$(2) CGO_ENABLED=0 $(GO) build \
-o $(BUILDDIR)/$(NAME)-$(1)-$(2) \
-a -tags "$(BUILDTAGS) static_build netgo" \
-installsuffix netgo ${GO_LDFLAGS_STATIC} .;
md5sum $(BUILDDIR)/$(NAME)-$(1)-$(2) > $(BUILDDIR)/$(NAME)-$(1)-$(2).md5;
sha256sum $(BUILDDIR)/$(NAME)-$(1)-$(2) > $(BUILDDIR)/$(NAME)-$(1)-$(2).sha256;
endef
.PHONY: release
release: *.go VERSION.txt ## Builds the cross-compiled binaries, naming them in such a way for release (eg. binary-GOOS-GOARCH)
@echo "+ $@"
$(foreach GOOSARCH,$(GOOSARCHES), $(call buildrelease,$(subst /,,$(dir $(GOOSARCH))),$(notdir $(GOOSARCH))))
.PHONY: bump-version
BUMP := patch
bump-version: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ]
@$(GO) get -u github.com/jessfraz/junk/sembump # update sembump tool
$(eval NEW_VERSION = $(shell sembump --kind $(BUMP) $(VERSION)))
@echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)"
echo $(NEW_VERSION) > VERSION.txt
@echo "Updating links to download binaries in README.md"
sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md
git add VERSION.txt README.md
git commit -vsam "Bump version to $(NEW_VERSION)"
@echo "Run make tag to create and push the tag for new version $(NEW_VERSION)"
.PHONY: tag
tag: ## Create a new git tag to prepare to build a release
git tag -sa $(VERSION) -m "$(VERSION)"
@echo "Run git push origin $(VERSION) to push your new tag to GitHub and trigger a travis build."
.PHONY: AUTHORS
AUTHORS:
@$(file >$@,# This file lists all individuals having contributed content to the repository.)
@$(file >>$@,# For how it is generated, see `make AUTHORS`.)
@echo "$(shell git log --format='\n%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf)" >> $@
.PHONY: clean
clean: ## Cleanup any build binaries or packages
@echo "+ $@"
$(RM) $(NAME)
$(RM) -r $(BUILDDIR)
sudo $(RM) -r $(CURDIR)/.certs
# set the graph driver as the current graphdriver if not set
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
export DOCKER_GRAPHDRIVER
# if this session isn't interactive, then we don't want to allocate a
# TTY, which would fail, but if it is interactive, we do want to attach
# so that the user can send e.g. ^C through.
INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0)
ifeq ($(INTERACTIVE), 1)
DOCKER_FLAGS += -t
endif
.PHONY: prebuild
prebuild:
.PHONY: dind
DIND_CONTAINER=reg-dind
DIND_DOCKER_IMAGE=r.j3ss.co/docker:userns
dind: stop-dind ## Starts a docker-in-docker container for running the tests with
dind: stop-dind ## Starts a docker-in-docker container for running the tests with.
docker run -d \
--name $(DIND_CONTAINER) \
--name $(NAME)-dind \
--privileged \
-v $(CURDIR)/.certs:/etc/docker/ssl \
-v $(CURDIR):/go/src/github.com/genuinetools/reg \
-v /tmp:/tmp \
$(DIND_DOCKER_IMAGE) \
$(REGISTRY)/docker:userns \
dockerd -D --storage-driver $(DOCKER_GRAPHDRIVER) \
-H tcp://127.0.0.1:2375 \
--host=unix:///var/run/docker.sock \
@ -180,33 +32,27 @@ dind: stop-dind ## Starts a docker-in-docker container for running the tests wit
--tlscert=/etc/docker/ssl/server.cert
.PHONY: stop-dind
stop-dind: ## Stops the docker-in-docker container
@docker rm -f $(DIND_CONTAINER) >/dev/null 2>&1 || true
stop-dind: ## Stops the docker-in-docker container.
@docker rm -f $(NAME)-dind >/dev/null 2>&1 || true
.PHONY: dtest
DOCKER_IMAGE := reg-dev
dtest: ## Run the tests in a docker container
docker build --rm --force-rm -f Dockerfile.dev -t $(DOCKER_IMAGE) .
dtest: image-dev ## Run the tests in a docker container.
docker run --rm -i $(DOCKER_FLAGS) \
-v $(CURDIR):/go/src/github.com/genuinetools/reg \
--workdir /go/src/github.com/genuinetools/reg \
-v $(CURDIR)/.certs:/etc/docker/ssl:ro \
-v /tmp:/tmp \
--disable-content-trust=true \
--net container:$(DIND_CONTAINER) \
--net container:$(NAME)-dind \
-e DOCKER_HOST=tcp://127.0.0.1:2375 \
-e DOCKER_TLS_VERIFY=true \
-e DOCKER_CERT_PATH=/etc/docker/ssl \
-e DOCKER_API_VERSION \
$(DOCKER_IMAGE) \
$(REGISTRY)/$(NAME):dev \
make test
.PHONY: snakeoil
snakeoil: ## Update snakeoil certs for testing
snakeoil: ## Update snakeoil certs for testing.
go run /usr/local/go/src/crypto/tls/generate_cert.go --host localhost,127.0.0.1 --ca
mv $(CURDIR)/key.pem $(CURDIR)/testutils/snakeoil/key.pem
mv $(CURDIR)/cert.pem $(CURDIR)/testutils/snakeoil/cert.pem
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

View file

@ -1,25 +1,33 @@
# reg
[![Travis CI](https://img.shields.io/travis/genuinetools/reg.svg?style=for-the-badge)](https://travis-ci.org/genuinetools/reg)
[![make-all](https://github.com/genuinetools/reg/workflows/make%20all/badge.svg)](https://github.com/genuinetools/reg/actions?query=workflow%3A%22make+all%22)
[![make-image](https://github.com/genuinetools/reg/workflows/make%20image/badge.svg)](https://github.com/genuinetools/reg/actions?query=workflow%3A%22make+image%22)
[![make-test](https://github.com/genuinetools/reg/workflows/make%20test/badge.svg)](https://github.com/genuinetools/reg/actions?query=workflow%3A%22make+test%22)
[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://godoc.org/github.com/genuinetools/reg)
[![Github All Releases](https://img.shields.io/github/downloads/genuinetools/reg/total.svg?style=for-the-badge)](https://github.com/genuinetools/reg/releases)
Docker registry v2 command line client and repo listing generator with security checks.
* [Installation](README.md#installation)
* [Binaries](README.md#binaries)
* [Via Go](README.md#via-go)
* [Usage](README.md#usage)
* [Auth](README.md#auth)
* [List Repositories and Tags](README.md#list-repositories-and-tags)
* [Get a Manifest](README.md#get-a-manifest)
* [Get the Digest](README.md#get-the-digest)
* [Download a Layer](README.md#download-a-layer)
* [Delete an Image](README.md#delete-an-image)
* [Vulnerability Reports](README.md#vulnerability-reports)
* [Running a Static UI Server for a Registry](README.md#running-a-static-ui-server-for-a-registry)
* [Using Self-Signed Certs with a Registry](README.md#using-self-signed-certs-with-a-registry)
* [Contributing](README.md#contributing)
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**
- [Installation](#installation)
- [Binaries](#binaries)
- [Via Go](#via-go)
- [Usage](#usage)
- [Auth](#auth)
- [List Repositories and Tags](#list-repositories-and-tags)
- [Get a Manifest](#get-a-manifest)
- [Get the Digest](#get-the-digest)
- [Download a Layer](#download-a-layer)
- [Delete an Image](#delete-an-image)
- [Vulnerability Reports](#vulnerability-reports)
- [Generating Static Website for a Registry](#generating-static-website-for-a-registry)
- [Using Self-Signed Certs with a Registry](#using-self-signed-certs-with-a-registry)
- [Contributing](#contributing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Installation
@ -43,6 +51,7 @@ Usage: reg <command>
Flags:
--auth-url alternate URL for registry authentication (ex. auth.docker.io) (default: <none>)
-d enable debug logging (default: false)
-f, --force-non-ssl force allow use of non-ssl (default: false)
-k, --insecure do not verify tls certificates (default: false)

View file

@ -1 +1 @@
v0.15.7
v0.16.1

196
basic.mk Normal file
View file

@ -0,0 +1,196 @@
# Set the shell
SHELL := /bin/bash
# Set an output prefix, which is the local directory if not specified
PREFIX?=$(shell pwd)
# Set the build dir, where built cross-compiled binaries will be output
BUILDDIR := ${PREFIX}/cross
# Populate version variables
# Add to compile time flags
VERSION := $(shell cat VERSION.txt)
GITCOMMIT := $(shell git rev-parse --short HEAD)
GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no)
ifneq ($(GITUNTRACKEDCHANGES),)
GITCOMMIT := $(GITCOMMIT)-dirty
endif
ifeq ($(GITCOMMIT),)
GITCOMMIT := ${GITHUB_SHA}
endif
CTIMEVAR=-X $(PKG)/version.GITCOMMIT=$(GITCOMMIT) -X $(PKG)/version.VERSION=$(VERSION)
GO_LDFLAGS=-ldflags "-w $(CTIMEVAR)"
GO_LDFLAGS_STATIC=-ldflags "-w $(CTIMEVAR) -extldflags -static"
# Set our default go compiler
GO := go
# List the GOOS and GOARCH to build
GOOSARCHES = $(shell cat .goosarch)
# Set the graph driver as the current graphdriver if not set.
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
export DOCKER_GRAPHDRIVER
# If this session isn't interactive, then we don't want to allocate a
# TTY, which would fail, but if it is interactive, we do want to attach
# so that the user can send e.g. ^C through.
INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0)
ifeq ($(INTERACTIVE), 1)
DOCKER_FLAGS += -t
endif
.PHONY: build
build: prebuild $(NAME) ## Builds a dynamic executable or package.
$(NAME): $(wildcard *.go) $(wildcard */*.go) VERSION.txt
@echo "+ $@"
$(GO) build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o $(NAME) .
.PHONY: static
static: prebuild ## Builds a static executable.
@echo "+ $@"
CGO_ENABLED=$(CGO_ENABLED) $(GO) build \
-tags "$(BUILDTAGS) static_build" \
${GO_LDFLAGS_STATIC} -o $(NAME) .
all: clean build fmt lint test staticcheck vet install ## Runs a clean, build, fmt, lint, test, staticcheck, vet and install.
.PHONY: fmt
fmt: ## Verifies all files have been `gofmt`ed.
@echo "+ $@"
@if [[ ! -z "$(shell gofmt -s -l . | grep -v '.pb.go:' | grep -v '.twirp.go:' | grep -v vendor | tee /dev/stderr)" ]]; then \
exit 1; \
fi
.PHONY: lint
lint: ## Verifies `golint` passes.
@echo "+ $@"
@if [[ ! -z "$(shell golint ./... | grep -v '.pb.go:' | grep -v '.twirp.go:' | grep -v vendor | tee /dev/stderr)" ]]; then \
exit 1; \
fi
.PHONY: test
test: prebuild ## Runs the go tests.
@echo "+ $@"
@$(GO) test -v -tags "$(BUILDTAGS) cgo" $(shell $(GO) list ./... | grep -v vendor)
.PHONY: vet
vet: ## Verifies `go vet` passes.
@echo "+ $@"
@if [[ ! -z "$(shell $(GO) vet $(shell $(GO) list ./... | grep -v vendor) | tee /dev/stderr)" ]]; then \
exit 1; \
fi
.PHONY: staticcheck
staticcheck: ## Verifies `staticcheck` passes.
@echo "+ $@"
@if [[ ! -z "$(shell staticcheck $(shell $(GO) list ./... | grep -v vendor) | tee /dev/stderr)" ]]; then \
exit 1; \
fi
.PHONY: cover
cover: prebuild ## Runs go test with coverage.
@echo "" > coverage.txt
@for d in $(shell $(GO) list ./... | grep -v vendor); do \
$(GO) test -race -coverprofile=profile.out -covermode=atomic "$$d"; \
if [ -f profile.out ]; then \
cat profile.out >> coverage.txt; \
rm profile.out; \
fi; \
done;
.PHONY: install
install: prebuild ## Installs the executable or package.
@echo "+ $@"
$(GO) install -a -tags "$(BUILDTAGS)" ${GO_LDFLAGS} .
define buildpretty
mkdir -p $(BUILDDIR)/$(1)/$(2);
GOOS=$(1) GOARCH=$(2) CGO_ENABLED=$(CGO_ENABLED) $(GO) build \
-o $(BUILDDIR)/$(1)/$(2)/$(NAME) \
-a -tags "$(BUILDTAGS) static_build netgo" \
-installsuffix netgo ${GO_LDFLAGS_STATIC} .;
md5sum $(BUILDDIR)/$(1)/$(2)/$(NAME) > $(BUILDDIR)/$(1)/$(2)/$(NAME).md5;
sha256sum $(BUILDDIR)/$(1)/$(2)/$(NAME) > $(BUILDDIR)/$(1)/$(2)/$(NAME).sha256;
endef
.PHONY: cross
cross: *.go VERSION.txt prebuild ## Builds the cross-compiled binaries, creating a clean directory structure (eg. GOOS/GOARCH/binary).
@echo "+ $@"
$(foreach GOOSARCH,$(GOOSARCHES), $(call buildpretty,$(subst /,,$(dir $(GOOSARCH))),$(notdir $(GOOSARCH))))
define buildrelease
GOOS=$(1) GOARCH=$(2) CGO_ENABLED=$(CGO_ENABLED) $(GO) build \
-o $(BUILDDIR)/$(NAME)-$(1)-$(2) \
-a -tags "$(BUILDTAGS) static_build netgo" \
-installsuffix netgo ${GO_LDFLAGS_STATIC} .;
md5sum $(BUILDDIR)/$(NAME)-$(1)-$(2) > $(BUILDDIR)/$(NAME)-$(1)-$(2).md5;
sha256sum $(BUILDDIR)/$(NAME)-$(1)-$(2) > $(BUILDDIR)/$(NAME)-$(1)-$(2).sha256;
endef
.PHONY: release
release: *.go VERSION.txt prebuild ## Builds the cross-compiled binaries, naming them in such a way for release (eg. binary-GOOS-GOARCH).
@echo "+ $@"
$(foreach GOOSARCH,$(GOOSARCHES), $(call buildrelease,$(subst /,,$(dir $(GOOSARCH))),$(notdir $(GOOSARCH))))
.PHONY: bump-version
BUMP := patch
bump-version: ## Bump the version in the version file. Set BUMP to [ patch | major | minor ].
@$(GO) get -u github.com/jessfraz/junk/sembump || true # update sembump tool
$(eval NEW_VERSION = $(shell sembump --kind $(BUMP) $(VERSION)))
@echo "Bumping VERSION.txt from $(VERSION) to $(NEW_VERSION)"
echo $(NEW_VERSION) > VERSION.txt
@echo "Updating links to download binaries in README.md"
sed -i s/$(VERSION)/$(NEW_VERSION)/g README.md
git add VERSION.txt README.md
git commit -vsam "Bump version to $(NEW_VERSION)"
@echo "Run make tag to create and push the tag for new version $(NEW_VERSION)"
.PHONY: tag
tag: ## Create a new git tag to prepare to build a release.
git tag -sa $(VERSION) -m "$(VERSION)"
@echo "Run git push origin $(VERSION) to push your new tag to GitHub and trigger a release."
REGISTRY := r.j3ss.co
.PHONY: image
image: ## Create the docker image from the Dockerfile.
@docker build --rm --force-rm -t $(REGISTRY)/$(NAME) .
.PHONY: image-dev
image-dev:
@docker build --rm --force-rm -f Dockerfile.dev -t $(REGISTRY)/$(NAME):dev .
.PHONY: AUTHORS
AUTHORS:
@$(file >$@,# This file lists all individuals having contributed content to the repository.)
@$(file >>$@,# For how it is generated, see `make AUTHORS`.)
@echo "$(shell git log --format='\n%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf)" >> $@
.PHONY: vendor
vendor: ## Updates the vendoring directory.
@$(RM) go.sum
@$(RM) -r vendor
GO111MODULE=on $(GO) mod init || true
GO111MODULE=on $(GO) mod tidy
GO111MODULE=on $(GO) mod vendor
@$(RM) Gopkg.toml Gopkg.lock
.PHONY: clean
clean: ## Cleanup any build binaries or packages.
@echo "+ $@"
$(RM) $(NAME)
$(RM) -r $(BUILDDIR)
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | sed 's/^[^:]*://g' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
check_defined = \
$(strip $(foreach 1,$1, \
$(call __check_defined,$1,$(strip $(value 2)))))
__check_defined = \
$(if $(value $1),, \
$(error Undefined $1$(if $2, ($2))$(if $(value @), \
required by target `$@')))

View file

@ -13,7 +13,7 @@ var (
)
// GetAncestry displays an ancestry and all of its features and vulnerabilities.
func (c *Clair) GetAncestry(name string) (*clairpb.GetAncestryResponse_Ancestry, error) {
func (c *Clair) GetAncestry(ctx context.Context, name string) (*clairpb.GetAncestryResponse_Ancestry, error) {
c.Logf("clair.ancestry.get name=%s", name)
if c.grpcConn == nil {
@ -22,7 +22,7 @@ func (c *Clair) GetAncestry(name string) (*clairpb.GetAncestryResponse_Ancestry,
client := clairpb.NewAncestryServiceClient(c.grpcConn)
resp, err := client.GetAncestry(context.Background(), &clairpb.GetAncestryRequest{
resp, err := client.GetAncestry(ctx, &clairpb.GetAncestryRequest{
AncestryName: name,
})
if err != nil {
@ -41,7 +41,7 @@ func (c *Clair) GetAncestry(name string) (*clairpb.GetAncestryResponse_Ancestry,
}
// PostAncestry performs the analysis of all layers from the provided path.
func (c *Clair) PostAncestry(name string, layers []*clairpb.PostAncestryRequest_PostLayer) error {
func (c *Clair) PostAncestry(ctx context.Context, name string, layers []*clairpb.PostAncestryRequest_PostLayer) error {
c.Logf("clair.ancestry.post name=%s", name)
if c.grpcConn == nil {
@ -50,7 +50,7 @@ func (c *Clair) PostAncestry(name string, layers []*clairpb.PostAncestryRequest_
client := clairpb.NewAncestryServiceClient(c.grpcConn)
resp, err := client.PostAncestry(context.Background(), &clairpb.PostAncestryRequest{
resp, err := client.PostAncestry(ctx, &clairpb.PostAncestryRequest{
AncestryName: name,
Layers: layers,
Format: "Docker",

View file

@ -1,6 +1,7 @@
package clair
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
@ -11,7 +12,7 @@ import (
"google.golang.org/grpc"
)
// Clair defines the client for retriving information from the clair API.
// Clair defines the client for retrieving information from the clair API.
type Clair struct {
URL string
Client *http.Client
@ -81,15 +82,24 @@ func New(url string, opt Opt) (*Clair, error) {
return registry, nil
}
// url returns a clair URL with the passed arguements concatenated.
// Close closes the gRPC connection
func (c *Clair) Close() error {
return c.grpcConn.Close()
}
// url returns a clair URL with the passed arguments concatenated.
func (c *Clair) url(pathTemplate string, args ...interface{}) string {
pathSuffix := fmt.Sprintf(pathTemplate, args...)
url := fmt.Sprintf("%s%s", c.URL, pathSuffix)
return url
}
func (c *Clair) getJSON(url string, response interface{}) (http.Header, error) {
resp, err := c.Client.Get(url)
func (c *Clair) getJSON(ctx context.Context, url string, response interface{}) (http.Header, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := c.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}

View file

@ -2,18 +2,19 @@ package clair
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
)
// GetLayer displays a Layer and optionally all of its features and vulnerabilities.
func (c *Clair) GetLayer(name string, features, vulnerabilities bool) (*Layer, error) {
func (c *Clair) GetLayer(ctx context.Context, name string, features, vulnerabilities bool) (*Layer, error) {
url := c.url("/v1/layers/%s?features=%t&vulnerabilities=%t", name, features, vulnerabilities)
c.Logf("clair.layers.get url=%s name=%s", url, name)
var respLayer layerEnvelope
if _, err := c.getJSON(url, &respLayer); err != nil {
if _, err := c.getJSON(ctx, url, &respLayer); err != nil {
return nil, err
}
@ -25,7 +26,7 @@ func (c *Clair) GetLayer(name string, features, vulnerabilities bool) (*Layer, e
}
// PostLayer performs the analysis of a Layer from the provided path.
func (c *Clair) PostLayer(layer *Layer) (*Layer, error) {
func (c *Clair) PostLayer(ctx context.Context, layer *Layer) (*Layer, error) {
url := c.url("/v1/layers")
c.Logf("clair.layers.post url=%s name=%s", url, layer.Name)
@ -36,7 +37,14 @@ func (c *Clair) PostLayer(layer *Layer) (*Layer, error) {
c.Logf("clair.layers.post req.Body=%s", string(b))
resp, err := c.Client.Post(url, "application/json", bytes.NewReader(b))
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
@ -57,7 +65,7 @@ func (c *Clair) PostLayer(layer *Layer) (*Layer, error) {
}
// DeleteLayer removes a layer reference from clair.
func (c *Clair) DeleteLayer(name string) error {
func (c *Clair) DeleteLayer(ctx context.Context, name string) error {
url := c.url("/v1/layers/%s", name)
c.Logf("clair.layers.delete url=%s name=%s", url, name)
@ -66,7 +74,7 @@ func (c *Clair) DeleteLayer(name string) error {
return err
}
resp, err := c.Client.Do(req)
resp, err := c.Client.Do(req.WithContext(ctx))
if err != nil {
return err
}
@ -77,5 +85,5 @@ func (c *Clair) DeleteLayer(name string) error {
return nil
}
return fmt.Errorf("Got status code: %d", resp.StatusCode)
return fmt.Errorf("got status code: %d", resp.StatusCode)
}

View file

@ -1,6 +1,7 @@
package clair
import (
"context"
"fmt"
"strings"
@ -10,7 +11,7 @@ import (
)
// NewClairLayer will form a layer struct required for a clair scan.
func (c *Clair) NewClairLayer(r *registry.Registry, image string, fsLayers map[int]distribution.Descriptor, index int) (*Layer, error) {
func (c *Clair) NewClairLayer(ctx context.Context, r *registry.Registry, image string, fsLayers map[int]distribution.Descriptor, index int) (*Layer, error) {
var parentName string
if index < len(fsLayers)-1 {
parentName = fsLayers[index+1].Digest.String()
@ -20,7 +21,7 @@ func (c *Clair) NewClairLayer(r *registry.Registry, image string, fsLayers map[i
p := strings.Join([]string{r.URL, "v2", image, "blobs", fsLayers[index].Digest.String()}, "/")
// Get the headers.
h, err := r.Headers(p)
h, err := r.Headers(ctx, p)
if err != nil {
return nil, err
}
@ -35,12 +36,12 @@ func (c *Clair) NewClairLayer(r *registry.Registry, image string, fsLayers map[i
}
// NewClairV3Layer will form a layer struct required for a clair scan.
func (c *Clair) NewClairV3Layer(r *registry.Registry, image string, fsLayer distribution.Descriptor) (*clairpb.PostAncestryRequest_PostLayer, error) {
func (c *Clair) NewClairV3Layer(ctx context.Context, r *registry.Registry, image string, fsLayer distribution.Descriptor) (*clairpb.PostAncestryRequest_PostLayer, error) {
// Form the path.
p := strings.Join([]string{r.URL, "v2", image, "blobs", fsLayer.Digest.String()}, "/")
// Get the headers.
h, err := r.Headers(p)
h, err := r.Headers(ctx, p)
if err != nil {
return nil, err
}
@ -52,10 +53,10 @@ func (c *Clair) NewClairV3Layer(r *registry.Registry, image string, fsLayer dist
}, nil
}
func (c *Clair) getLayers(r *registry.Registry, repo, tag string, filterEmpty bool) (map[int]distribution.Descriptor, error) {
func (c *Clair) getLayers(ctx context.Context, r *registry.Registry, repo, tag string, filterEmpty bool) (map[int]distribution.Descriptor, string, error) {
ok := true
// Get the manifest to pass to clair.
mf, err := r.ManifestV2(repo, tag)
mf, err := r.ManifestV2(ctx, repo, tag)
if err != nil {
ok = false
c.Logf("couldn't retrieve manifest v2, falling back to v1")
@ -73,12 +74,12 @@ func (c *Clair) getLayers(r *registry.Registry, repo, tag string, filterEmpty bo
}
}
return filteredLayers, nil
return filteredLayers, mf.Config.Digest.String(), nil
}
m, err := r.ManifestV1(repo, tag)
m, err := r.ManifestV1(ctx, repo, tag)
if err != nil {
return nil, fmt.Errorf("getting the v1 manifest for %s:%s failed: %v", repo, tag, err)
return nil, "", fmt.Errorf("getting the v1 manifest for %s:%s failed: %v", repo, tag, err)
}
for i := 0; i < len(m.FSLayers); i++ {
@ -91,5 +92,5 @@ func (c *Clair) getLayers(r *registry.Registry, repo, tag string, filterEmpty bo
}
}
return filteredLayers, nil
return filteredLayers, filteredLayers[0].Digest.String(), nil
}

View file

@ -1,6 +1,7 @@
package clair
import (
"context"
"errors"
"fmt"
"time"
@ -10,7 +11,7 @@ import (
)
// Vulnerabilities scans the given repo and tag.
func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
func (c *Clair) Vulnerabilities(ctx context.Context, r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
report := VulnerabilityReport{
RegistryURL: r.Domain,
Repo: repo,
@ -19,7 +20,7 @@ func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (Vulnera
VulnsBySeverity: make(map[string][]Vulnerability),
}
filteredLayers, err := c.getLayers(r, repo, tag, true)
filteredLayers, _, err := c.getLayers(ctx, r, repo, tag, true)
if err != nil {
return report, fmt.Errorf("getting filtered layers failed: %v", err)
}
@ -31,20 +32,20 @@ func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (Vulnera
for i := len(filteredLayers) - 1; i >= 0; i-- {
// Form the clair layer.
l, err := c.NewClairLayer(r, repo, filteredLayers, i)
l, err := c.NewClairLayer(ctx, r, repo, filteredLayers, i)
if err != nil {
return report, err
}
// Post the layer.
if _, err := c.PostLayer(l); err != nil {
if _, err := c.PostLayer(ctx, l); err != nil {
return report, err
}
}
report.Name = filteredLayers[0].Digest.String()
vl, err := c.GetLayer(filteredLayers[0].Digest.String(), true, true)
vl, err := c.GetLayer(ctx, filteredLayers[0].Digest.String(), true, true)
if err != nil {
return report, err
}
@ -76,7 +77,7 @@ func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (Vulnera
}
// VulnerabilitiesV3 scans the given repo and tag using the clair v3 API.
func (c *Clair) VulnerabilitiesV3(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
func (c *Clair) VulnerabilitiesV3(ctx context.Context, r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
report := VulnerabilityReport{
RegistryURL: r.Domain,
Repo: repo,
@ -85,7 +86,7 @@ func (c *Clair) VulnerabilitiesV3(r *registry.Registry, repo, tag string) (Vulne
VulnsBySeverity: make(map[string][]Vulnerability),
}
layers, err := c.getLayers(r, repo, tag, false)
layers, reportName, err := c.getLayers(ctx, r, repo, tag, false)
if err != nil {
return report, fmt.Errorf("getting filtered layers failed: %v", err)
}
@ -95,12 +96,12 @@ func (c *Clair) VulnerabilitiesV3(r *registry.Registry, repo, tag string) (Vulne
return report, nil
}
report.Name = layers[0].Digest.String()
report.Name = reportName
clairLayers := []*clairpb.PostAncestryRequest_PostLayer{}
for i := len(layers) - 1; i >= 0; i-- {
// Form the clair layer.
l, err := c.NewClairV3Layer(r, repo, layers[i])
l, err := c.NewClairV3Layer(ctx, r, repo, layers[i])
if err != nil {
return report, err
}
@ -110,12 +111,12 @@ func (c *Clair) VulnerabilitiesV3(r *registry.Registry, repo, tag string) (Vulne
}
// Post the ancestry.
if err := c.PostAncestry(layers[0].Digest.String(), clairLayers); err != nil {
if err := c.PostAncestry(ctx, reportName, clairLayers); err != nil {
return report, fmt.Errorf("posting ancestry failed: %v", err)
}
// Get the ancestry.
vl, err := c.GetAncestry(layers[0].Digest.String())
vl, err := c.GetAncestry(ctx, reportName)
if err != nil {
return report, err
}

View file

@ -31,13 +31,13 @@ func (cmd *digestCommand) Run(ctx context.Context, args []string) error {
}
// Create the registry client.
r, err := createRegistryClient(image.Domain)
r, err := createRegistryClient(ctx, image.Domain)
if err != nil {
return err
}
// Get the digest.
digest, err := r.Digest(image)
digest, err := r.Digest(ctx, image)
if err != nil {
return err
}

39
go.mod Normal file
View file

@ -0,0 +1,39 @@
module github.com/genuinetools/reg
replace github.com/Sirupsen/logrus => github.com/sirupsen/logrus v1.4.3-0.20190807103436-de736cf91b92
go 1.13
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/Microsoft/hcsshim v0.8.6 // indirect
github.com/containerd/containerd v1.2.9 // indirect
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
github.com/coreos/clair v2.0.1-0.20190910143208-94150ab1f4ac+incompatible
github.com/deckarep/golang-set v1.7.1 // indirect
github.com/docker/cli v0.0.0-20190913211141-95327f4e6241
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v1.4.2-0.20190916154449-92cc603036dd
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e // indirect
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb
github.com/gogo/protobuf v1.3.0 // indirect
github.com/google/go-cmp v0.3.1
github.com/gorilla/mux v1.7.3
github.com/grpc-ecosystem/grpc-gateway v1.11.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.0
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/peterhellberg/link v1.0.0
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
github.com/sirupsen/logrus v1.4.2
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
google.golang.org/grpc v1.23.1
gotest.tools v2.2.0+incompatible // indirect
)

124
go.sum Normal file
View file

@ -0,0 +1,124 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/containerd v1.2.9 h1:6tyNjBmAMG47QuFPIT9LgiiexoVxC6qpTGR+eD0R0Z8=
github.com/containerd/containerd v1.2.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw=
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/clair v2.0.1-0.20190910143208-94150ab1f4ac+incompatible h1:V+KI58nlBLYl1KMqBc0Ollhik0gIp2dDkPZQsg3NipQ=
github.com/coreos/clair v2.0.1-0.20190910143208-94150ab1f4ac+incompatible/go.mod h1:uXhHPWAoRqw0jJc2f8RrPCwRhIo9otQ8OEWUFtpCiwA=
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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/docker/cli v0.0.0-20190913211141-95327f4e6241 h1:btTBgRvrdoe+b7NfX/7PnUbiXzGceLCt09QZkg1bgqA=
github.com/docker/cli v0.0.0-20190913211141-95327f4e6241/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.4.2-0.20190916154449-92cc603036dd h1:kDIT0qjvLHbdL86aa+VteVpVZOR7coIyIejM/o3CwOo=
github.com/docker/docker v1.4.2-0.20190916154449-92cc603036dd/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e h1:P10tZmVD2XclAaT9l7OduMH1OLFzTa1wUuUqHZnEdI0=
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb h1:9MQ4N7zyYTtdjLGqE5McDbgjIjqR5TAPc6lytEOdndc=
github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/grpc-ecosystem/grpc-gateway v1.11.1 h1:/dBYI+n4xIL+Y9SKXQrjlKTmJJDwCSlNLRwZ5nBhIek=
github.com/grpc-ecosystem/grpc-gateway v1.11.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
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/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/peterhellberg/link v1.0.0 h1:mUWkiegowUXEcmlb+ybF75Q/8D2Y0BjZtR8cxoKhaQo=
github.com/peterhellberg/link v1.0.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -2,6 +2,7 @@ package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"html/template"
@ -43,7 +44,7 @@ type Repository struct {
VulnerabilityReport clair.VulnerabilityReport `json:"vulnerability"`
}
// A AnalysisResult holds all vulnerabilities of a scan
// An AnalysisResult holds all vulnerabilities of a scan
type AnalysisResult struct {
Repositories []Repository `json:"repositories"`
RegistryDomain string `json:"registryDomain"`
@ -53,7 +54,7 @@ type AnalysisResult struct {
UpdateInterval time.Duration
}
func (rc *registryController) repositories(staticDir string) error {
func (rc *registryController) repositories(ctx context.Context, staticDir string) error {
rc.l.Lock()
defer rc.l.Unlock()
@ -65,7 +66,7 @@ func (rc *registryController) repositories(staticDir string) error {
UpdateInterval: rc.interval,
}
repoList, err := rc.reg.Catalog("")
repoList, err := rc.reg.Catalog(ctx, "")
if err != nil {
return fmt.Errorf("getting catalog for %s failed: %v", rc.reg.Domain, err)
}
@ -94,7 +95,7 @@ func (rc *registryController) repositories(staticDir string) error {
// Parse and execute the tags templates.
// If we are generating the tags files, disable vulnerability links in the
// templates since they won't go anywhere without a server side component.
b, err := rc.generateTagsTemplate(repo, false)
b, err := rc.generateTagsTemplate(ctx, repo, false)
if err != nil {
logrus.Warnf("generating tags template for repo %q failed: %v", repo, err)
}
@ -156,7 +157,7 @@ func (rc *registryController) tagsHandler(w http.ResponseWriter, r *http.Request
}
// Generate the tags template.
b, err := rc.generateTagsTemplate(repo, rc.cl != nil)
b, err := rc.generateTagsTemplate(context.TODO(), repo, rc.cl != nil)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "tags",
@ -173,9 +174,9 @@ func (rc *registryController) tagsHandler(w http.ResponseWriter, r *http.Request
fmt.Fprint(w, string(b))
}
func (rc *registryController) generateTagsTemplate(repo string, hasVulns bool) ([]byte, error) {
func (rc *registryController) generateTagsTemplate(ctx context.Context, repo string, hasVulns bool) ([]byte, error) {
// Get the tags from the server.
tags, err := rc.reg.Tags(repo)
tags, err := rc.reg.Tags(ctx, repo)
if err != nil {
return nil, fmt.Errorf("getting tags for %s failed: %v", repo, err)
}
@ -196,7 +197,7 @@ func (rc *registryController) generateTagsTemplate(repo string, hasVulns bool) (
for _, tag := range tags {
// get the manifest
m1, err := rc.reg.ManifestV1(repo, tag)
m1, err := rc.reg.ManifestV1(ctx, repo, tag)
if err != nil {
return nil, fmt.Errorf("getting v1 manifest for %s:%s failed: %v", repo, tag, err)
}
@ -260,7 +261,7 @@ func (rc *registryController) vulnerabilitiesHandler(w http.ResponseWriter, r *h
return
}
image, err := registry.ParseImage(repo + ":" + tag)
image, err := registry.ParseImage(rc.reg.Domain + "/" + repo + ":" + tag)
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "vulnerabilities",
@ -272,10 +273,10 @@ func (rc *registryController) vulnerabilitiesHandler(w http.ResponseWriter, r *h
}
// Get the vulnerability report.
result, err := rc.cl.VulnerabilitiesV3(rc.reg, image.Path, image.Reference())
result, err := rc.cl.VulnerabilitiesV3(context.TODO(), rc.reg, image.Path, image.Reference())
if err != nil {
// Fallback to Clair v2 API.
result, err = rc.cl.Vulnerabilities(rc.reg, image.Path, image.Reference())
result, err = rc.cl.Vulnerabilities(context.TODO(), rc.reg, image.Path, image.Reference())
if err != nil {
logrus.WithFields(logrus.Fields{
"func": "vulnerabilities",

File diff suppressed because one or more lines are too long

View file

@ -19,25 +19,25 @@ var Assets = func() http.FileSystem {
fs := vfsgen۰FS{
"/": &vfsgen۰DirInfo{
name: "/",
modTime: time.Date(2018, 9, 10, 21, 40, 58, 715425786, time.UTC),
modTime: time.Date(2018, 10, 4, 17, 36, 7, 550458133, time.UTC),
},
"/repositories.html": &vfsgen۰CompressedFileInfo{
name: "repositories.html",
modTime: time.Date(2018, 9, 10, 21, 40, 58, 715425786, time.UTC),
modTime: time.Date(2018, 10, 4, 17, 36, 7, 550458133, time.UTC),
uncompressedSize: 1904,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x55\xc9\x6e\xe4\x36\x10\xbd\xf7\x57\x94\x89\x39\x8e\x44\x18\x39\x8c\xd3\xa0\x84\x04\x1e\x1f\x0c\x64\x19\x18\x31\x90\x60\x30\x07\x36\x55\x92\x68\x53\xa4\x86\x2c\xb5\xd1\x51\xf4\xef\x01\xb5\xa0\x57\x1b\xce\x21\xba\x50\x7c\xf5\xea\xb1\x16\xaa\xd4\xf7\x05\x96\xda\x22\x30\x8f\xad\x0b\x9a\x9c\xd7\x18\xd8\x30\xac\xc4\xd5\xe7\xdf\x6f\xff\xf8\xeb\xcb\x1d\xd4\xd4\x98\x7c\x25\xae\x92\xe4\xab\x2e\xc1\x10\xdc\xdf\xc1\xa7\x6f\x39\x8c\x8f\x88\x56\x50\x46\x86\x90\x31\xeb\x92\xa7\x00\x86\x12\x8d\x3f\x4e\xcb\xcd\xb4\x7c\x62\x39\x88\xab\xaf\x68\x0b\x5d\x7e\x4b\x92\xbd\xda\xa1\xd4\x3b\xd4\xde\x90\xb9\x79\x8f\xcc\x6b\xfe\x15\xcd\x12\x11\xc8\x2f\xf8\x8f\x8e\x49\x72\xec\x5c\xa3\x2c\xf2\xd5\x78\x60\x83\x24\x41\xd5\xd2\x07\xa4\x8c\x75\x54\x26\x37\x6c\x36\x6d\x64\x40\xa8\x3d\x96\x19\xe3\x0c\x0e\xf9\x35\x51\x9b\xe0\xf7\x4e\x6f\x33\xf6\x67\xf2\xf8\x73\x72\xeb\x9a\x56\x92\xde\x18\x64\xa0\x9c\x25\xb4\x94\xb1\xfb\xbb\x0c\x8b\x0a\x3f\xaa\xda\xbb\x06\xb3\xeb\x45\x97\x34\x19\xcc\xfb\x1e\xd2\x07\xac\x74\x20\xbf\xfb\xec\x1a\xa9\x2d\x0c\x83\xe0\x93\x71\x22\x1a\x6d\x9f\xc1\xa3\xc9\x98\x56\xce\x32\xa0\x5d\x8b\x19\xd3\x8d\xac\x90\x6b\xe5\xd8\x12\x5c\x20\x49\x5a\xf1\x52\x6e\x23\x2f\x8d\xa6\x33\x85\x40\x3b\x83\xa1\x46\xa4\x53\x37\x15\x02\x9f\xac\xa9\x0a\x81\x01\xcf\x57\x82\x4f\x15\x12\x1b\x57\xec\x66\xa9\xfa\xfa\xb5\x90\xeb\xeb\x99\x52\x3a\xdf\x4c\xaf\xe3\x56\xdb\xb6\x23\xb0\xb2\xc1\x8c\x95\xda\x10\xfa\x25\x85\x80\xd2\xab\x9a\xe5\x42\x2e\xbd\x52\x06\xa5\x67\xf9\xb8\x08\x2e\x67\x41\x3e\x29\x4e\x9b\x42\x6f\x17\xf6\x8b\x97\x6d\x8b\x9e\x1d\x1c\x46\x72\xb3\xd4\x6d\x8f\xf9\x63\x60\x02\xeb\xfc\x61\xf9\x62\x76\xf0\x9b\x6c\x50\x70\xaa\x2f\x13\xbf\x74\xc6\xc0\xad\x6b\x1a\x69\x8b\x73\x96\xe0\xa7\x07\xf4\x3d\x78\x69\x2b\x84\x0f\xcf\xb8\xfb\x08\x1f\xb6\xd2\x74\x08\xeb\x2c\x96\x6d\xff\x91\xc2\x30\xbc\x27\xce\x02\xb6\xd2\xe8\xca\x66\x8c\x5c\xcb\xce\x19\x23\x4b\x2e\xcd\x8c\x53\x80\xf7\xfd\x7c\x66\x1a\xf3\x82\x7f\xa0\xf3\xe6\x7b\x87\x7e\x07\xc3\xc0\x49\x56\xe1\x15\x99\x13\xbf\x93\xf8\xf6\xf9\xca\x0b\x71\x72\x2a\xe6\x0e\x9d\x86\x3f\x47\xef\x75\x55\x13\x03\xeb\x62\xd7\xfe\xd7\x34\x84\x72\x05\xe6\x85\x53\xcf\xe8\xa1\x8d\xbd\xdb\x2b\x3d\x3e\xdc\x8f\x97\x75\xa4\xfc\xd7\xf4\x4e\x90\xf3\xae\xa3\x2d\x0e\xab\x26\xf8\xc1\x75\x14\xbc\xd0\xdb\x0b\x97\xb8\x74\x8e\x8e\xef\x70\x9b\xff\x2a\x0b\x84\x17\x4d\xf5\x9c\x8b\xf8\x61\x8e\x18\x36\xbb\x7d\x95\xe2\xf8\x09\x6b\xce\x2b\x4d\x75\xb7\x49\x95\x6b\xf8\x13\x86\x50\x7a\xf9\x37\xcb\x7f\x5a\x5e\x63\x3e\x82\xb7\x47\xfa\xb7\x35\xaa\x67\xd7\x11\x50\x8d\x10\x5c\xe7\x15\x42\xd4\x07\x49\xeb\x37\xf5\x2b\xb4\x9d\xb6\x48\xce\x99\xc0\x3d\x56\x2c\x7f\xc3\x78\xe9\xe4\x5f\x64\x20\x78\x6c\x0b\x49\x58\xac\x63\xc9\xd2\x88\xcc\xc0\xd8\x99\x63\xfe\x64\x81\x7b\x4b\xe8\xb7\xd2\xac\xc7\x32\xa7\x13\xba\x80\x87\x6e\x53\x95\xe3\xa8\xe7\xe9\x54\xd9\x38\xe9\x47\x4b\x50\x5e\xb7\x04\xc1\xab\xfd\xd0\x7b\x0a\x7c\x82\x43\x1a\xff\x11\x62\xde\xc5\xd1\x37\xcd\x3c\xc1\xa7\xbf\x67\xdf\xa3\x2d\x86\x61\xf5\x6f\x00\x00\x00\xff\xff\x82\xfc\x97\x4b\x70\x07\x00\x00"),
},
"/tags.html": &vfsgen۰CompressedFileInfo{
name: "tags.html",
modTime: time.Date(2018, 9, 10, 21, 40, 58, 715425786, time.UTC),
modTime: time.Date(2018, 10, 4, 18, 37, 50, 489870313, time.UTC),
uncompressedSize: 2868,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x56\xdf\x6f\xdb\x36\x10\x7e\xf7\x5f\x71\x25\x0a\xb4\x05\x2c\x31\xe9\xd6\xb5\x73\x24\x61\x85\x9b\x61\x1d\xf6\x0b\x43\x32\x6c\x08\xf2\x40\x4b\x27\x89\x0e\x4d\xaa\xe4\xc9\x89\xa7\xe9\x7f\x1f\x68\xc9\x8e\xec\xd8\x41\x86\xee\xa1\x7e\x11\x7d\x3f\x3e\x7d\xf7\x9d\xc8\x63\xd3\x64\x98\x4b\x8d\xc0\x48\x14\x8e\xb5\xed\x28\x7a\xf6\xe1\xd7\xe9\xc5\x5f\xbf\x9d\x43\x49\x0b\x95\x8c\xa2\x67\x41\x70\x25\x73\x50\x04\x1f\xcf\xe1\xed\x75\x02\xeb\x5f\xe4\xbd\x90\x2a\xe1\x5c\xcc\xb4\x09\xe6\x0e\x14\x05\x12\xbf\xed\x1e\xef\xba\xc7\x5b\x96\x40\xf4\xec\x0a\x75\x26\xf3\xeb\x20\xb8\x47\x1b\x42\x3d\x01\xed\x11\x98\x77\x4f\x81\x39\x96\x5f\x50\x0f\xe1\x0d\xc9\x81\xfc\x75\x62\x10\xec\x26\x97\x28\xb2\x64\xb4\x7e\xe1\x02\x49\x40\x5a\x0a\xeb\x90\x62\x56\x53\x1e\xbc\x63\xbd\x6b\x26\x1c\x42\x69\x31\x8f\x19\x67\x30\x8c\x2f\x89\xaa\x00\x3f\xd5\x72\x19\xb3\x3f\x83\xcb\xf7\xc1\xd4\x2c\x2a\x41\x72\xa6\x90\x41\x6a\x34\xa1\xa6\x98\x7d\x3c\x8f\x31\x2b\x70\x9c\x96\xd6\x2c\x30\x3e\xdd\xe0\x92\x24\x85\x49\xd3\x40\xf8\x3b\x16\xd2\x91\x5d\x7d\x30\x0b\x21\x35\xb4\x2d\xf7\xd6\x5f\xc4\x02\xa1\x6d\x23\xde\x05\x76\x49\x4a\xea\x1b\xb0\xa8\x62\x26\x53\xa3\x19\xd0\xaa\xc2\x98\xc9\x85\x28\x90\xcb\xd4\xb0\x0d\x51\x47\x82\x64\xca\x73\xb1\xf4\x71\xa1\x77\x3d\x40\x70\xb4\x52\xe8\x4a\x44\xda\x4f\x4b\x9d\xe3\x9d\x37\x4c\x9d\x63\xc0\x93\x51\xc4\x3b\xb5\xa2\x99\xc9\x56\x3d\x54\x79\xfa\x14\xfa\xe5\x69\x1f\x9e\xc9\xe5\xa6\x27\xb7\x56\x54\x15\xda\x9e\x53\x27\x87\x98\x6d\xaa\xbc\xb7\xd9\x5d\x43\x67\x2c\x13\x8f\x1d\x71\x2a\x0f\x7b\x2f\x44\x71\xdc\x39\xb5\x28\x08\xb3\xc3\x01\x4d\x23\x73\x08\x7f\x10\xee\x8f\x5a\x69\xd7\xb6\x3e\xc1\x2f\xd1\x8a\x99\x54\x92\x24\xba\x75\x62\xd3\xa0\xce\xda\x76\x97\x2c\xdf\x67\xdb\x34\x60\x85\x2e\x10\x9e\xdf\xe0\x6a\x0c\xcf\x97\x42\xd5\x08\x93\xd8\x2b\x56\x19\x27\xc9\x58\x89\x0e\xf6\x71\x0e\x17\x9d\xc1\x52\x28\x59\xe8\x98\x29\xcc\x89\x81\x36\x5e\xc3\x87\x91\xdb\x32\x9e\x0f\xeb\x10\x9b\x06\x5b\xac\x8c\x6f\x4f\x47\xa6\x6b\xd2\x3f\x50\x5b\xf5\xa9\x46\xbb\xf2\xbd\x23\x51\x0c\x02\x2e\x44\xe1\x8d\x4b\x8f\xc3\x0e\xd6\x3d\xa8\x76\x08\x7a\x34\x6a\x9f\x1a\x17\x47\x61\x23\x4e\xd9\x61\x2d\x7a\x29\xac\x2c\xca\x2f\x5b\x8b\x2e\xe7\x4b\x91\x62\xc3\xaa\xdf\x04\xe1\xf7\xc6\x2e\x04\x01\x3b\x79\x0d\x3f\x0a\x3d\x86\xd7\x27\x27\xdf\xc0\xe9\x9b\xc9\xc9\xd7\x93\x93\x37\x70\x79\x31\x65\x87\xc8\x1f\xe6\xf2\xa0\x9c\xcf\x64\xfb\x3f\xf5\x09\x64\x16\xb3\x07\xdf\xe6\x64\x3f\x9a\x1d\x26\xb1\x7b\x6a\x39\x59\x68\xa1\x58\x12\xf1\x4c\x2e\x8f\xb0\xe6\xe2\x40\x9b\x8e\x08\xf6\xc4\x43\x04\x75\x36\x6c\x43\xc4\x07\x47\x65\x4f\xe5\xc1\x01\x9b\x1b\x43\xbb\xe7\x6b\x95\xfc\x2c\x32\x84\x5b\x49\x25\x44\xa9\xc9\x30\x89\xbe\x8a\xf8\x7a\x01\xb3\xd5\xbd\xdc\x7e\xa8\xb9\x09\xe7\x85\xa4\xb2\x9e\x85\xa9\x59\xf0\x39\x3a\x97\x5b\xf1\x37\x4b\xbe\xdb\x2c\x7d\x9d\x11\xaf\x76\xf0\xa7\x25\xa6\x37\xa6\x26\xa0\x12\xc1\x99\xda\xa6\x08\x1e\x1f\x04\x4d\x1e\xc5\x2f\x50\xd7\x52\x23\x19\xa3\x1c\xb7\x58\xb0\xe4\x11\xe7\xa1\x37\xff\x24\x1c\xc1\x65\x95\xf9\x8f\x7a\xe2\x25\x0b\xbd\xa5\x37\xac\x87\x50\x35\x94\xcb\xdf\x04\x78\xd8\x49\xe4\x2f\x02\x6b\x8f\x4b\xad\xac\x08\x9c\x4d\xef\xe7\xe0\xdc\xf1\xce\xec\x42\x7f\x85\x88\xfa\x7f\x5d\xc6\xfe\xb0\xd8\x81\xe9\xe6\x32\xe1\x1d\xf1\xb9\x58\x8a\xce\xba\x6d\xc7\x52\x58\x10\x73\x71\x37\x15\x4a\x39\x88\xe1\x6a\x34\x68\xf7\x7f\x9e\x19\x2f\x3e\x6b\x87\x84\x73\x67\xf4\x8b\xf1\xe8\xd8\x07\x77\x7d\xd6\x2f\x6e\xa5\xce\xcc\x6d\x68\xb4\x32\x22\x83\x18\xf2\x5a\xa7\x24\x8d\x7e\xf9\x0a\x9a\x6d\xf6\x7b\x6b\xc5\x2a\xac\xac\x21\xe3\x25\x08\x73\x63\xcf\x45\x5a\x86\xa9\x50\xea\xe5\xb6\xe4\xf1\x7d\x72\x6d\xd5\x18\xa4\xce\xf0\xee\x55\x33\xa8\xc9\xbf\x63\x38\x79\x57\x53\x53\x6b\xf2\xd1\xaf\xce\xb6\x61\xed\x76\xdd\x9e\xf5\xed\xdd\x6d\x50\xb7\xc1\x22\xde\xdd\x59\x22\xde\xdd\x84\x37\xf6\x7f\x03\x00\x00\xff\xff\x88\x1c\x69\x6a\x34\x0b\x00\x00"),
},
"/vulns.html": &vfsgen۰CompressedFileInfo{
name: "vulns.html",
modTime: time.Date(2018, 9, 10, 21, 40, 58, 715425786, time.UTC),
modTime: time.Date(2018, 10, 4, 17, 36, 7, 550458133, time.UTC),
uncompressedSize: 2715,
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x56\xdf\x6f\xdb\x36\x10\x7e\xcf\x5f\x71\x21\xfa\x18\x99\x48\x82\xa1\x59\x40\x0b\x5b\x93\x60\x2b\xd0\xfd\x40\x9b\x0e\x1b\x8a\x62\xa0\xa4\xb3\xc4\x86\x22\x35\xf2\xe4\xcd\x13\xf4\xbf\x0f\x12\x2d\x5b\x96\xec\x2c\xc3\xfc\x20\x4b\xbe\xbb\xef\xee\xfb\xee\x74\x74\xd3\x64\xb8\x52\x06\x81\xad\x6b\x6d\x3c\x6b\xdb\x33\x71\x7e\xff\xd3\xdd\xe3\x6f\x3f\x3f\x40\x41\xa5\x8e\xcf\xc4\x79\x14\x7d\x52\x2b\xd0\x04\x6f\x1f\xe0\xf5\xe7\x18\xfa\x8f\xe8\xac\x90\x6a\xe9\xfd\x92\x19\x1b\x7d\xf1\xa0\x29\x52\xf8\x75\xf8\xba\x09\x5f\xaf\x59\x0c\xe2\xfc\x13\x9a\x4c\xad\x3e\x47\xd1\x1e\x6d\x0c\xf5\x02\xb4\x67\x60\x6e\x5e\x02\x73\x2a\x3e\xa7\x2d\x44\xf7\x43\x7c\x24\xbe\x0f\x8c\xa2\xc3\xe0\x02\x65\x16\x9f\xf5\x09\x4b\x24\x09\x69\x21\x9d\x47\x5a\xb2\x9a\x56\xd1\x0d\xdb\x9a\x12\xe9\x11\x0a\x87\xab\x25\xe3\x0c\xc6\xfe\x05\x51\x15\xe1\x1f\xb5\x5a\x2f\xd9\xaf\xd1\xc7\x6f\xa3\x3b\x5b\x56\x92\x54\xa2\x91\x41\x6a\x0d\xa1\xa1\x25\x7b\xfb\xb0\xc4\x2c\xc7\x8b\xb4\x70\xb6\xc4\xe5\xe5\x80\x4b\x8a\x34\xc6\x4d\x03\x8b\xf7\x98\x2b\x4f\x6e\xf3\xf1\xfd\x3b\x68\x5b\x1e\x7e\xaa\x2c\xb4\xed\x6d\x77\xff\x28\x73\x68\x5b\xf8\xa5\xd6\x06\x9d\x4c\x94\x56\xb4\x81\xce\xc1\x91\xe0\x01\x25\x20\x6a\x65\x9e\xc0\xa1\x5e\x32\x95\x5a\xc3\x80\x36\x15\x2e\x99\x2a\x65\x8e\x5c\xa5\x96\x0d\x2c\x3c\x49\x52\x29\x5f\xc9\x75\xe7\xb7\xe8\x4c\x33\x04\x4f\x1b\x8d\xbe\x40\xa4\x69\x58\xea\x3d\x4f\xac\x25\x4f\x4e\x56\x8b\x52\x99\x45\xea\x3d\x03\x1e\x9f\x09\x1e\x14\x15\x89\xcd\x36\x5b\xc4\x4c\xad\x87\x46\x74\x8a\x48\x65\xd0\x6d\xb3\xf5\x76\xbb\xeb\x53\xe2\x50\x66\xa9\xab\xcb\x64\x64\xdf\x56\x15\x0b\xb9\x6b\xc1\x11\xc9\x04\x97\xb1\xe0\x5a\xcd\xe2\x06\x6c\x99\x92\x5a\xe3\x10\x3a\x95\xf6\x30\x54\x70\xab\xe3\xb3\xfd\xe3\x88\x41\x25\x73\x8c\x3a\x8e\x07\x1c\xc2\xc4\x5e\xfe\x87\x56\x0a\x5f\x4a\xad\xe3\xe3\x1d\x0d\x36\xc1\x8b\xcb\x71\x4d\x99\x5a\x8f\x1e\xab\xa1\x22\xc2\xbf\x28\x72\x2a\x2f\x88\xc5\xdf\x61\x87\x46\x98\x81\x35\xb7\xd0\x34\x8b\x7b\x49\xd8\xb1\xab\x46\x74\x9a\x26\xbc\x2e\x8b\x37\x32\xeb\xf2\x7b\xf8\xaa\x6d\x8f\x92\x95\x1a\x1d\x41\x7f\x8d\x32\x69\x72\x74\x0c\x9c\xd5\xb8\xb5\x4c\x04\x68\x9a\x1d\x62\xdb\xc2\xf7\x2a\x2f\x2e\xe0\xce\x29\x52\xa9\xd4\x17\x20\x4d\xc6\xad\x83\x7b\x5c\xa5\xd6\x5c\xc2\x7a\x44\x5c\xa1\x87\x95\xad\x4d\x76\x8a\x6c\xd3\xa0\xc9\xda\x76\xd4\x91\xe2\x2a\xfe\x50\x97\xa5\x74\x1b\xc1\x8b\xab\x91\x2c\xf5\x6e\x98\xb4\xf2\x14\xe5\xce\xd6\xd5\x7c\x98\xe6\x3e\x91\x22\x2c\x27\x8e\xbd\xb3\xaf\xa4\xd9\xcd\xa7\xcc\xf2\x30\x42\x1a\x0d\x2c\x82\x78\x9d\xbc\x9d\xd3\x91\xd8\x24\x7e\xb4\x24\xb5\xe0\xc9\xa4\x82\xd9\xa0\x36\x8d\xeb\xf4\x85\x57\x4f\xb8\xb9\x80\x57\x6b\xa9\x6b\x84\xdb\xe5\x36\xc7\x9b\xcd\x07\x5c\xa3\x53\xb4\x19\x35\xea\xff\x51\xd1\x32\x41\x0d\xfd\x35\x6a\x9a\xd4\x6a\xeb\xfa\xe4\x6d\x0b\x55\xad\xf5\x30\x50\x5b\xaa\xdb\x82\x4e\x53\x6d\x9a\x3e\x1a\xa6\xf5\x1d\x21\x1a\x5a\xb9\xf7\xa8\x0f\x5e\xb5\xe2\x2a\xbe\x47\x92\x4a\xfb\xc3\xc6\xee\x04\xea\x4f\xb7\x7f\x91\x66\xef\x3c\xe8\x18\xc2\x4e\x8c\x79\x25\x0d\x6a\xe8\xaf\x51\x86\x2b\x59\xeb\xe9\x68\xcf\xbc\xfb\x15\xa0\x4c\x7e\x4c\xe6\xe2\xfa\xd0\xb5\xdf\xcf\x9d\x94\xa1\x9c\xc5\x8f\xb2\xc4\x89\x4e\x2f\x6f\x51\x80\xd8\x73\x9e\x74\x6b\x66\x3f\x39\x9b\xbc\xb8\x9e\x0e\xe5\xc1\x3b\x77\x9c\x76\xb7\xd6\x8f\x70\xde\x25\xbe\x47\x9f\x3a\x55\x91\xb2\x66\x36\x0a\x2f\x80\x5f\x59\x4b\xb3\xc5\xda\xbb\x0e\xcb\x7f\x97\xe9\x9d\x32\x4f\x6d\xcb\x80\xa4\xcb\xbb\x03\xfb\xf7\x44\x4b\xf3\x34\xd2\x20\x38\x74\xe7\xc2\x73\x75\x9c\xd8\x34\xd3\xe7\xbd\x7f\x28\xf1\x60\xf9\xa6\x68\xe6\x55\x8b\x2a\xfe\x41\x66\x08\x7f\x2a\x2a\x40\xa4\x36\xc3\x58\x5c\x0b\xde\xdf\x40\xb2\xd9\x33\xea\xfe\x42\xf8\x5b\xce\x73\x45\x45\x9d\x2c\x52\x5b\xf2\x2f\xe8\xfd\xca\xc9\xbf\x59\xfc\xcd\x70\x1b\xce\xb7\x6a\x96\xe3\xae\xc0\xf4\xc9\xd6\x04\x54\x20\x78\x5b\xbb\x14\xa1\xcb\x01\x92\x6e\x9f\xcd\x91\xa3\xa9\x95\x41\xb2\x56\x7b\xee\x30\x67\xf1\x33\xc6\x59\x76\xc1\x83\x0e\xdb\x23\x3e\x88\x28\x78\x38\xf5\x05\x0f\xff\x37\x07\xf1\xfe\x09\x00\x00\xff\xff\x30\xe0\x25\xc3\x9b\x0a\x00\x00"),

View file

@ -38,19 +38,19 @@ func (cmd *layerCommand) Run(ctx context.Context, args []string) error {
}
// Create the registry client.
r, err := createRegistryClient(image.Domain)
r, err := createRegistryClient(ctx, image.Domain)
if err != nil {
return err
}
// Get the digest.
digest, err := r.Digest(image)
digest, err := r.Digest(ctx, image)
if err != nil {
return err
}
// Download the layer.
layer, err := r.DownloadLayer(image.Path, digest)
layer, err := r.DownloadLayer(ctx, image.Path, digest)
if err != nil {
return err
}

10
list.go
View file

@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
@ -29,13 +30,16 @@ func (cmd *listCommand) Run(ctx context.Context, args []string) error {
}
// Create the registry client.
r, err := createRegistryClient(args[0])
r, err := createRegistryClient(ctx, args[0])
if err != nil {
return err
}
// Get the repositories via catalog.
repos, err := r.Catalog("")
repos, err := r.Catalog(ctx, "")
if err != nil {
if _, ok := err.(*json.SyntaxError); ok {
return fmt.Errorf("domain %s is not a valid registry", r.Domain)
}
return err
}
sort.Strings(repos)
@ -52,7 +56,7 @@ func (cmd *listCommand) Run(ctx context.Context, args []string) error {
for _, repo := range repos {
go func(repo string) {
// Get the tags.
tags, err := r.Tags(repo)
tags, err := r.Tags(ctx, repo)
if err != nil {
fmt.Printf("Get tags of [%s] error: %s", repo, err)
}

22
main.go
View file

@ -24,6 +24,7 @@ var (
timeout time.Duration
authURL string
username string
password string
@ -64,6 +65,8 @@ func main() {
p.FlagSet.DurationVar(&timeout, "timeout", time.Minute, "timeout for HTTP requests")
p.FlagSet.StringVar(&authURL, "auth-url", "", "alternate URL for registry authentication (ex. auth.docker.io)")
p.FlagSet.StringVar(&username, "username", "", "username for the registry")
p.FlagSet.StringVar(&username, "u", "", "username for the registry")
@ -75,7 +78,7 @@ func main() {
// Set the before function.
p.Before = func(ctx context.Context) error {
// On ^C, or SIGTERM handle exit.
signals := make(chan os.Signal, 0)
signals := make(chan os.Signal)
signal.Notify(signals, os.Interrupt)
signal.Notify(signals, syscall.SIGTERM)
_, cancel := context.WithCancel(ctx)
@ -99,22 +102,31 @@ func main() {
p.Run()
}
func createRegistryClient(domain string) (*registry.Registry, error) {
auth, err := repoutils.GetAuthConfig(username, password, domain)
func createRegistryClient(ctx context.Context, domain string) (*registry.Registry, error) {
// Use the auth-url domain if provided.
authDomain := authURL
if authDomain == "" {
authDomain = domain
}
auth, err := repoutils.GetAuthConfig(username, password, authDomain)
if err != nil {
return nil, err
}
// Prevent non-ssl unless explicitly forced
if !forceNonSSL && strings.HasPrefix(auth.ServerAddress, "http:") {
return nil, fmt.Errorf("Attempted to use insecure protocol! Use force-non-ssl option to force")
return nil, fmt.Errorf("attempted to use insecure protocol! Use force-non-ssl option to force")
}
// Create the registry client.
return registry.New(auth, registry.Opt{
logrus.Infof("domain: %s", domain)
logrus.Infof("server address: %s", auth.ServerAddress)
return registry.New(ctx, auth, registry.Opt{
Domain: domain,
Insecure: insecure,
Debug: debug,
SkipPing: skipPing,
NonSSL: forceNonSSL,
Timeout: timeout,
})
}

View file

@ -36,7 +36,7 @@ func (cmd *manifestCommand) Run(ctx context.Context, args []string) error {
}
// Create the registry client.
r, err := createRegistryClient(image.Domain)
r, err := createRegistryClient(ctx, image.Domain)
if err != nil {
return err
}
@ -44,13 +44,13 @@ func (cmd *manifestCommand) Run(ctx context.Context, args []string) error {
var manifest interface{}
if cmd.v1 {
// Get the v1 manifest if it was explicitly asked for.
manifest, err = r.ManifestV1(image.Path, image.Reference())
manifest, err = r.ManifestV1(ctx, image.Path, image.Reference())
if err != nil {
return err
}
} else {
// Get the v2 manifest.
manifest, err = r.Manifest(image.Path, image.Reference())
manifest, err = r.Manifest(ctx, image.Path, image.Reference())
if err != nil {
return err
}

View file

@ -29,3 +29,16 @@ func TestManifestV1(t *testing.T) {
t.Fatalf("expected: %s\ngot: %s", expected, out)
}
}
func TestManifestWithHubDomain(t *testing.T) {
// Regression test for https://github.com/genuinetools/reg/issues/164
out, err := run("manifest", "busybox")
if err != nil {
t.Fatalf("output: %s, error: %v", out, err)
}
expected := `"schemaVersion": 2,`
if !strings.Contains(out, expected) {
t.Fatalf("expected: %s\ngot: %s", expected, out)
}
}

View file

@ -1,6 +1,7 @@
package registry
import (
"context"
"net/url"
"github.com/peterhellberg/link"
@ -11,7 +12,7 @@ type catalogResponse struct {
}
// Catalog returns the repositories in a registry.
func (r *Registry) Catalog(u string) ([]string, error) {
func (r *Registry) Catalog(ctx context.Context, u string) ([]string, error) {
if u == "" {
u = "/v2/_catalog"
}
@ -19,7 +20,7 @@ func (r *Registry) Catalog(u string) ([]string, error) {
r.Logf("registry.catalog url=%s", uri)
var response catalogResponse
h, err := r.getJSON(uri, &response, false)
h, err := r.getJSON(ctx, uri, &response)
if err != nil {
return nil, err
}
@ -27,7 +28,7 @@ func (r *Registry) Catalog(u string) ([]string, error) {
for _, l := range link.ParseHeader(h) {
if l.Rel == "next" {
unescaped, _ := url.QueryUnescape(l.URI)
repos, err := r.Catalog(unescaped)
repos, err := r.Catalog(ctx, unescaped)
if err != nil {
return nil, err
}

View file

@ -1,6 +1,7 @@
package registry
import (
"context"
"fmt"
"net/http"
@ -10,7 +11,7 @@ import (
// Delete removes a repository digest from the registry.
// https://docs.docker.com/registry/spec/api/#deleting-an-image
func (r *Registry) Delete(repository string, digest digest.Digest) (err error) {
func (r *Registry) Delete(ctx context.Context, repository string, digest digest.Digest) (err error) {
url := r.url("/v2/%s/manifests/%s", repository, digest)
r.Logf("registry.manifests.delete url=%s repository=%s digest=%s",
url, repository, digest)
@ -21,7 +22,7 @@ func (r *Registry) Delete(repository string, digest digest.Digest) (err error) {
}
req.Header.Add("Accept", fmt.Sprintf("%s;q=0.9", schema2.MediaTypeManifest))
resp, err := r.Client.Do(req)
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return err
}
@ -31,5 +32,5 @@ func (r *Registry) Delete(repository string, digest digest.Digest) (err error) {
return nil
}
return fmt.Errorf("Got status code: %d", resp.StatusCode)
return fmt.Errorf("got status code: %d", resp.StatusCode)
}

View file

@ -1,6 +1,7 @@
package registry
import (
"context"
"fmt"
"net/http"
@ -9,7 +10,7 @@ import (
)
// Digest returns the digest for an image.
func (r *Registry) Digest(image Image) (digest.Digest, error) {
func (r *Registry) Digest(ctx context.Context, image Image) (digest.Digest, error) {
if len(image.Digest) > 1 {
// return early if we already have an image digest.
return image.Digest, nil
@ -25,14 +26,14 @@ func (r *Registry) Digest(image Image) (digest.Digest, error) {
}
req.Header.Add("Accept", schema2.MediaTypeManifest)
resp, err := r.Client.Do(req)
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
return "", fmt.Errorf("Got status code: %d", resp.StatusCode)
return "", fmt.Errorf("got status code: %d", resp.StatusCode)
}
return digest.Parse(resp.Header.Get("Docker-Content-Digest"))

View file

@ -1,23 +1,25 @@
package registry
import (
"context"
"testing"
"github.com/genuinetools/reg/repoutils"
)
func TestDigestFromDockerHub(t *testing.T) {
ctx := context.Background()
auth, err := repoutils.GetAuthConfig("", "", "docker.io")
if err != nil {
t.Fatalf("Could not get auth config: %s", err)
}
r, err := New(auth, Opt{})
r, err := New(ctx, auth, Opt{})
if err != nil {
t.Fatalf("Could not create registry instance: %s", err)
}
d, err := r.Digest(Image{Domain: "docker.io", Path: "library/alpine", Tag: "latest"})
d, err := r.Digest(ctx, Image{Domain: "docker.io", Path: "library/alpine", Tag: "latest"})
if err != nil {
t.Fatalf("Could not get digest: %s", err)
}
@ -28,17 +30,18 @@ func TestDigestFromDockerHub(t *testing.T) {
}
func TestDigestFromGCR(t *testing.T) {
ctx := context.Background()
auth, err := repoutils.GetAuthConfig("", "", "gcr.io")
if err != nil {
t.Fatalf("Could not get auth config: %s", err)
}
r, err := New(auth, Opt{})
r, err := New(ctx, auth, Opt{SkipPing: true})
if err != nil {
t.Fatalf("Could not create registry instance: %s", err)
}
d, err := r.Digest(Image{Domain: "gcr.io", Path: "google_containers/hyperkube", Tag: "v1.9.9"})
d, err := r.Digest(ctx, Image{Domain: "gcr.io", Path: "google-containers/hyperkube", Tag: "v1.9.9"})
if err != nil {
t.Fatalf("Could not get digest: %s", err)
}

View file

@ -1,21 +1,27 @@
package registry
import (
"context"
"io"
"net/http"
"net/url"
"fmt"
"github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
)
// DownloadLayer downloads a specific layer by digest for a repository.
func (r *Registry) DownloadLayer(repository string, digest digest.Digest) (io.ReadCloser, error) {
func (r *Registry) DownloadLayer(ctx context.Context, repository string, digest digest.Digest) (io.ReadCloser, error) {
url := r.url("/v2/%s/blobs/%s", repository, digest)
r.Logf("registry.layer.download url=%s repository=%s digest=%s", url, repository, digest)
resp, err := r.Client.Get(url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
@ -24,8 +30,8 @@ func (r *Registry) DownloadLayer(repository string, digest digest.Digest) (io.Re
}
// UploadLayer uploads a specific layer by digest for a repository.
func (r *Registry) UploadLayer(repository string, digest reference.Reference, content io.Reader) error {
uploadURL, token, err := r.initiateUpload(repository)
func (r *Registry) UploadLayer(ctx context.Context, repository string, digest reference.Reference, content io.Reader) error {
uploadURL, token, err := r.initiateUpload(ctx, repository)
if err != nil {
return err
}
@ -42,16 +48,20 @@ func (r *Registry) UploadLayer(repository string, digest reference.Reference, co
upload.Header.Set("Content-Type", "application/octet-stream")
upload.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
_, err = r.Client.Do(upload)
_, err = r.Client.Do(upload.WithContext(ctx))
return err
}
// HasLayer returns if the registry contains the specific digest for a repository.
func (r *Registry) HasLayer(repository string, digest digest.Digest) (bool, error) {
func (r *Registry) HasLayer(ctx context.Context, repository string, digest digest.Digest) (bool, error) {
checkURL := r.url("/v2/%s/blobs/%s", repository, digest)
r.Logf("registry.layer.check url=%s repository=%s digest=%s", checkURL, repository, digest)
resp, err := r.Client.Head(checkURL)
req, err := http.NewRequest("HEAD", checkURL, nil)
if err != nil {
return false, err
}
resp, err := r.Client.Do(req.WithContext(ctx))
if err == nil {
defer resp.Body.Close()
return resp.StatusCode == http.StatusOK, nil
@ -72,11 +82,16 @@ func (r *Registry) HasLayer(repository string, digest digest.Digest) (bool, erro
return false, err
}
func (r *Registry) initiateUpload(repository string) (*url.URL, string, error) {
func (r *Registry) initiateUpload(ctx context.Context, repository string) (*url.URL, string, error) {
initiateURL := r.url("/v2/%s/blobs/uploads/", repository)
r.Logf("registry.layer.initiate-upload url=%s repository=%s", initiateURL, repository)
resp, err := r.Client.Post(initiateURL, "application/octet-stream", nil)
req, err := http.NewRequest("POST", initiateURL, nil)
if err != nil {
return nil, "", err
}
req.Header.Set("Content-Type", "application/octet-stream")
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, "", err
}

View file

@ -2,8 +2,9 @@ package registry
import (
"bytes"
"context"
"encoding/json"
"fmt"
"errors"
"io/ioutil"
"net/http"
@ -13,8 +14,13 @@ import (
"github.com/docker/distribution/manifest/schema2"
)
var (
// ErrUnexpectedSchemaVersion a specific schema version was requested, but was not returned
ErrUnexpectedSchemaVersion = errors.New("recieved a different schema version than expected")
)
// Manifest returns the manifest for a specific repository:tag.
func (r *Registry) Manifest(repository, ref string) (distribution.Manifest, error) {
func (r *Registry) Manifest(ctx context.Context, repository, ref string) (distribution.Manifest, error) {
uri := r.url("/v2/%s/manifests/%s", repository, ref)
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
@ -23,9 +29,9 @@ func (r *Registry) Manifest(repository, ref string) (distribution.Manifest, erro
return nil, err
}
req.Header.Add("Accept", fmt.Sprintf("%s;q=0.9", schema2.MediaTypeManifest))
req.Header.Add("Accept", schema2.MediaTypeManifest)
resp, err := r.Client.Do(req)
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
@ -46,12 +52,12 @@ func (r *Registry) Manifest(repository, ref string) (distribution.Manifest, erro
}
// ManifestList gets the registry v2 manifest list.
func (r *Registry) ManifestList(repository, ref string) (manifestlist.ManifestList, error) {
func (r *Registry) ManifestList(ctx context.Context, repository, ref string) (manifestlist.ManifestList, error) {
uri := r.url("/v2/%s/manifests/%s", repository, ref)
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
var m manifestlist.ManifestList
if _, err := r.getJSON(uri, &m, true); err != nil {
if _, err := r.getJSON(ctx, uri, &m); err != nil {
r.Logf("registry.manifests response=%v", m)
return m, err
}
@ -60,35 +66,43 @@ func (r *Registry) ManifestList(repository, ref string) (manifestlist.ManifestLi
}
// ManifestV2 gets the registry v2 manifest.
func (r *Registry) ManifestV2(repository, ref string) (schema2.Manifest, error) {
func (r *Registry) ManifestV2(ctx context.Context, repository, ref string) (schema2.Manifest, error) {
uri := r.url("/v2/%s/manifests/%s", repository, ref)
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
var m schema2.Manifest
if _, err := r.getJSON(uri, &m, true); err != nil {
if _, err := r.getJSON(ctx, uri, &m); err != nil {
r.Logf("registry.manifests response=%v", m)
return m, err
}
if m.Versioned.SchemaVersion != 2 {
return m, ErrUnexpectedSchemaVersion
}
return m, nil
}
// ManifestV1 gets the registry v1 manifest.
func (r *Registry) ManifestV1(repository, ref string) (schema1.SignedManifest, error) {
func (r *Registry) ManifestV1(ctx context.Context, repository, ref string) (schema1.SignedManifest, error) {
uri := r.url("/v2/%s/manifests/%s", repository, ref)
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
var m schema1.SignedManifest
if _, err := r.getJSON(uri, &m, false); err != nil {
if _, err := r.getJSON(ctx, uri, &m); err != nil {
r.Logf("registry.manifests response=%v", m)
return m, err
}
if m.Versioned.SchemaVersion != 1 {
return m, ErrUnexpectedSchemaVersion
}
return m, nil
}
// PutManifest calls a PUT for the specific manifest for an image.
func (r *Registry) PutManifest(repository, ref string, manifest distribution.Manifest) error {
func (r *Registry) PutManifest(ctx context.Context, repository, ref string, manifest distribution.Manifest) error {
url := r.url("/v2/%s/manifests/%s", repository, ref)
r.Logf("registry.manifest.put url=%s repository=%s reference=%s", url, repository, ref)
@ -103,7 +117,7 @@ func (r *Registry) PutManifest(repository, ref string, manifest distribution.Man
}
req.Header.Set("Content-Type", schema2.MediaTypeManifest)
resp, err := r.Client.Do(req)
resp, err := r.Client.Do(req.WithContext(ctx))
if resp != nil {
defer resp.Body.Close()
}

View file

@ -1,10 +1,26 @@
package registry
import (
"context"
"net/http"
"strings"
)
// Pingable checks pingable
func (r *Registry) Pingable() bool {
// Currently *.gcr.io/v2 can't be ping if users have each projects auth
return !strings.HasSuffix(r.URL, "gcr.io")
}
// Ping tries to contact a registry URL to make sure it is up and accessible.
func (r *Registry) Ping() error {
func (r *Registry) Ping(ctx context.Context) error {
url := r.url("/v2/")
r.Logf("registry.ping url=%s", url)
resp, err := r.Client.Get(url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
resp, err := r.Client.Do(req.WithContext(ctx))
if resp != nil {
defer resp.Body.Close()
}

31
registry/ping_test.go Normal file
View file

@ -0,0 +1,31 @@
package registry
import (
"testing"
)
func TestPingable(t *testing.T) {
testcases := map[string]struct {
registry Registry
expect bool
}{
"Docker": {
registry: Registry{URL: "https://registry-1.docker.io"},
expect: true,
},
"GCR_global": {
registry: Registry{URL: "https://gcr.io"},
expect: false,
},
"GCR_asia": {
registry: Registry{URL: "https://asia.gcr.io"},
expect: false,
},
}
for label, testcase := range testcases {
actual := testcase.registry.Pingable()
if testcase.expect != actual {
t.Fatalf("%s: expected (%v), got (%v)", label, testcase.expect, actual)
}
}
}

View file

@ -1,6 +1,7 @@
package registry
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
@ -15,7 +16,7 @@ import (
"github.com/docker/docker/api/types"
)
// Registry defines the client for retriving information from the registry API.
// Registry defines the client for retrieving information from the registry API.
type Registry struct {
URL string
Domain string
@ -41,15 +42,17 @@ func Log(format string, args ...interface{}) {
// Opt holds the options for a new registry.
type Opt struct {
Domain string
Insecure bool
Debug bool
SkipPing bool
NonSSL bool
Timeout time.Duration
Headers map[string]string
}
// New creates a new Registry struct with the given URL and credentials.
func New(auth types.AuthConfig, opt Opt) (*Registry, error) {
func New(ctx context.Context, auth types.AuthConfig, opt Opt) (*Registry, error) {
transport := http.DefaultTransport
if opt.Insecure {
@ -60,14 +63,30 @@ func New(auth types.AuthConfig, opt Opt) (*Registry, error) {
}
}
return newFromTransport(auth, transport, opt)
return newFromTransport(ctx, auth, transport, opt)
}
func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, opt Opt) (*Registry, error) {
url := strings.TrimSuffix(auth.ServerAddress, "/")
func newFromTransport(ctx context.Context, auth types.AuthConfig, transport http.RoundTripper, opt Opt) (*Registry, error) {
if len(opt.Domain) < 1 || opt.Domain == "docker.io" {
opt.Domain = auth.ServerAddress
}
url := strings.TrimSuffix(opt.Domain, "/")
authURL := strings.TrimSuffix(auth.ServerAddress, "/")
if !reProtocol.MatchString(url) {
if !opt.NonSSL {
url = "https://" + url
} else {
url = "http://" + url
}
}
if !reProtocol.MatchString(authURL) {
if !opt.NonSSL {
authURL = "https://" + authURL
} else {
authURL = "http://" + authURL
}
}
tokenTransport := &TokenTransport{
@ -77,7 +96,7 @@ func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, opt Op
}
basicAuthTransport := &BasicTransport{
Transport: tokenTransport,
URL: url,
URL: authURL,
Username: auth.Username,
Password: auth.Password,
}
@ -108,8 +127,8 @@ func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, opt Op
Opt: opt,
}
if !opt.SkipPing {
if err := registry.Ping(); err != nil {
if registry.Pingable() && !opt.SkipPing {
if err := registry.Ping(ctx); err != nil {
return nil, err
}
}
@ -124,15 +143,20 @@ func (r *Registry) url(pathTemplate string, args ...interface{}) string {
return url
}
func (r *Registry) getJSON(url string, response interface{}, addV2Header bool) (http.Header, error) {
func (r *Registry) getJSON(ctx context.Context, url string, response interface{}) (http.Header, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if addV2Header {
req.Header.Add("Accept", fmt.Sprintf("%s,%s;q=0.9", schema2.MediaTypeManifest, manifestlist.MediaTypeManifestList))
switch response.(type) {
case *schema2.Manifest:
req.Header.Add("Accept", schema2.MediaTypeManifest)
case *manifestlist.ManifestList:
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
}
resp, err := r.Client.Do(req)
resp, err := r.Client.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}

View file

@ -1,16 +1,18 @@
package registry
import "context"
type tagsResponse struct {
Tags []string `json:"tags"`
}
// Tags returns the tags for a specific repository.
func (r *Registry) Tags(repository string) ([]string, error) {
func (r *Registry) Tags(ctx context.Context, repository string) ([]string, error) {
url := r.url("/v2/%s/tags/list", repository)
r.Logf("registry.tags url=%s repository=%s", url, repository)
var response tagsResponse
if _, err := r.getJSON(url, &response, false); err != nil {
if _, err := r.getJSON(ctx, url, &response); err != nil {
return nil, err
}

View file

@ -1,6 +1,7 @@
package registry
import (
"context"
"crypto/tls"
"encoding/base64"
"encoding/json"
@ -8,8 +9,11 @@ import (
"fmt"
"net/http"
"net/url"
"regexp"
)
var gcrMatcher = regexp.MustCompile(`https://([a-z]+\.|)gcr\.io/`)
// TokenTransport defines the data structure for authentication via tokens.
type TokenTransport struct {
Transport http.RoundTripper
@ -26,6 +30,7 @@ func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
authService, err := isTokenDemand(resp)
if err != nil {
resp.Body.Close()
return nil, err
}
@ -33,15 +38,28 @@ func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return resp, nil
}
resp.Body.Close()
return t.authAndRetry(authService, req)
}
type authToken struct {
Token string `json:"token"`
AccessToken string `json:"access_token"`
}
func (t authToken) String() (string, error) {
if t.Token != "" {
return t.Token, nil
}
if t.AccessToken != "" {
return t.AccessToken, nil
}
return "", errors.New("auth token cannot be empty")
}
func (t *TokenTransport) authAndRetry(authService *authService, req *http.Request) (*http.Response, error) {
token, authResp, err := t.auth(authService)
token, authResp, err := t.auth(req.Context(), authService)
if err != nil {
return authResp, err
}
@ -53,7 +71,7 @@ func (t *TokenTransport) authAndRetry(authService *authService, req *http.Reques
return response, err
}
func (t *TokenTransport) auth(authService *authService) (string, *http.Response, error) {
func (t *TokenTransport) auth(ctx context.Context, authService *authService) (string, *http.Response, error) {
authReq, err := authService.Request(t.Username, t.Password)
if err != nil {
return "", nil, err
@ -63,7 +81,7 @@ func (t *TokenTransport) auth(authService *authService) (string, *http.Response,
Transport: t.Transport,
}
resp, err := c.Do(authReq)
resp, err := c.Do(authReq.WithContext(ctx))
if err != nil {
return "", nil, err
}
@ -78,7 +96,8 @@ func (t *TokenTransport) auth(authService *authService) (string, *http.Response,
return "", nil, err
}
return authToken.Token, nil, nil
token, err := authToken.String()
return token, nil, err
}
func (t *TokenTransport) retry(req *http.Request, token string) (*http.Response, error) {
@ -122,7 +141,7 @@ func isTokenDemand(resp *http.Response) (*authService, error) {
// Token returns the required token for the specific resource url. If the registry requires basic authentication, this
// function returns ErrBasicAuth.
func (r *Registry) Token(url string) (string, error) {
func (r *Registry) Token(ctx context.Context, url string) (string, error) {
r.Logf("registry.token url=%s", url)
req, err := http.NewRequest("GET", url, nil)
@ -142,12 +161,19 @@ func (r *Registry) Token(url string) (string, error) {
}
}
resp, err := client.Do(req)
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusForbidden && gcrMatcher.MatchString(url) {
// GCR is not sending HTTP 401 on missing credentials but a HTTP 403 without
// any further information about why the request failed. Sending the credentials
// from the Docker config fixes this.
return "", ErrBasicAuth
}
a, err := isTokenDemand(resp)
if err != nil {
return "", err
@ -162,14 +188,14 @@ func (r *Registry) Token(url string) (string, error) {
if err != nil {
return "", err
}
resp, err = http.DefaultClient.Do(authReq)
resp, err = http.DefaultClient.Do(authReq.WithContext(ctx))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Getting token failed with StatusCode != StatusOK but %d", resp.StatusCode)
return "", fmt.Errorf("getting token failed with StatusCode != StatusOK but %d", resp.StatusCode)
}
var authToken authToken
@ -177,17 +203,13 @@ func (r *Registry) Token(url string) (string, error) {
return "", err
}
if authToken.Token == "" {
return "", errors.New("Auth token cannot be empty")
}
return authToken.Token, nil
return authToken.String()
}
// Headers returns the authorization headers for a specific uri.
func (r *Registry) Headers(uri string) (map[string]string, error) {
func (r *Registry) Headers(ctx context.Context, uri string) (map[string]string, error) {
// Get the token.
token, err := r.Token(uri)
token, err := r.Token(ctx, uri)
if err != nil {
if err == ErrBasicAuth {
// If we couldn't get a token because the server requires basic auth, just return basic auth headers.

View file

@ -1,14 +1,18 @@
package registry
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/docker/docker/api/types"
)
func TestErrBasicAuth(t *testing.T) {
ctx := context.Background()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
w.Header().Set("www-authenticate", `Basic realm="Registry Realm",service="Docker registry"`)
@ -24,11 +28,11 @@ func TestErrBasicAuth(t *testing.T) {
Password: "ss3j",
ServerAddress: ts.URL,
}
r, err := New(authConfig, Opt{Insecure: true, Debug: true})
r, err := New(ctx, authConfig, Opt{Insecure: true, Debug: true})
if err != nil {
t.Fatalf("expected no error creating client, got %v", err)
}
token, err := r.Token(ts.URL)
token, err := r.Token(ctx, ts.URL)
if err != ErrBasicAuth {
t.Fatalf("expected ErrBasicAuth getting token, got %v", err)
}
@ -36,3 +40,157 @@ func TestErrBasicAuth(t *testing.T) {
t.Fatalf("expected empty token, got %v", err)
}
}
var authURI string
func oauthFlow(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/oauth2/accesstoken") {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"access_token":"abcdef1234"}`))
return
}
if strings.HasPrefix(r.URL.Path, "/oauth2/token") {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"token":"abcdef1234"}`))
return
}
auth := r.Header.Get("authorization")
if !strings.HasPrefix(auth, "Bearer") {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if authURI != "" {
w.Header().Set("www-authenticate", `Bearer realm="`+authURI+`/oauth2/token",service="my.endpoint.here"`)
}
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]}`))
return
}
w.WriteHeader(http.StatusOK)
}
func TestBothTokenAndAccessTokenWork(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(oauthFlow))
defer ts.Close()
for _, which := range []string{"token", "accesstoken"} {
ctx := context.Background()
authURI = ts.URL + "/oauth2/" + which + "?service=my.endpoint.here"
authConfig := types.AuthConfig{
Username: "abc",
Password: "123",
ServerAddress: ts.URL,
}
authConfig.Email = "me@email.com"
r, err := New(ctx, authConfig, Opt{Insecure: true, Debug: true})
if err != nil {
t.Fatalf("expected no error creating client, got %v", err)
}
token, err := r.Token(ctx, ts.URL)
if err != nil {
t.Fatalf("err getting token from url: %v err: %v", ts.URL, err)
}
if token == "" {
t.Fatalf("error got empty token")
}
}
}
type testTransport func(*http.Request) (*http.Response, error)
func (tt testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return tt(req)
}
func TestTokenTransportErrorHandling(t *testing.T) {
tokenTransport := &TokenTransport{
Transport: testTransport(func(req *http.Request) (*http.Response, error) {
return nil, errors.New("transport failed")
}),
}
_, err := tokenTransport.RoundTrip(httptest.NewRequest(http.MethodGet, "/", nil))
if err == nil {
t.Fatalf("got no error from round trip: %s", err)
}
}
type testBody struct {
t *testing.T
closed bool
}
func (tb *testBody) Read(p []byte) (n int, err error) {
tb.t.Helper()
panic("unexpected read")
}
func (tb *testBody) Close() error {
tb.closed = true
return nil
}
func TestTokenTransportTokenDemandErr(t *testing.T) {
body := &testBody{t: t}
tokenTransport := &TokenTransport{
Transport: testTransport(func(req *http.Request) (*http.Response, error) {
return &http.Response{
Body: body,
StatusCode: http.StatusUnauthorized,
}, nil
}),
}
resp, err := tokenTransport.RoundTrip(httptest.NewRequest(http.MethodGet, "/", nil))
if err == nil {
t.Fatal("Expected error due to missing auth challenge header, got none")
}
if resp != nil {
t.Fatal("Expected no response")
}
if !body.closed {
t.Fatal("Expected body to be closed")
}
}
func TestTokenTransportAuthLeak(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(oauthFlow))
authURI = ts.URL + "/oauth2/token?service=my.endpoint.here"
callCounter := 0
body := &testBody{t: t}
tokenTransport := &TokenTransport{
Transport: testTransport(func(req *http.Request) (*http.Response, error) {
callCounter++
switch callCounter {
case 1: // failing authentication
header := http.Header{}
header.Set("www-authenticate", `Bearer realm="`+authURI+`/oauth2/token",service="my.endpoint.here"`)
return &http.Response{
Body: body,
StatusCode: http.StatusUnauthorized,
Header: header,
}, nil
case 2: // auth request
return ts.Client().Transport.RoundTrip(req)
default:
return &http.Response{
StatusCode: http.StatusOK,
Body: &testBody{t: t},
Header: http.Header{},
}, nil
}
}),
}
resp, err := tokenTransport.RoundTrip(httptest.NewRequest(http.MethodGet, "/", nil))
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if resp == nil {
t.Fatal("Response is missing")
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("unexpected status code: %d", resp.StatusCode)
}
if !body.closed {
t.Fatal("Expected body to be closed")
}
}

View file

@ -31,13 +31,13 @@ func (cmd *removeCommand) Run(ctx context.Context, args []string) error {
}
// Create the registry client.
r, err := createRegistryClient(image.Domain)
r, err := createRegistryClient(ctx, image.Domain)
if err != nil {
return err
}
// Get the digest.
digest, err := r.Digest(image)
digest, err := r.Digest(ctx, image)
if err != nil {
return err
}
@ -47,7 +47,7 @@ func (cmd *removeCommand) Run(ctx context.Context, args []string) error {
}
// Delete the reference.
if err := r.Delete(image.Path, digest); err != nil {
if err := r.Delete(ctx, image.Path, digest); err != nil {
return err
}
fmt.Printf("Deleted %s\n", image.String())

View file

@ -4,8 +4,9 @@ import (
"fmt"
"strings"
"github.com/docker/cli/cli/config"
clitypes "github.com/docker/cli/cli/config/types"
"github.com/docker/distribution/reference"
"github.com/docker/docker-ce/components/cli/cli/config"
"github.com/docker/docker/api/types"
"github.com/sirupsen/logrus"
)
@ -31,7 +32,7 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
dcfg, err := config.Load(config.Dir())
if err != nil {
return types.AuthConfig{}, fmt.Errorf("Loading config file failed: %v", err)
return types.AuthConfig{}, fmt.Errorf("loading config file failed: %v", err)
}
// return error early if there are no auths saved
@ -49,35 +50,42 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
authConfigs, err := dcfg.GetAllCredentials()
if err != nil {
return types.AuthConfig{}, fmt.Errorf("Getting credentials failed: %v", err)
return types.AuthConfig{}, fmt.Errorf("getting credentials failed: %v", err)
}
// if they passed a specific registry, return those creds _if_ they exist
if registry != "" {
// try with the user input
if creds, ok := authConfigs[registry]; ok {
return creds, nil
c := fixAuthConfig(creds, registry)
return c, nil
}
// remove https:// from user input and try again
if strings.HasPrefix(registry, "https://") {
if creds, ok := authConfigs[strings.TrimPrefix(registry, "https://")]; ok {
return creds, nil
registryCleaned := strings.TrimPrefix(registry, "https://")
if creds, ok := authConfigs[registryCleaned]; ok {
c := fixAuthConfig(creds, registryCleaned)
return c, nil
}
}
// remove http:// from user input and try again
if strings.HasPrefix(registry, "http://") {
if creds, ok := authConfigs[strings.TrimPrefix(registry, "http://")]; ok {
return creds, nil
registryCleaned := strings.TrimPrefix(registry, "http://")
if creds, ok := authConfigs[registryCleaned]; ok {
c := fixAuthConfig(creds, registryCleaned)
return c, nil
}
}
// add https:// to user input and try again
// see https://github.com/genuinetools/reg/issues/32
if !strings.HasPrefix(registry, "https://") && !strings.HasPrefix(registry, "http://") {
if creds, ok := authConfigs["https://"+registry]; ok {
return creds, nil
registryCleaned := "https://" + registry
if creds, ok := authConfigs[registryCleaned]; ok {
c := fixAuthConfig(creds, registryCleaned)
return c, nil
}
}
@ -93,7 +101,8 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
// found in the auth config.
for _, creds := range authConfigs {
fmt.Printf("No registry passed. Using registry %q\n", creds.ServerAddress)
return creds, nil
c := fixAuthConfig(creds, creds.ServerAddress)
return c, nil
}
// Don't use any authentication.
@ -102,6 +111,26 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
return types.AuthConfig{}, nil
}
// fixAuthConfig overwrites the AuthConfig's ServerAddress field with the
// registry value if ServerAddress is empty. For example, config.Load() will
// return AuthConfigs with empty ServerAddresses if the configuration file
// contains only an "credsHelper" object.
func fixAuthConfig(creds clitypes.AuthConfig, registry string) (c types.AuthConfig) {
c.Username = creds.Username
c.Password = creds.Password
c.Auth = creds.Auth
c.Email = creds.Email
c.IdentityToken = creds.IdentityToken
c.RegistryToken = creds.RegistryToken
c.ServerAddress = creds.ServerAddress
if creds.ServerAddress == "" {
c.ServerAddress = registry
}
return c
}
// GetRepoAndRef parses the repo name and reference.
func GetRepoAndRef(image string) (repo, ref string, err error) {
if image == "" {

View file

@ -7,8 +7,8 @@ import (
"strings"
"testing"
"github.com/docker/cli/cli/config"
"github.com/docker/distribution/reference"
"github.com/docker/docker-ce/components/cli/cli/config"
"github.com/docker/docker/api/types"
"github.com/google/go-cmp/cmp"
)
@ -35,7 +35,7 @@ func TestGetAuthConfig(t *testing.T) {
{
name: "invalid config dir",
configdir: "testdata/invalid",
err: errors.New("Loading config file failed: "),
err: errors.New("loading config file failed: "),
config: types.AuthConfig{},
},
{

View file

@ -61,7 +61,7 @@ type serverCommand struct {
func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
// Create the registry client.
r, err := createRegistryClient(cmd.registryServer)
r, err := createRegistryClient(ctx, cmd.registryServer)
if err != nil {
return err
}
@ -127,7 +127,7 @@ func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
// Create the initial index.
logrus.Info("creating initial static index")
if err := rc.repositories(staticDir); err != nil {
if err := rc.repositories(ctx, staticDir); err != nil {
return fmt.Errorf("creating index failed: %v", err)
}
@ -142,7 +142,7 @@ func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
// Create more indexes every X minutes based off interval.
for range ticker.C {
logrus.Info("creating timer based static index")
if err := rc.repositories(staticDir); err != nil {
if err := rc.repositories(ctx, staticDir); err != nil {
logrus.Warnf("creating static index failed: %v", err)
}
}

View file

@ -33,12 +33,12 @@ func (cmd *tagsCommand) Run(ctx context.Context, args []string) error {
}
// Create the registry client.
r, err := createRegistryClient(image.Domain)
r, err := createRegistryClient(ctx, image.Domain)
if err != nil {
return err
}
tags, err := r.Tags(image.Path)
tags, err := r.Tags(ctx, image.Path)
if err != nil {
return err
}

View file

@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIRAKiO1JljWQroJypQxJGTyQYwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xODA2MDYxNjE4MzFaFw0xOTA2MDYxNjE4
MzFaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDIjs9fg/lVbozhiYuu7kswhXGTdLlbvRTpfCX/wNpeVi8AIWRLetsO
Q/L/Os3rz+3ejozFREZViqJ5hDODgzMCP93M7lAZhzhrgxwe8+ZYPhj3WHqAc+TK
fnW0SNa+gOYPDo98xx1eWQKSLZ8Qj+NjkbBnUu92/zkSKFIvR6RFZwRxrItGZ003
OtsSwDn5yVGfcbtJyXMrR1669LvInIRlz9ekumG7cGi8odSKl3xErmGTZYEzozeP
MuJLDVM0fh5oOOucsn1sJJXqTNzFXQqvmnXJp62jop9tJcioeyDJvqwWjyVQpwIN
ZiqGFFu8LDvba4t+PVwsqrMoUCWEq1yjAgMBAAGjVDBSMA4GA1UdDwEB/wQEAwIC
pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQT
MBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAbwh3lknBACmn
lAm5e/OUpFiGEwV3otRR7tLiQFFPGtQ3hI8kHCjWplDyA9dCiPrH3z84cV/fH5sK
4SfG40ucO4r9BVof7dnahl4dejafymxlicD87zUsWRJFIgAlJlpVBkjv9Xd2Fupr
PWpbT8fHxeaZ5+Q5vvF7j2qCv+8i3GNgVVFC86HFc0DHRT78a2EW9YkWKdkFUh1W
zvuBDwoYv4rVKlhyhrPHMIHGBwdb0meRghqRilSR0qOnLy3fyrKQYpB4WAsBRl+/
dFYS9krqSOeqhIZTYkQzI6wlxexZ6vcb7NRhxX33BaFQNmsJnW9oZ2sHkpJ/Ceoe
7QaSRcCbtQ==
MIIDAjCCAeqgAwIBAgIQD5bBaNvi21NmTHx8qe8gQjANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQKEwdBY21lIENvMB4XDTE5MDcyNTAwNDQwOFoXDTIwMDcyNDAwNDQw
OFowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAOSu1L+Cvn/WbcRO71oWVzjvhhkxPjVYUDHwWIq4c354GjapYYsMD7G5
mabMpxm7IER9Xa18+RRvFReykzwYmHWKTBisXvmndUd+Dtwo5sumStNyxmz+lAHr
fJn/JbQP+7RjY5wL/6oO5u4hTEtQB+QY3gjoHVcx12pBY7MohHzqG3nLTRYdshi9
As8NZhKOZ11Rm+mx+YzkUENc0WDmUOyaWKhCtZ7iz6F+g1qoAIny2EzTSpsXdDkl
3b+kc1C0WNbFRu9IkIoedOQbxZlZpx+C0eO1RMDnCDJTVOHjKwPoGoyyIrHCS433
8AjxircOmJg6yY8J4ieveKwUExVE4bMCAwEAAaNUMFIwDgYDVR0PAQH/BAQDAgKk
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0RBBMw
EYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCsPLe9GuLwxmhR
5c2FcdsfxutFfFMPQr1LV0IQx/sOw36Lt6jq1S3i9Opea3Tj8nPontnwPVYJMAIY
qdjlOMqXPEcrp3uoCUp6JHk0P1j82xFeT3dgHitN5rGLZH1/4e9zTtYyvR/xivKs
yif8BU7lwES6pcFas3OCqN4RRBPjTzvz2P5/zLsi8bHmBdakKMSiSrvmY2BsmKYv
fjT7YJzkgpIsoK4qts5agI3UhkrsNLBZOXhZ21nwN0Vts4W1pkJ/8ATJPJRceJ3k
61n6UYX6PiqAeAN6L1lUx46eUrRY8daXmIe9Fi9adzqtYOvfinZ56EcnkXDtPGSt
+ojNIRcf
-----END CERTIFICATE-----

View file

@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyI7PX4P5VW6M4YmLru5LMIVxk3S5W70U6Xwl/8DaXlYvACFk
S3rbDkPy/zrN68/t3o6MxURGVYqieYQzg4MzAj/dzO5QGYc4a4McHvPmWD4Y91h6
gHPkyn51tEjWvoDmDw6PfMcdXlkCki2fEI/jY5GwZ1Lvdv85EihSL0ekRWcEcayL
RmdNNzrbEsA5+clRn3G7SclzK0deuvS7yJyEZc/XpLphu3BovKHUipd8RK5hk2WB
M6M3jzLiSw1TNH4eaDjrnLJ9bCSV6kzcxV0Kr5p1yaeto6KfbSXIqHsgyb6sFo8l
UKcCDWYqhhRbvCw722uLfj1cLKqzKFAlhKtcowIDAQABAoIBABrnzboqolBrvEql
gS++mCeyP0Jg7lz4SM3p8c8VuDXfqf6CfEoD+U84nmjPIsD37BlnVktAlY70Qke7
DI4gE7/Bgaf0cJp8IX2K4ULlxYkhC4hjPkvtQExKGtBM1UJJWJO8iFNdAvudRVMG
+8flBcRdzySNY8K7CpT9A8mD+u20X1n5yQt4wiBlda2IqEgDmTOJ1pxq99bR+nVu
Z1jglgKvVgSuL0JFR3flT2zcRZlRiF3LESfuTy46VXdRGm5wX8rlQaxntsq3BCIs
d5+Sy0ajnESZlr4FlHN4h0aNpchntLGKtU5lmyraomRHiD2pxJ3ZM+A5veDVGx9J
Y+hJ+gECgYEA0zhPogEmIYVNl1l9LA2Hz2GXvS2hNCswLL/lxUXSrFrbdpl0lXGE
d2hgnIFA8CjdMj3P/3VTY71SiDYROEuGbOB87CRpDGdmkpFueIrxbgW92divamVj
7gIV9Jdm4hKKALANPDF3JeF7j4M1nNDnRJPQl8KR2x7c7LniVvJ3fAMCgYEA8xPT
n0aZhkZSLueqT1v0IzpE2Vs82o0y4LPBBaxA6Hx5IkQ4Z+D3ifyH0baJfUVOOV7N
NbfOGzMzZ0+6IelaC5UZCl6fiSxn2/IgByr52tEHJv/wthfqw1Lskxp8ebS3bszI
/ZaZkZ4e3TKRC6M7lP2YG0qH626emzWxf8nvyuECgYATLfbVMCOFQbSE/MRH/saJ
R0RfEkikExPhzF8R5cA2lF1/THnwpAkySpETRQ1fgWZsjH5ZpQ64bNWUtswjf4Aq
XMwbKUc8sBr5Tilo2r0Hj4/ouytajvBfCWNy/ViDSMmtPE9HWvqFvw7YPkLdBX8q
k/2J+koCSrAm8s4htQyyfwKBgQCRcuirIsSUqxlcBbVL/TrNpX0zDjwQjnLGL+ks
6tCADJMzJN0Xk26re5cNrosAkWroO0jRfuVuMynsBLHcvtPpoFK2eL4/h3myC2SM
xXNyMqdz96viWddY/xKeRzf6X19vhkwyKV5E2vee5jYSX580XLYahnNkNfHj77IB
RCUfIQKBgQDHtgkK1nRyh3Vv7IpFFGXVOd4HiJ12nWFyXpS1mjzdQ5ARIrEFNz2Z
y13bfrfjbEkVWXeCDBzkICWMnRBDtQc1hb6APfodUlakpLyxvFCA5SzygXvrK+MK
aDPwY39N3pu0mvfM2Zs2LEXnAIp9VUvWCP+S4GvmHKbp+2tr6PtgkQ==
MIIEogIBAAKCAQEA5K7Uv4K+f9ZtxE7vWhZXOO+GGTE+NVhQMfBYirhzfngaNqlh
iwwPsbmZpsynGbsgRH1drXz5FG8VF7KTPBiYdYpMGKxe+ad1R34O3Cjmy6ZK03LG
bP6UAet8mf8ltA/7tGNjnAv/qg7m7iFMS1AH5BjeCOgdVzHXakFjsyiEfOobectN
Fh2yGL0Czw1mEo5nXVGb6bH5jORQQ1zRYOZQ7JpYqEK1nuLPoX6DWqgAifLYTNNK
mxd0OSXdv6RzULRY1sVG70iQih505BvFmVmnH4LR47VEwOcIMlNU4eMrA+gajLIi
scJLjffwCPGKtw6YmDrJjwniJ694rBQTFUThswIDAQABAoIBACC69TBD4kghxKwo
MBHQyMMZZTQN3D1+WlwLbmDMbZtYTUgtjgx8uXJSgi9hczk3kzK6TsIZ9Px7uaUe
pfwf+RI5rRKK+Tz6Psrwu+e+4uma73K8JrJK7ZwovDi+UAMplnkHEKaxAPKyF1NV
6LmOxtJazMyxjCGCvLY0jbSCxBOsmNpfVULFV61Sx0xbQDniUPTFxrCAbBxRCNAd
QJten23Ib4J/CtCOtVOiT3ZZKk4vkhP7+AyDd88iVKQrHUefmTzzDQ8Q2QG4NtSx
6HK3+pOlNdAd9r/kbLfCWGmJ8Mt/13TYin+iUeW5iTKHqJMhVweBHsElt+/aMQcZ
ePgimAECgYEA+11MSRqRea5h33l+Mk+qTNquwnyvp+cwshEhQIkHZMbonRWX41Fh
hPzUE22AGAky9XrjkwA0ljvkcCCKPrmg6a8Y7VyN8z5qD92p4CfuRoHH6ZK4z4ob
zxKBFy1wxkoe/vhcqxWBaZGFAbnWtTuCkNG1JUdeLL4oAy1eBJ5tuLMCgYEA6OZz
4/BXbNpLunJrYQkEVwO1NE0MBKnRm8NezrmLATSnb4L8jYmdg8iXk2NeQZoidToi
6pLSJ9NGyJmKnAHRlQV+4YoNeKd7ZMgy3/evWp9Dp97pRnB1EpUz8S5S46Y6ICVP
QzRpc52Y132yRmVVYyBdFB2gwx/kOmZMYSuGswECgYBZjN+Em8aABzQvHWVsHx2Y
y7++4hTiSGb8yUWDbE4KBSMR84NtJK0NZ9VWJoigB4b0FZfF4yjY7PVxTOZzekZt
ekfzKCoaIn4+sMW4agFldQ73k6HrrlRB7WG4FCQ9QGLSi0DYYoz8Qy+TcdWgM1dM
qKlUYpyWbBhtKVh5ZMfKKQKBgCr4ulFIOxk+fdJ84gsZWs+jQ3/O53TE0IPo3ElQ
OrGSQYJ1rJuA1KtIC4+KOg3onor6mw6k4FmwxdKlYcm/HChexzZd/nZQkJfbyGdC
j/wsfpMusePpQ/CVzuulHG4ccYUU3TcNKJfIcDbyXj5W3BZaFlJr1QmrjLSpw1X6
bfQBAoGAbIO/OfsvAyjCo7xtceVW3PbbEmPgrfwLFqzTb40mEIQzxjtSrz5u4U2e
/jYm4340ihr8cCCKcPC+KydJ3GqxHWUS7QUVeknJ7ZmwUA7LTVokWZgdWQGvrAlp
NtUJP5v4YrnGcPT/PJcYvIRgJHKyEsQUuQHoXP/9zK4/5wicyq4=
-----END RSA PRIVATE KEY-----

View file

@ -33,7 +33,7 @@ import (
func StartRegistry(dcli *client.Client, config, username, password string) (string, string, error) {
_, filename, _, ok := runtime.Caller(0)
if !ok {
return "", "", errors.New("No caller information")
return "", "", errors.New("no caller information")
}
image := "registry:2"
@ -126,7 +126,7 @@ func startClairDB(dcli *client.Client) (string, error) {
func StartClair(dcli *client.Client) (string, string, error) {
_, filename, _, ok := runtime.Caller(0)
if !ok {
return "", "", errors.New("No caller information")
return "", "", errors.New("no caller information")
}
// start the database container.
@ -284,7 +284,7 @@ func imageExists(dcli *client.Client, image string) (bool, error) {
func waitForConn(addr, cert, key string) error {
tlsCert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err)
return fmt.Errorf("could not load X509 key pair: %v. Make sure the key is not encrypted", err)
}
certPool, err := x509.SystemCertPool()

12
vendor/github.com/Azure/go-ansiterm/README.md generated vendored Normal file
View file

@ -0,0 +1,12 @@
# go-ansiterm
This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent.
For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position.
The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
See parser_test.go for examples exercising the state machine and generating appropriate function calls.
-----
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

1
vendor/github.com/Microsoft/go-winio/.gitignore generated vendored Normal file
View file

@ -0,0 +1 @@
*.exe

22
vendor/github.com/Microsoft/go-winio/README.md generated vendored Normal file
View file

@ -0,0 +1,22 @@
# go-winio
This repository contains utilities for efficiently performing Win32 IO operations in
Go. Currently, this is focused on accessing named pipes and other file handles, and
for using named pipes as a net transport.
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
newer operating systems. This is similar to the implementation of network sockets in Go's net
package.
Please see the LICENSE file for licensing information.
This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
see the [Code of Conduct
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
questions or comments.
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
for another named pipe implementation.

View file

@ -1,27 +0,0 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -16,6 +16,7 @@ import (
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
type atomicBool int32
@ -79,6 +80,7 @@ type win32File struct {
wg sync.WaitGroup
wgLock sync.RWMutex
closing atomicBool
socket bool
readDeadline deadlineHandler
writeDeadline deadlineHandler
}
@ -109,7 +111,13 @@ func makeWin32File(h syscall.Handle) (*win32File, error) {
}
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
return makeWin32File(h)
// If we return the result of makeWin32File directly, it can result in an
// interface-wrapped nil, rather than a nil interface value.
f, err := makeWin32File(h)
if err != nil {
return nil, err
}
return f, nil
}
// closeHandle closes the resources associated with a Win32 handle
@ -190,6 +198,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er
if f.closing.isSet() {
err = ErrFileClosed
}
} else if err != nil && f.socket {
// err is from Win32. Query the overlapped structure to get the winsock error.
var bytes, flags uint32
err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
}
case <-timeout:
cancelIoEx(f.handle, &c.o)
@ -265,6 +277,10 @@ func (f *win32File) Flush() error {
return syscall.FlushFileBuffers(f.handle)
}
func (f *win32File) Fd() uintptr {
return uintptr(f.handle)
}
func (d *deadlineHandler) set(deadline time.Time) error {
d.setLock.Lock()
defer d.setLock.Unlock()

9
vendor/github.com/Microsoft/go-winio/go.mod generated vendored Normal file
View file

@ -0,0 +1,9 @@
module github.com/Microsoft/go-winio
go 1.12
require (
github.com/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.1
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b
)

16
vendor/github.com/Microsoft/go-winio/go.sum generated vendored Normal file
View file

@ -0,0 +1,16 @@
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/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

305
vendor/github.com/Microsoft/go-winio/hvsock.go generated vendored Normal file
View file

@ -0,0 +1,305 @@
package winio
import (
"fmt"
"io"
"net"
"os"
"syscall"
"time"
"unsafe"
"github.com/Microsoft/go-winio/pkg/guid"
)
//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
const (
afHvSock = 34 // AF_HYPERV
socketError = ^uintptr(0)
)
// An HvsockAddr is an address for a AF_HYPERV socket.
type HvsockAddr struct {
VMID guid.GUID
ServiceID guid.GUID
}
type rawHvsockAddr struct {
Family uint16
_ uint16
VMID guid.GUID
ServiceID guid.GUID
}
// Network returns the address's network name, "hvsock".
func (addr *HvsockAddr) Network() string {
return "hvsock"
}
func (addr *HvsockAddr) String() string {
return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
}
// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
func VsockServiceID(port uint32) guid.GUID {
g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3")
g.Data1 = port
return g
}
func (addr *HvsockAddr) raw() rawHvsockAddr {
return rawHvsockAddr{
Family: afHvSock,
VMID: addr.VMID,
ServiceID: addr.ServiceID,
}
}
func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
addr.VMID = raw.VMID
addr.ServiceID = raw.ServiceID
}
// HvsockListener is a socket listener for the AF_HYPERV address family.
type HvsockListener struct {
sock *win32File
addr HvsockAddr
}
// HvsockConn is a connected socket of the AF_HYPERV address family.
type HvsockConn struct {
sock *win32File
local, remote HvsockAddr
}
func newHvSocket() (*win32File, error) {
fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1)
if err != nil {
return nil, os.NewSyscallError("socket", err)
}
f, err := makeWin32File(fd)
if err != nil {
syscall.Close(fd)
return nil, err
}
f.socket = true
return f, nil
}
// ListenHvsock listens for connections on the specified hvsock address.
func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
l := &HvsockListener{addr: *addr}
sock, err := newHvSocket()
if err != nil {
return nil, l.opErr("listen", err)
}
sa := addr.raw()
err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa)))
if err != nil {
return nil, l.opErr("listen", os.NewSyscallError("socket", err))
}
err = syscall.Listen(sock.handle, 16)
if err != nil {
return nil, l.opErr("listen", os.NewSyscallError("listen", err))
}
return &HvsockListener{sock: sock, addr: *addr}, nil
}
func (l *HvsockListener) opErr(op string, err error) error {
return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
}
// Addr returns the listener's network address.
func (l *HvsockListener) Addr() net.Addr {
return &l.addr
}
// Accept waits for the next connection and returns it.
func (l *HvsockListener) Accept() (_ net.Conn, err error) {
sock, err := newHvSocket()
if err != nil {
return nil, l.opErr("accept", err)
}
defer func() {
if sock != nil {
sock.Close()
}
}()
c, err := l.sock.prepareIo()
if err != nil {
return nil, l.opErr("accept", err)
}
defer l.sock.wg.Done()
// AcceptEx, per documentation, requires an extra 16 bytes per address.
const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
var addrbuf [addrlen * 2]byte
var bytes uint32
err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o)
_, err = l.sock.asyncIo(c, nil, bytes, err)
if err != nil {
return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
}
conn := &HvsockConn{
sock: sock,
}
conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
sock = nil
return conn, nil
}
// Close closes the listener, causing any pending Accept calls to fail.
func (l *HvsockListener) Close() error {
return l.sock.Close()
}
/* Need to finish ConnectEx handling
func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) {
sock, err := newHvSocket()
if err != nil {
return nil, err
}
defer func() {
if sock != nil {
sock.Close()
}
}()
c, err := sock.prepareIo()
if err != nil {
return nil, err
}
defer sock.wg.Done()
var bytes uint32
err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o)
_, err = sock.asyncIo(ctx, c, nil, bytes, err)
if err != nil {
return nil, err
}
conn := &HvsockConn{
sock: sock,
remote: *addr,
}
sock = nil
return conn, nil
}
*/
func (conn *HvsockConn) opErr(op string, err error) error {
return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
}
func (conn *HvsockConn) Read(b []byte) (int, error) {
c, err := conn.sock.prepareIo()
if err != nil {
return 0, conn.opErr("read", err)
}
defer conn.sock.wg.Done()
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
var flags, bytes uint32
err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err)
if err != nil {
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("wsarecv", err)
}
return 0, conn.opErr("read", err)
} else if n == 0 {
err = io.EOF
}
return n, err
}
func (conn *HvsockConn) Write(b []byte) (int, error) {
t := 0
for len(b) != 0 {
n, err := conn.write(b)
if err != nil {
return t + n, err
}
t += n
b = b[n:]
}
return t, nil
}
func (conn *HvsockConn) write(b []byte) (int, error) {
c, err := conn.sock.prepareIo()
if err != nil {
return 0, conn.opErr("write", err)
}
defer conn.sock.wg.Done()
buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))}
var bytes uint32
err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err)
if err != nil {
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("wsasend", err)
}
return 0, conn.opErr("write", err)
}
return n, err
}
// Close closes the socket connection, failing any pending read or write calls.
func (conn *HvsockConn) Close() error {
return conn.sock.Close()
}
func (conn *HvsockConn) shutdown(how int) error {
err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD)
if err != nil {
return os.NewSyscallError("shutdown", err)
}
return nil
}
// CloseRead shuts down the read end of the socket.
func (conn *HvsockConn) CloseRead() error {
err := conn.shutdown(syscall.SHUT_RD)
if err != nil {
return conn.opErr("close", err)
}
return nil
}
// CloseWrite shuts down the write end of the socket, notifying the other endpoint that
// no more data will be written.
func (conn *HvsockConn) CloseWrite() error {
err := conn.shutdown(syscall.SHUT_WR)
if err != nil {
return conn.opErr("close", err)
}
return nil
}
// LocalAddr returns the local address of the connection.
func (conn *HvsockConn) LocalAddr() net.Addr {
return &conn.local
}
// RemoteAddr returns the remote address of the connection.
func (conn *HvsockConn) RemoteAddr() net.Addr {
return &conn.remote
}
// SetDeadline implements the net.Conn SetDeadline method.
func (conn *HvsockConn) SetDeadline(t time.Time) error {
conn.SetReadDeadline(t)
conn.SetWriteDeadline(t)
return nil
}
// SetReadDeadline implements the net.Conn SetReadDeadline method.
func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
return conn.sock.SetReadDeadline(t)
}
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
return conn.sock.SetWriteDeadline(t)
}

View file

@ -3,10 +3,13 @@
package winio
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"runtime"
"syscall"
"time"
"unsafe"
@ -18,6 +21,48 @@ import (
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile
//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U
//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl
type ioStatusBlock struct {
Status, Information uintptr
}
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
ObjectName *unicodeString
Attributes uintptr
SecurityDescriptor *securityDescriptor
SecurityQoS uintptr
}
type unicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type securityDescriptor struct {
Revision byte
Sbz1 byte
Control uint16
Owner uintptr
Group uintptr
Sacl uintptr
Dacl uintptr
}
type ntstatus int32
func (status ntstatus) Err() error {
if status >= 0 {
return nil
}
return rtlNtStatusToDosError(status)
}
const (
cERROR_PIPE_BUSY = syscall.Errno(231)
@ -25,21 +70,20 @@ const (
cERROR_PIPE_CONNECTED = syscall.Errno(535)
cERROR_SEM_TIMEOUT = syscall.Errno(121)
cPIPE_ACCESS_DUPLEX = 0x3
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000
cSECURITY_SQOS_PRESENT = 0x100000
cSECURITY_ANONYMOUS = 0
cPIPE_REJECT_REMOTE_CLIENTS = 0x8
cPIPE_UNLIMITED_INSTANCES = 255
cNMPWAIT_USE_DEFAULT_WAIT = 0
cNMPWAIT_NOWAIT = 1
cPIPE_TYPE_MESSAGE = 4
cPIPE_READMODE_MESSAGE = 2
cFILE_OPEN = 1
cFILE_CREATE = 2
cFILE_PIPE_MESSAGE_TYPE = 1
cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2
cSE_DACL_PRESENT = 4
)
var (
@ -137,9 +181,30 @@ func (s pipeAddress) String() string {
return string(s)
}
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
for {
select {
case <-ctx.Done():
return syscall.Handle(0), ctx.Err()
default:
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err == nil {
return h, nil
}
if err != cERROR_PIPE_BUSY {
return h, &os.PathError{Err: err, Op: "open", Path: *path}
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(time.Millisecond * 10)
}
}
}
// DialPipe connects to a named pipe by path, timing out if the connection
// takes longer than the specified duration. If timeout is nil, then we use
// a default timeout of 5 seconds. (We do not use WaitNamedPipe.)
// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
var absTimeout time.Time
if timeout != nil {
@ -147,23 +212,22 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
} else {
absTimeout = time.Now().Add(time.Second * 2)
}
var err error
var h syscall.Handle
for {
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err != cERROR_PIPE_BUSY {
break
}
if time.Now().After(absTimeout) {
ctx, _ := context.WithDeadline(context.Background(), absTimeout)
conn, err := DialPipeContext(ctx, path)
if err == context.DeadlineExceeded {
return nil, ErrTimeout
}
return conn, err
}
// Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds.
time.Sleep(time.Millisecond * 10)
}
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout.
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
var err error
var h syscall.Handle
h, err = tryDialPipe(ctx, &path)
if err != nil {
return nil, &os.PathError{Op: "open", Path: path, Err: err}
return nil, err
}
var flags uint32
@ -196,41 +260,85 @@ type acceptResponse struct {
type win32PipeListener struct {
firstHandle syscall.Handle
path string
securityDescriptor []byte
config PipeConfig
acceptCh chan (chan acceptResponse)
closeCh chan int
doneCh chan int
}
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED
if first {
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE
}
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS
if c.MessageMode {
mode |= cPIPE_TYPE_MESSAGE
}
sa := &syscall.SecurityAttributes{}
sa.Length = uint32(unsafe.Sizeof(*sa))
if securityDescriptor != nil {
len := uint32(len(securityDescriptor))
sa.SecurityDescriptor = localAlloc(0, len)
defer localFree(sa.SecurityDescriptor)
copy((*[0xffff]byte)(unsafe.Pointer(sa.SecurityDescriptor))[:], securityDescriptor)
}
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, sa)
func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
path16, err := syscall.UTF16FromString(path)
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
var oa objectAttributes
oa.Length = unsafe.Sizeof(oa)
var ntPath unicodeString
if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
defer localFree(ntPath.Buffer)
oa.ObjectName = &ntPath
// The security descriptor is only needed for the first pipe.
if first {
if sd != nil {
len := uint32(len(sd))
sdb := localAlloc(0, len)
defer localFree(sdb)
copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
} else {
// Construct the default named pipe security descriptor.
var dacl uintptr
if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
return 0, fmt.Errorf("getting default named pipe ACL: %s", err)
}
defer localFree(dacl)
sdb := &securityDescriptor{
Revision: 1,
Control: cSE_DACL_PRESENT,
Dacl: dacl,
}
oa.SecurityDescriptor = sdb
}
}
typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS)
if c.MessageMode {
typ |= cFILE_PIPE_MESSAGE_TYPE
}
disposition := uint32(cFILE_OPEN)
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE)
if first {
disposition = cFILE_CREATE
// By not asking for read or write access, the named pipe file system
// will put this pipe into an initially disconnected state, blocking
// client connections until the next call with first == false.
access = syscall.SYNCHRONIZE
}
timeout := int64(-50 * 10000) // 50ms
var (
h syscall.Handle
iosb ioStatusBlock
)
err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err()
if err != nil {
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
runtime.KeepAlive(ntPath)
return h, nil
}
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false)
h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
if err != nil {
return nil, err
}
@ -341,28 +449,9 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
if err != nil {
return nil, err
}
// Create a client handle and connect it. This results in the pipe
// instance always existing, so that clients see ERROR_PIPE_BUSY
// rather than ERROR_FILE_NOT_FOUND. This ties the first instance
// up so that no other instances can be used. This would have been
// cleaner if the Win32 API matched CreateFile with ConnectNamedPipe
// instead of CreateNamedPipe. (Apparently created named pipes are
// considered to be in listening state regardless of whether any
// active calls to ConnectNamedPipe are outstanding.)
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err != nil {
syscall.Close(h)
return nil, err
}
// Close the client handle. The server side of the instance will
// still be busy, leading to ERROR_PIPE_BUSY instead of
// ERROR_NOT_FOUND, as long as we don't close the server handle,
// or disconnect the client with DisconnectNamedPipe.
syscall.Close(h2)
l := &win32PipeListener{
firstHandle: h,
path: path,
securityDescriptor: sd,
config: *c,
acceptCh: make(chan (chan acceptResponse)),
closeCh: make(chan int),

235
vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go generated vendored Normal file
View file

@ -0,0 +1,235 @@
// Package guid provides a GUID type. The backing structure for a GUID is
// identical to that used by the golang.org/x/sys/windows GUID type.
// There are two main binary encodings used for a GUID, the big-endian encoding,
// and the Windows (mixed-endian) encoding. See here for details:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
package guid
import (
"crypto/rand"
"crypto/sha1"
"encoding"
"encoding/binary"
"fmt"
"strconv"
"golang.org/x/sys/windows"
)
// Variant specifies which GUID variant (or "type") of the GUID. It determines
// how the entirety of the rest of the GUID is interpreted.
type Variant uint8
// The variants specified by RFC 4122.
const (
// VariantUnknown specifies a GUID variant which does not conform to one of
// the variant encodings specified in RFC 4122.
VariantUnknown Variant = iota
VariantNCS
VariantRFC4122
VariantMicrosoft
VariantFuture
)
// Version specifies how the bits in the GUID were generated. For instance, a
// version 4 GUID is randomly generated, and a version 5 is generated from the
// hash of an input string.
type Version uint8
var _ = (encoding.TextMarshaler)(GUID{})
var _ = (encoding.TextUnmarshaler)(&GUID{})
// GUID represents a GUID/UUID. It has the same structure as
// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
// that type. It is defined as its own type so that stringification and
// marshaling can be supported. The representation matches that used by native
// Windows code.
type GUID windows.GUID
// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
func NewV4() (GUID, error) {
var b [16]byte
if _, err := rand.Read(b[:]); err != nil {
return GUID{}, err
}
g := FromArray(b)
g.setVersion(4) // Version 4 means randomly generated.
g.setVariant(VariantRFC4122)
return g, nil
}
// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
// and the sample code treats it as a series of bytes, so we do the same here.
//
// Some implementations, such as those found on Windows, treat the name as a
// big-endian UTF16 stream of bytes. If that is desired, the string can be
// encoded as such before being passed to this function.
func NewV5(namespace GUID, name []byte) (GUID, error) {
b := sha1.New()
namespaceBytes := namespace.ToArray()
b.Write(namespaceBytes[:])
b.Write(name)
a := [16]byte{}
copy(a[:], b.Sum(nil))
g := FromArray(a)
g.setVersion(5) // Version 5 means generated from a string.
g.setVariant(VariantRFC4122)
return g, nil
}
func fromArray(b [16]byte, order binary.ByteOrder) GUID {
var g GUID
g.Data1 = order.Uint32(b[0:4])
g.Data2 = order.Uint16(b[4:6])
g.Data3 = order.Uint16(b[6:8])
copy(g.Data4[:], b[8:16])
return g
}
func (g GUID) toArray(order binary.ByteOrder) [16]byte {
b := [16]byte{}
order.PutUint32(b[0:4], g.Data1)
order.PutUint16(b[4:6], g.Data2)
order.PutUint16(b[6:8], g.Data3)
copy(b[8:16], g.Data4[:])
return b
}
// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
func FromArray(b [16]byte) GUID {
return fromArray(b, binary.BigEndian)
}
// ToArray returns an array of 16 bytes representing the GUID in big-endian
// encoding.
func (g GUID) ToArray() [16]byte {
return g.toArray(binary.BigEndian)
}
// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
func FromWindowsArray(b [16]byte) GUID {
return fromArray(b, binary.LittleEndian)
}
// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
// encoding.
func (g GUID) ToWindowsArray() [16]byte {
return g.toArray(binary.LittleEndian)
}
func (g GUID) String() string {
return fmt.Sprintf(
"%08x-%04x-%04x-%04x-%012x",
g.Data1,
g.Data2,
g.Data3,
g.Data4[:2],
g.Data4[2:])
}
// FromString parses a string containing a GUID and returns the GUID. The only
// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
// format.
func FromString(s string) (GUID, error) {
if len(s) != 36 {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
var g GUID
data1, err := strconv.ParseUint(s[0:8], 16, 32)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data1 = uint32(data1)
data2, err := strconv.ParseUint(s[9:13], 16, 16)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data2 = uint16(data2)
data3, err := strconv.ParseUint(s[14:18], 16, 16)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data3 = uint16(data3)
for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
v, err := strconv.ParseUint(s[x:x+2], 16, 8)
if err != nil {
return GUID{}, fmt.Errorf("invalid GUID %q", s)
}
g.Data4[i] = uint8(v)
}
return g, nil
}
func (g *GUID) setVariant(v Variant) {
d := g.Data4[0]
switch v {
case VariantNCS:
d = (d & 0x7f)
case VariantRFC4122:
d = (d & 0x3f) | 0x80
case VariantMicrosoft:
d = (d & 0x1f) | 0xc0
case VariantFuture:
d = (d & 0x0f) | 0xe0
case VariantUnknown:
fallthrough
default:
panic(fmt.Sprintf("invalid variant: %d", v))
}
g.Data4[0] = d
}
// Variant returns the GUID variant, as defined in RFC 4122.
func (g GUID) Variant() Variant {
b := g.Data4[0]
if b&0x80 == 0 {
return VariantNCS
} else if b&0xc0 == 0x80 {
return VariantRFC4122
} else if b&0xe0 == 0xc0 {
return VariantMicrosoft
} else if b&0xe0 == 0xe0 {
return VariantFuture
}
return VariantUnknown
}
func (g *GUID) setVersion(v Version) {
g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
}
// Version returns the GUID version, as defined in RFC 4122.
func (g GUID) Version() Version {
return Version((g.Data3 & 0xF000) >> 12)
}
// MarshalText returns the textual representation of the GUID.
func (g GUID) MarshalText() ([]byte, error) {
return []byte(g.String()), nil
}
// UnmarshalText takes the textual representation of a GUID, and unmarhals it
// into this GUID.
func (g *GUID) UnmarshalText(text []byte) error {
g2, err := FromString(string(text))
if err != nil {
return err
}
*g = g2
return nil
}

View file

@ -1,3 +1,3 @@
package winio
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go

View file

@ -1,4 +1,4 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
// Code generated by 'go generate'; DO NOT EDIT.
package winio
@ -38,19 +38,25 @@ func errnoErr(e syscall.Errno) error {
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
modntdll = windows.NewLazySystemDLL("ntdll.dll")
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
procCreateFileW = modkernel32.NewProc("CreateFileW")
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW")
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
@ -69,6 +75,7 @@ var (
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
procBackupRead = modkernel32.NewProc("BackupRead")
procBackupWrite = modkernel32.NewProc("BackupWrite")
procbind = modws2_32.NewProc("bind")
)
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
@ -120,6 +127,24 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro
return
}
func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
var _p0 uint32
if wait {
_p0 = 1
} else {
_p0 = 0
}
r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
@ -176,27 +201,6 @@ func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityA
return
}
func waitNamedPipe(name string, timeout uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(name)
if err != nil {
return
}
return _waitNamedPipe(_p0, timeout)
}
func _waitNamedPipe(name *uint16, timeout uint32) (err error) {
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
if r1 == 0 {
@ -227,6 +231,32 @@ func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
return
}
func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) {
r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0)
status = ntstatus(r0)
return
}
func rtlNtStatusToDosError(status ntstatus) (winerr error) {
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
if r0 != 0 {
winerr = syscall.Errno(r0)
}
return
}
func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0)
status = ntstatus(r0)
return
}
func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) {
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0)
status = ntstatus(r0)
return
}
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(accountName)
@ -518,3 +548,15 @@ func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, p
}
return
}
func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
if r1 == socketError {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}

View file

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Simon Eskildsen
Copyright (c) 2015 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -9,13 +9,13 @@ 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 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.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,51 @@
package osversion
import (
"fmt"
"golang.org/x/sys/windows"
)
// OSVersion is a wrapper for Windows version information
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
type OSVersion struct {
Version uint32
MajorVersion uint8
MinorVersion uint8
Build uint16
}
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
type osVersionInfoEx struct {
OSVersionInfoSize uint32
MajorVersion uint32
MinorVersion uint32
BuildNumber uint32
PlatformID uint32
CSDVersion [128]uint16
ServicePackMajor uint16
ServicePackMinor uint16
SuiteMask uint16
ProductType byte
Reserve byte
}
// Get gets the operating system version on Windows.
// The calling application must be manifested to get the correct version information.
func Get() OSVersion {
var err error
osv := OSVersion{}
osv.Version, err = windows.GetVersion()
if err != nil {
// GetVersion never fails.
panic(err)
}
osv.MajorVersion = uint8(osv.Version & 0xFF)
osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF)
osv.Build = uint16(osv.Version >> 16)
return osv
}
func (osv OSVersion) ToString() string {
return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build)
}

View file

@ -0,0 +1,10 @@
package osversion
const (
// RS2 was a client-only release in case you're asking why it's not in the list.
RS1 = 14393
RS3 = 16299
RS4 = 17134
RS5 = 17763
)

View file

@ -1,26 +0,0 @@
Copyright (c) 2012, Neal van Veen (nealvanveen@gmail.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.

View file

@ -1,514 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
// Boolean capabilities
var BoolAttr = [...]string{
"auto_left_margin", "bw",
"auto_right_margin", "am",
"no_esc_ctlc", "xsb",
"ceol_standout_glitch", "xhp",
"eat_newline_glitch", "xenl",
"erase_overstrike", "eo",
"generic_type", "gn",
"hard_copy", "hc",
"has_meta_key", "km",
"has_status_line", "hs",
"insert_null_glitch", "in",
"memory_above", "da",
"memory_below", "db",
"move_insert_mode", "mir",
"move_standout_mode", "msgr",
"over_strike", "os",
"status_line_esc_ok", "eslok",
"dest_tabs_magic_smso", "xt",
"tilde_glitch", "hz",
"transparent_underline", "ul",
"xon_xoff", "nxon",
"needs_xon_xoff", "nxon",
"prtr_silent", "mc5i",
"hard_cursor", "chts",
"non_rev_rmcup", "nrrmc",
"no_pad_char", "npc",
"non_dest_scroll_region", "ndscr",
"can_change", "ccc",
"back_color_erase", "bce",
"hue_lightness_saturation", "hls",
"col_addr_glitch", "xhpa",
"cr_cancels_micro_mode", "crxm",
"has_print_wheel", "daisy",
"row_addr_glitch", "xvpa",
"semi_auto_right_margin", "sam",
"cpi_changes_res", "cpix",
"lpi_changes_res", "lpix",
"backspaces_with_bs", "",
"crt_no_scrolling", "",
"no_correctly_working_cr", "",
"gnu_has_meta_key", "",
"linefeed_is_newline", "",
"has_hardware_tabs", "",
"return_does_clr_eol", "",
}
// Numerical capabilities
var NumAttr = [...]string{
"columns", "cols",
"init_tabs", "it",
"lines", "lines",
"lines_of_memory", "lm",
"magic_cookie_glitch", "xmc",
"padding_baud_rate", "pb",
"virtual_terminal", "vt",
"width_status_line", "wsl",
"num_labels", "nlab",
"label_height", "lh",
"label_width", "lw",
"max_attributes", "ma",
"maximum_windows", "wnum",
"max_colors", "colors",
"max_pairs", "pairs",
"no_color_video", "ncv",
"buffer_capacity", "bufsz",
"dot_vert_spacing", "spinv",
"dot_horz_spacing", "spinh",
"max_micro_address", "maddr",
"max_micro_jump", "mjump",
"micro_col_size", "mcs",
"micro_line_size", "mls",
"number_of_pins", "npins",
"output_res_char", "orc",
"output_res_line", "orl",
"output_res_horz_inch", "orhi",
"output_res_vert_inch", "orvi",
"print_rate", "cps",
"wide_char_size", "widcs",
"buttons", "btns",
"bit_image_entwining", "bitwin",
"bit_image_type", "bitype",
"magic_cookie_glitch_ul", "",
"carriage_return_delay", "",
"new_line_delay", "",
"backspace_delay", "",
"horizontal_tab_delay", "",
"number_of_function_keys", "",
}
// String capabilities
var StrAttr = [...]string{
"back_tab", "cbt",
"bell", "bel",
"carriage_return", "cr",
"change_scroll_region", "csr",
"clear_all_tabs", "tbc",
"clear_screen", "clear",
"clr_eol", "el",
"clr_eos", "ed",
"column_address", "hpa",
"command_character", "cmdch",
"cursor_address", "cup",
"cursor_down", "cud1",
"cursor_home", "home",
"cursor_invisible", "civis",
"cursor_left", "cub1",
"cursor_mem_address", "mrcup",
"cursor_normal", "cnorm",
"cursor_right", "cuf1",
"cursor_to_ll", "ll",
"cursor_up", "cuu1",
"cursor_visible", "cvvis",
"delete_character", "dch1",
"delete_line", "dl1",
"dis_status_line", "dsl",
"down_half_line", "hd",
"enter_alt_charset_mode", "smacs",
"enter_blink_mode", "blink",
"enter_bold_mode", "bold",
"enter_ca_mode", "smcup",
"enter_delete_mode", "smdc",
"enter_dim_mode", "dim",
"enter_insert_mode", "smir",
"enter_secure_mode", "invis",
"enter_protected_mode", "prot",
"enter_reverse_mode", "rev",
"enter_standout_mode", "smso",
"enter_underline_mode", "smul",
"erase_chars", "ech",
"exit_alt_charset_mode", "rmacs",
"exit_attribute_mode", "sgr0",
"exit_ca_mode", "rmcup",
"exit_delete_mode", "rmdc",
"exit_insert_mode", "rmir",
"exit_standout_mode", "rmso",
"exit_underline_mode", "rmul",
"flash_screen", "flash",
"form_feed", "ff",
"from_status_line", "fsl",
"init_1string", "is1",
"init_2string", "is2",
"init_3string", "is3",
"init_file", "if",
"insert_character", "ich1",
"insert_line", "il1",
"insert_padding", "ip",
"key_backspace", "kbs",
"key_catab", "ktbc",
"key_clear", "kclr",
"key_ctab", "kctab",
"key_dc", "kdch1",
"key_dl", "kdl1",
"key_down", "kcud1",
"key_eic", "krmir",
"key_eol", "kel",
"key_eos", "ked",
"key_f0", "kf0",
"key_f1", "kf1",
"key_f10", "kf10",
"key_f2", "kf2",
"key_f3", "kf3",
"key_f4", "kf4",
"key_f5", "kf5",
"key_f6", "kf6",
"key_f7", "kf7",
"key_f8", "kf8",
"key_f9", "kf9",
"key_home", "khome",
"key_ic", "kich1",
"key_il", "kil1",
"key_left", "kcub1",
"key_ll", "kll",
"key_npage", "knp",
"key_ppage", "kpp",
"key_right", "kcuf1",
"key_sf", "kind",
"key_sr", "kri",
"key_stab", "khts",
"key_up", "kcuu1",
"keypad_local", "rmkx",
"keypad_xmit", "smkx",
"lab_f0", "lf0",
"lab_f1", "lf1",
"lab_f10", "lf10",
"lab_f2", "lf2",
"lab_f3", "lf3",
"lab_f4", "lf4",
"lab_f5", "lf5",
"lab_f6", "lf6",
"lab_f7", "lf7",
"lab_f8", "lf8",
"lab_f9", "lf9",
"meta_off", "rmm",
"meta_on", "smm",
"newline", "_glitch",
"pad_char", "npc",
"parm_dch", "dch",
"parm_delete_line", "dl",
"parm_down_cursor", "cud",
"parm_ich", "ich",
"parm_index", "indn",
"parm_insert_line", "il",
"parm_left_cursor", "cub",
"parm_right_cursor", "cuf",
"parm_rindex", "rin",
"parm_up_cursor", "cuu",
"pkey_key", "pfkey",
"pkey_local", "pfloc",
"pkey_xmit", "pfx",
"print_screen", "mc0",
"prtr_off", "mc4",
"prtr_on", "mc5",
"repeat_char", "rep",
"reset_1string", "rs1",
"reset_2string", "rs2",
"reset_3string", "rs3",
"reset_file", "rf",
"restore_cursor", "rc",
"row_address", "mvpa",
"save_cursor", "row_address",
"scroll_forward", "ind",
"scroll_reverse", "ri",
"set_attributes", "sgr",
"set_tab", "hts",
"set_window", "wind",
"tab", "s_magic_smso",
"to_status_line", "tsl",
"underline_char", "uc",
"up_half_line", "hu",
"init_prog", "iprog",
"key_a1", "ka1",
"key_a3", "ka3",
"key_b2", "kb2",
"key_c1", "kc1",
"key_c3", "kc3",
"prtr_non", "mc5p",
"char_padding", "rmp",
"acs_chars", "acsc",
"plab_norm", "pln",
"key_btab", "kcbt",
"enter_xon_mode", "smxon",
"exit_xon_mode", "rmxon",
"enter_am_mode", "smam",
"exit_am_mode", "rmam",
"xon_character", "xonc",
"xoff_character", "xoffc",
"ena_acs", "enacs",
"label_on", "smln",
"label_off", "rmln",
"key_beg", "kbeg",
"key_cancel", "kcan",
"key_close", "kclo",
"key_command", "kcmd",
"key_copy", "kcpy",
"key_create", "kcrt",
"key_end", "kend",
"key_enter", "kent",
"key_exit", "kext",
"key_find", "kfnd",
"key_help", "khlp",
"key_mark", "kmrk",
"key_message", "kmsg",
"key_move", "kmov",
"key_next", "knxt",
"key_open", "kopn",
"key_options", "kopt",
"key_previous", "kprv",
"key_print", "kprt",
"key_redo", "krdo",
"key_reference", "kref",
"key_refresh", "krfr",
"key_replace", "krpl",
"key_restart", "krst",
"key_resume", "kres",
"key_save", "ksav",
"key_suspend", "kspd",
"key_undo", "kund",
"key_sbeg", "kBEG",
"key_scancel", "kCAN",
"key_scommand", "kCMD",
"key_scopy", "kCPY",
"key_screate", "kCRT",
"key_sdc", "kDC",
"key_sdl", "kDL",
"key_select", "kslt",
"key_send", "kEND",
"key_seol", "kEOL",
"key_sexit", "kEXT",
"key_sfind", "kFND",
"key_shelp", "kHLP",
"key_shome", "kHOM",
"key_sic", "kIC",
"key_sleft", "kLFT",
"key_smessage", "kMSG",
"key_smove", "kMOV",
"key_snext", "kNXT",
"key_soptions", "kOPT",
"key_sprevious", "kPRV",
"key_sprint", "kPRT",
"key_sredo", "kRDO",
"key_sreplace", "kRPL",
"key_sright", "kRIT",
"key_srsume", "kRES",
"key_ssave", "kSAV",
"key_ssuspend", "kSPD",
"key_sundo", "kUND",
"req_for_input", "rfi",
"key_f11", "kf11",
"key_f12", "kf12",
"key_f13", "kf13",
"key_f14", "kf14",
"key_f15", "kf15",
"key_f16", "kf16",
"key_f17", "kf17",
"key_f18", "kf18",
"key_f19", "kf19",
"key_f20", "kf20",
"key_f21", "kf21",
"key_f22", "kf22",
"key_f23", "kf23",
"key_f24", "kf24",
"key_f25", "kf25",
"key_f26", "kf26",
"key_f27", "kf27",
"key_f28", "kf28",
"key_f29", "kf29",
"key_f30", "kf30",
"key_f31", "kf31",
"key_f32", "kf32",
"key_f33", "kf33",
"key_f34", "kf34",
"key_f35", "kf35",
"key_f36", "kf36",
"key_f37", "kf37",
"key_f38", "kf38",
"key_f39", "kf39",
"key_f40", "kf40",
"key_f41", "kf41",
"key_f42", "kf42",
"key_f43", "kf43",
"key_f44", "kf44",
"key_f45", "kf45",
"key_f46", "kf46",
"key_f47", "kf47",
"key_f48", "kf48",
"key_f49", "kf49",
"key_f50", "kf50",
"key_f51", "kf51",
"key_f52", "kf52",
"key_f53", "kf53",
"key_f54", "kf54",
"key_f55", "kf55",
"key_f56", "kf56",
"key_f57", "kf57",
"key_f58", "kf58",
"key_f59", "kf59",
"key_f60", "kf60",
"key_f61", "kf61",
"key_f62", "kf62",
"key_f63", "kf63",
"clr_bol", "el1",
"clear_margins", "mgc",
"set_left_margin", "smgl",
"set_right_margin", "smgr",
"label_format", "fln",
"set_clock", "sclk",
"display_clock", "dclk",
"remove_clock", "rmclk",
"create_window", "cwin",
"goto_window", "wingo",
"hangup", "hup",
"dial_phone", "dial",
"quick_dial", "qdial",
"tone", "tone",
"pulse", "pulse",
"flash_hook", "hook",
"fixed_pause", "pause",
"wait_tone", "wait",
"user0", "u0",
"user1", "u1",
"user2", "u2",
"user3", "u3",
"user4", "u4",
"user5", "u5",
"user6", "u6",
"user7", "u7",
"user8", "u8",
"user9", "u9",
"orig_pair", "op",
"orig_colors", "oc",
"initialize_color", "initc",
"initialize_pair", "initp",
"set_color_pair", "scp",
"set_foreground", "setf",
"set_background", "setb",
"change_char_pitch", "cpi",
"change_line_pitch", "lpi",
"change_res_horz", "chr",
"change_res_vert", "cvr",
"define_char", "defc",
"enter_doublewide_mode", "swidm",
"enter_draft_quality", "sdrfq",
"enter_italics_mode", "sitm",
"enter_leftward_mode", "slm",
"enter_micro_mode", "smicm",
"enter_near_letter_quality", "snlq",
"enter_normal_quality", "snrmq",
"enter_shadow_mode", "sshm",
"enter_subscript_mode", "ssubm",
"enter_superscript_mode", "ssupm",
"enter_upward_mode", "sum",
"exit_doublewide_mode", "rwidm",
"exit_italics_mode", "ritm",
"exit_leftward_mode", "rlm",
"exit_micro_mode", "rmicm",
"exit_shadow_mode", "rshm",
"exit_subscript_mode", "rsubm",
"exit_superscript_mode", "rsupm",
"exit_upward_mode", "rum",
"micro_column_address", "mhpa",
"micro_down", "mcud1",
"micro_left", "mcub1",
"micro_right", "mcuf1",
"micro_row_address", "mvpa",
"micro_up", "mcuu1",
"order_of_pins", "porder",
"parm_down_micro", "mcud",
"parm_left_micro", "mcub",
"parm_right_micro", "mcuf",
"parm_up_micro", "mcuu",
"select_char_set", "scs",
"set_bottom_margin", "smgb",
"set_bottom_margin_parm", "smgbp",
"set_left_margin_parm", "smglp",
"set_right_margin_parm", "smgrp",
"set_top_margin", "smgt",
"set_top_margin_parm", "smgtp",
"start_bit_image", "sbim",
"start_char_set_def", "scsd",
"stop_bit_image", "rbim",
"stop_char_set_def", "rcsd",
"subscript_characters", "subcs",
"superscript_characters", "supcs",
"these_cause_cr", "docr",
"zero_motion", "zerom",
"char_set_names", "csnm",
"key_mouse", "kmous",
"mouse_info", "minfo",
"req_mouse_pos", "reqmp",
"get_mouse", "getm",
"set_a_foreground", "setaf",
"set_a_background", "setab",
"pkey_plab", "pfxl",
"device_type", "devt",
"code_set_init", "csin",
"set0_des_seq", "s0ds",
"set1_des_seq", "s1ds",
"set2_des_seq", "s2ds",
"set3_des_seq", "s3ds",
"set_lr_margin", "smglr",
"set_tb_margin", "smgtb",
"bit_image_repeat", "birep",
"bit_image_newline", "binel",
"bit_image_carriage_return", "bicr",
"color_names", "colornm",
"define_bit_image_region", "defbi",
"end_bit_image_region", "endbi",
"set_color_band", "setcolor",
"set_page_length", "slines",
"display_pc_char", "dispc",
"enter_pc_charset_mode", "smpch",
"exit_pc_charset_mode", "rmpch",
"enter_scancode_mode", "smsc",
"exit_scancode_mode", "rmsc",
"pc_term_options", "pctrm",
"scancode_escape", "scesc",
"alt_scancode_esc", "scesa",
"enter_horizontal_hl_mode", "ehhlm",
"enter_left_hl_mode", "elhlm",
"enter_low_hl_mode", "elohlm",
"enter_right_hl_mode", "erhlm",
"enter_top_hl_mode", "ethlm",
"enter_vertical_hl_mode", "evhlm",
"set_a_attributes", "sgr1",
"set_pglen_inch", "slength",
"termcap_init2", "",
"termcap_reset", "",
"linefeed_if_not_lf", "",
"backspace_if_not_bs", "",
"other_non_function_keys", "",
"arrow_key_map", "",
"acs_ulcorner", "",
"acs_llcorner", "",
"acs_urcorner", "",
"acs_lrcorner", "",
"acs_ltee", "",
"acs_rtee", "",
"acs_btee", "",
"acs_ttee", "",
"acs_hline", "",
"acs_vline", "",
"acs_plus", "",
"memory_lock", "",
"memory_unlock", "",
"box_chars_1", "",
}

View file

@ -1,238 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Gotty is a Go-package for reading and parsing the terminfo database
package gotty
// TODO add more concurrency to name lookup, look for more opportunities.
import (
"encoding/binary"
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"
)
// Open a terminfo file by the name given and construct a TermInfo object.
// If something went wrong reading the terminfo database file, an error is
// returned.
func OpenTermInfo(termName string) (*TermInfo, error) {
var term *TermInfo
var err error
// Find the environment variables
termloc := os.Getenv("TERMINFO")
if len(termloc) == 0 {
// Search like ncurses
locations := []string{os.Getenv("HOME") + "/.terminfo/", "/etc/terminfo/",
"/lib/terminfo/", "/usr/share/terminfo/"}
var path string
for _, str := range locations {
// Construct path
path = str + string(termName[0]) + "/" + termName
// Check if path can be opened
file, _ := os.Open(path)
if file != nil {
// Path can open, fall out and use current path
file.Close()
break
}
}
if len(path) > 0 {
term, err = readTermInfo(path)
} else {
err = errors.New(fmt.Sprintf("No terminfo file(-location) found"))
}
}
return term, err
}
// Open a terminfo file from the environment variable containing the current
// terminal name and construct a TermInfo object. If something went wrong
// reading the terminfo database file, an error is returned.
func OpenTermInfoEnv() (*TermInfo, error) {
termenv := os.Getenv("TERM")
return OpenTermInfo(termenv)
}
// Return an attribute by the name attr provided. If none can be found,
// an error is returned.
func (term *TermInfo) GetAttribute(attr string) (stacker, error) {
// Channel to store the main value in.
var value stacker
// Add a blocking WaitGroup
var block sync.WaitGroup
// Keep track of variable being written.
written := false
// Function to put into goroutine.
f := func(ats interface{}) {
var ok bool
var v stacker
// Switch on type of map to use and assign value to it.
switch reflect.TypeOf(ats).Elem().Kind() {
case reflect.Bool:
v, ok = ats.(map[string]bool)[attr]
case reflect.Int16:
v, ok = ats.(map[string]int16)[attr]
case reflect.String:
v, ok = ats.(map[string]string)[attr]
}
// If ok, a value is found, so we can write.
if ok {
value = v
written = true
}
// Goroutine is done
block.Done()
}
block.Add(3)
// Go for all 3 attribute lists.
go f(term.boolAttributes)
go f(term.numAttributes)
go f(term.strAttributes)
// Wait until every goroutine is done.
block.Wait()
// If a value has been written, return it.
if written {
return value, nil
}
// Otherwise, error.
return nil, fmt.Errorf("Erorr finding attribute")
}
// Return an attribute by the name attr provided. If none can be found,
// an error is returned. A name is first converted to its termcap value.
func (term *TermInfo) GetAttributeName(name string) (stacker, error) {
tc := GetTermcapName(name)
return term.GetAttribute(tc)
}
// A utility function that finds and returns the termcap equivalent of a
// variable name.
func GetTermcapName(name string) string {
// Termcap name
var tc string
// Blocking group
var wait sync.WaitGroup
// Function to put into a goroutine
f := func(attrs []string) {
// Find the string corresponding to the name
for i, s := range attrs {
if s == name {
tc = attrs[i+1]
}
}
// Goroutine is finished
wait.Done()
}
wait.Add(3)
// Go for all 3 attribute lists
go f(BoolAttr[:])
go f(NumAttr[:])
go f(StrAttr[:])
// Wait until every goroutine is done
wait.Wait()
// Return the termcap name
return tc
}
// This function takes a path to a terminfo file and reads it in binary
// form to construct the actual TermInfo file.
func readTermInfo(path string) (*TermInfo, error) {
// Open the terminfo file
file, err := os.Open(path)
defer file.Close()
if err != nil {
return nil, err
}
// magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize
// Header is composed of the magic 0432 octal number, size of the name
// section, size of the boolean section, the amount of number values,
// the number of offsets of strings, and the size of the string section.
var header [6]int16
// Byte array is used to read in byte values
var byteArray []byte
// Short array is used to read in short values
var shArray []int16
// TermInfo object to store values
var term TermInfo
// Read in the header
err = binary.Read(file, binary.LittleEndian, &header)
if err != nil {
return nil, err
}
// If magic number isn't there or isn't correct, we have the wrong filetype
if header[0] != 0432 {
return nil, errors.New(fmt.Sprintf("Wrong filetype"))
}
// Read in the names
byteArray = make([]byte, header[1])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.Names = strings.Split(string(byteArray), "|")
// Read in the booleans
byteArray = make([]byte, header[2])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.boolAttributes = make(map[string]bool)
for i, b := range byteArray {
if b == 1 {
term.boolAttributes[BoolAttr[i*2+1]] = true
}
}
// If the number of bytes read is not even, a byte for alignment is added
if len(byteArray)%2 != 0 {
err = binary.Read(file, binary.LittleEndian, make([]byte, 1))
if err != nil {
return nil, err
}
}
// Read in shorts
shArray = make([]int16, header[3])
err = binary.Read(file, binary.LittleEndian, &shArray)
if err != nil {
return nil, err
}
term.numAttributes = make(map[string]int16)
for i, n := range shArray {
if n != 0377 && n > -1 {
term.numAttributes[NumAttr[i*2+1]] = n
}
}
// Read the offsets into the short array
shArray = make([]int16, header[4])
err = binary.Read(file, binary.LittleEndian, &shArray)
if err != nil {
return nil, err
}
// Read the actual strings in the byte array
byteArray = make([]byte, header[5])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.strAttributes = make(map[string]string)
// We get an offset, and then iterate until the string is null-terminated
for i, offset := range shArray {
if offset > -1 {
r := offset
for ; byteArray[r] != 0; r++ {
}
term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r])
}
}
return &term, nil
}

View file

@ -1,362 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
import (
"bytes"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
var exp = [...]string{
"%%",
"%c",
"%s",
"%p(\\d)",
"%P([A-z])",
"%g([A-z])",
"%'(.)'",
"%{([0-9]+)}",
"%l",
"%\\+|%-|%\\*|%/|%m",
"%&|%\\||%\\^",
"%=|%>|%<",
"%A|%O",
"%!|%~",
"%i",
"%(:[\\ #\\-\\+]{0,4})?(\\d+\\.\\d+|\\d+)?[doxXs]",
"%\\?(.*?);",
}
var regex *regexp.Regexp
var staticVar map[byte]stacker
// Parses the attribute that is received with name attr and parameters params.
func (term *TermInfo) Parse(attr string, params ...interface{}) (string, error) {
// Get the attribute name first.
iface, err := term.GetAttribute(attr)
str, ok := iface.(string)
if err != nil {
return "", err
}
if !ok {
return str, errors.New("Only string capabilities can be parsed.")
}
// Construct the hidden parser struct so we can use a recursive stack based
// parser.
ps := &parser{}
// Dynamic variables only exist in this context.
ps.dynamicVar = make(map[byte]stacker, 26)
ps.parameters = make([]stacker, len(params))
// Convert the parameters to insert them into the parser struct.
for i, x := range params {
ps.parameters[i] = x
}
// Recursively walk and return.
result, err := ps.walk(str)
return result, err
}
// Parses the attribute that is received with name attr and parameters params.
// Only works on full name of a capability that is given, which it uses to
// search for the termcap name.
func (term *TermInfo) ParseName(attr string, params ...interface{}) (string, error) {
tc := GetTermcapName(attr)
return term.Parse(tc, params)
}
// Identify each token in a stack based manner and do the actual parsing.
func (ps *parser) walk(attr string) (string, error) {
// We use a buffer to get the modified string.
var buf bytes.Buffer
// Next, find and identify all tokens by their indices and strings.
tokens := regex.FindAllStringSubmatch(attr, -1)
if len(tokens) == 0 {
return attr, nil
}
indices := regex.FindAllStringIndex(attr, -1)
q := 0 // q counts the matches of one token
// Iterate through the string per character.
for i := 0; i < len(attr); i++ {
// If the current position is an identified token, execute the following
// steps.
if q < len(indices) && i >= indices[q][0] && i < indices[q][1] {
// Switch on token.
switch {
case tokens[q][0][:2] == "%%":
// Literal percentage character.
buf.WriteByte('%')
case tokens[q][0][:2] == "%c":
// Pop a character.
c, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
buf.WriteByte(c.(byte))
case tokens[q][0][:2] == "%s":
// Pop a string.
str, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
if _, ok := str.(string); !ok {
return buf.String(), errors.New("Stack head is not a string")
}
buf.WriteString(str.(string))
case tokens[q][0][:2] == "%p":
// Push a parameter on the stack.
index, err := strconv.ParseInt(tokens[q][1], 10, 8)
index--
if err != nil {
return buf.String(), err
}
if int(index) >= len(ps.parameters) {
return buf.String(), errors.New("Parameters index out of bound")
}
ps.st.push(ps.parameters[index])
case tokens[q][0][:2] == "%P":
// Pop a variable from the stack as a dynamic or static variable.
val, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
index := tokens[q][2]
if len(index) > 1 {
errorStr := fmt.Sprintf("%s is not a valid dynamic variables index",
index)
return buf.String(), errors.New(errorStr)
}
// Specify either dynamic or static.
if index[0] >= 'a' && index[0] <= 'z' {
ps.dynamicVar[index[0]] = val
} else if index[0] >= 'A' && index[0] <= 'Z' {
staticVar[index[0]] = val
}
case tokens[q][0][:2] == "%g":
// Push a variable from the stack as a dynamic or static variable.
index := tokens[q][3]
if len(index) > 1 {
errorStr := fmt.Sprintf("%s is not a valid static variables index",
index)
return buf.String(), errors.New(errorStr)
}
var val stacker
if index[0] >= 'a' && index[0] <= 'z' {
val = ps.dynamicVar[index[0]]
} else if index[0] >= 'A' && index[0] <= 'Z' {
val = staticVar[index[0]]
}
ps.st.push(val)
case tokens[q][0][:2] == "%'":
// Push a character constant.
con := tokens[q][4]
if len(con) > 1 {
errorStr := fmt.Sprintf("%s is not a valid character constant", con)
return buf.String(), errors.New(errorStr)
}
ps.st.push(con[0])
case tokens[q][0][:2] == "%{":
// Push an integer constant.
con, err := strconv.ParseInt(tokens[q][5], 10, 32)
if err != nil {
return buf.String(), err
}
ps.st.push(con)
case tokens[q][0][:2] == "%l":
// Push the length of the string that is popped from the stack.
popStr, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
if _, ok := popStr.(string); !ok {
errStr := fmt.Sprintf("Stack head is not a string")
return buf.String(), errors.New(errStr)
}
ps.st.push(len(popStr.(string)))
case tokens[q][0][:2] == "%?":
// If-then-else construct. First, the whole string is identified and
// then inside this substring, we can specify which parts to switch on.
ifReg, _ := regexp.Compile("%\\?(.*)%t(.*)%e(.*);|%\\?(.*)%t(.*);")
ifTokens := ifReg.FindStringSubmatch(tokens[q][0])
var (
ifStr string
err error
)
// Parse the if-part to determine if-else.
if len(ifTokens[1]) > 0 {
ifStr, err = ps.walk(ifTokens[1])
} else { // else
ifStr, err = ps.walk(ifTokens[4])
}
// Return any errors
if err != nil {
return buf.String(), err
} else if len(ifStr) > 0 {
// Self-defined limitation, not sure if this is correct, but didn't
// seem like it.
return buf.String(), errors.New("If-clause cannot print statements")
}
var thenStr string
// Pop the first value that is set by parsing the if-clause.
choose, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
// Switch to if or else.
if choose.(int) == 0 && len(ifTokens[1]) > 0 {
thenStr, err = ps.walk(ifTokens[3])
} else if choose.(int) != 0 {
if len(ifTokens[1]) > 0 {
thenStr, err = ps.walk(ifTokens[2])
} else {
thenStr, err = ps.walk(ifTokens[5])
}
}
if err != nil {
return buf.String(), err
}
buf.WriteString(thenStr)
case tokens[q][0][len(tokens[q][0])-1] == 'd': // Fallthrough for printing
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 'o': // digits.
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 'x':
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 'X':
fallthrough
case tokens[q][0][len(tokens[q][0])-1] == 's':
token := tokens[q][0]
// Remove the : that comes before a flag.
if token[1] == ':' {
token = token[:1] + token[2:]
}
digit, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
// The rest is determined like the normal formatted prints.
digitStr := fmt.Sprintf(token, digit.(int))
buf.WriteString(digitStr)
case tokens[q][0][:2] == "%i":
// Increment the parameters by one.
if len(ps.parameters) < 2 {
return buf.String(), errors.New("Not enough parameters to increment.")
}
val1, val2 := ps.parameters[0].(int), ps.parameters[1].(int)
val1++
val2++
ps.parameters[0], ps.parameters[1] = val1, val2
default:
// The rest of the tokens is a special case, where two values are
// popped and then operated on by the token that comes after them.
op1, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
op2, err := ps.st.pop()
if err != nil {
return buf.String(), err
}
var result stacker
switch tokens[q][0][:2] {
case "%+":
// Addition
result = op2.(int) + op1.(int)
case "%-":
// Subtraction
result = op2.(int) - op1.(int)
case "%*":
// Multiplication
result = op2.(int) * op1.(int)
case "%/":
// Division
result = op2.(int) / op1.(int)
case "%m":
// Modulo
result = op2.(int) % op1.(int)
case "%&":
// Bitwise AND
result = op2.(int) & op1.(int)
case "%|":
// Bitwise OR
result = op2.(int) | op1.(int)
case "%^":
// Bitwise XOR
result = op2.(int) ^ op1.(int)
case "%=":
// Equals
result = op2 == op1
case "%>":
// Greater-than
result = op2.(int) > op1.(int)
case "%<":
// Lesser-than
result = op2.(int) < op1.(int)
case "%A":
// Logical AND
result = op2.(bool) && op1.(bool)
case "%O":
// Logical OR
result = op2.(bool) || op1.(bool)
case "%!":
// Logical complement
result = !op1.(bool)
case "%~":
// Bitwise complement
result = ^(op1.(int))
}
ps.st.push(result)
}
i = indices[q][1] - 1
q++
} else {
// We are not "inside" a token, so just skip until the end or the next
// token, and add all characters to the buffer.
j := i
if q != len(indices) {
for !(j >= indices[q][0] && j < indices[q][1]) {
j++
}
} else {
j = len(attr)
}
buf.WriteString(string(attr[i:j]))
i = j
}
}
// Return the buffer as a string.
return buf.String(), nil
}
// Push a stacker-value onto the stack.
func (st *stack) push(s stacker) {
*st = append(*st, s)
}
// Pop a stacker-value from the stack.
func (st *stack) pop() (stacker, error) {
if len(*st) == 0 {
return nil, errors.New("Stack is empty.")
}
newStack := make(stack, len(*st)-1)
val := (*st)[len(*st)-1]
copy(newStack, (*st)[:len(*st)-1])
*st = newStack
return val, nil
}
// Initialize regexes and the static vars (that don't get changed between
// calls.
func init() {
// Initialize the main regex.
expStr := strings.Join(exp[:], "|")
regex, _ = regexp.Compile(expStr)
// Initialize the static variables.
staticVar = make(map[byte]stacker, 26)
}

View file

@ -1,23 +0,0 @@
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
type TermInfo struct {
boolAttributes map[string]bool
numAttributes map[string]int16
strAttributes map[string]string
// The various names of the TermInfo file.
Names []string
}
type stacker interface {
}
type stack []stacker
type parser struct {
st stack
parameters []stacker
dynamicVar map[byte]stacker
}

View file

@ -1,20 +0,0 @@
Copyright (C) 2013 Blake Mizerany
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.

View file

@ -1,316 +0,0 @@
// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm used, see:
//
// Effective Computation of Biased Quantiles over Data Streams
//
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
package quantile
import (
"math"
"sort"
)
// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
Value float64 `json:",string"`
Width float64 `json:",string"`
Delta float64 `json:",string"`
}
// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample
func (a Samples) Len() int { return len(a) }
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type invariant func(s *stream, r float64) float64
// NewLowBiased returns an initialized Stream for low-biased quantiles
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
// error guarantees can still be given even for the lower ranks of the data
// distribution.
//
// The provided epsilon is a relative error, i.e. the true quantile of a value
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
//
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
// properties.
func NewLowBiased(epsilon float64) *Stream {
ƒ := func(s *stream, r float64) float64 {
return 2 * epsilon * r
}
return newStream(ƒ)
}
// NewHighBiased returns an initialized Stream for high-biased quantiles
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
// error guarantees can still be given even for the higher ranks of the data
// distribution.
//
// The provided epsilon is a relative error, i.e. the true quantile of a value
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
//
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
// properties.
func NewHighBiased(epsilon float64) *Stream {
ƒ := func(s *stream, r float64) float64 {
return 2 * epsilon * (s.n - r)
}
return newStream(ƒ)
}
// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time. The targets map maps the desired quantiles to
// their absolute errors, i.e. the true quantile of a value returned by a query
// is guaranteed to be within (Quantile±Epsilon).
//
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(targetMap map[float64]float64) *Stream {
// Convert map to slice to avoid slow iterations on a map.
// ƒ is called on the hot path, so converting the map to a slice
// beforehand results in significant CPU savings.
targets := targetMapToSlice(targetMap)
ƒ := func(s *stream, r float64) float64 {
var m = math.MaxFloat64
var f float64
for _, t := range targets {
if t.quantile*s.n <= r {
f = (2 * t.epsilon * r) / t.quantile
} else {
f = (2 * t.epsilon * (s.n - r)) / (1 - t.quantile)
}
if f < m {
m = f
}
}
return m
}
return newStream(ƒ)
}
type target struct {
quantile float64
epsilon float64
}
func targetMapToSlice(targetMap map[float64]float64) []target {
targets := make([]target, 0, len(targetMap))
for quantile, epsilon := range targetMap {
t := target{
quantile: quantile,
epsilon: epsilon,
}
targets = append(targets, t)
}
return targets
}
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
// design. Take care when using across multiple goroutines.
type Stream struct {
*stream
b Samples
sorted bool
}
func newStream(ƒ invariant) *Stream {
x := &stream{ƒ: ƒ}
return &Stream{x, make(Samples, 0, 500), true}
}
// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
s.insert(Sample{Value: v, Width: 1})
}
func (s *Stream) insert(sample Sample) {
s.b = append(s.b, sample)
s.sorted = false
if len(s.b) == cap(s.b) {
s.flush()
}
}
// Query returns the computed qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
if !s.flushed() {
// Fast path when there hasn't been enough data for a flush;
// this also yields better accuracy for small sets of data.
l := len(s.b)
if l == 0 {
return 0
}
i := int(math.Ceil(float64(l) * q))
if i > 0 {
i -= 1
}
s.maybeSort()
return s.b[i].Value
}
s.flush()
return s.stream.query(q)
}
// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
//
// ATTENTION: This method is broken and does not yield correct results. The
// underlying algorithm is not capable of merging streams correctly.
func (s *Stream) Merge(samples Samples) {
sort.Sort(samples)
s.stream.merge(samples)
}
// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
s.stream.reset()
s.b = s.b[:0]
}
// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
if !s.flushed() {
return s.b
}
s.flush()
return s.stream.samples()
}
// Count returns the total number of samples observed in the stream
// since initialization.
func (s *Stream) Count() int {
return len(s.b) + s.stream.count()
}
func (s *Stream) flush() {
s.maybeSort()
s.stream.merge(s.b)
s.b = s.b[:0]
}
func (s *Stream) maybeSort() {
if !s.sorted {
s.sorted = true
sort.Sort(s.b)
}
}
func (s *Stream) flushed() bool {
return len(s.stream.l) > 0
}
type stream struct {
n float64
l []Sample
ƒ invariant
}
func (s *stream) reset() {
s.l = s.l[:0]
s.n = 0
}
func (s *stream) insert(v float64) {
s.merge(Samples{{v, 1, 0}})
}
func (s *stream) merge(samples Samples) {
// TODO(beorn7): This tries to merge not only individual samples, but
// whole summaries. The paper doesn't mention merging summaries at
// all. Unittests show that the merging is inaccurate. Find out how to
// do merges properly.
var r float64
i := 0
for _, sample := range samples {
for ; i < len(s.l); i++ {
c := s.l[i]
if c.Value > sample.Value {
// Insert at position i.
s.l = append(s.l, Sample{})
copy(s.l[i+1:], s.l[i:])
s.l[i] = Sample{
sample.Value,
sample.Width,
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
// TODO(beorn7): How to calculate delta correctly?
}
i++
goto inserted
}
r += c.Width
}
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
i++
inserted:
s.n += sample.Width
r += sample.Width
}
s.compress()
}
func (s *stream) count() int {
return int(s.n)
}
func (s *stream) query(q float64) float64 {
t := math.Ceil(q * s.n)
t += math.Ceil(s.ƒ(s, t) / 2)
p := s.l[0]
var r float64
for _, c := range s.l[1:] {
r += p.Width
if r+c.Width+c.Delta > t {
return p.Value
}
p = c
}
return p.Value
}
func (s *stream) compress() {
if len(s.l) < 2 {
return
}
x := s.l[len(s.l)-1]
xi := len(s.l) - 1
r := s.n - 1 - x.Width
for i := len(s.l) - 2; i >= 0; i-- {
c := s.l[i]
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
x.Width += c.Width
s.l[xi] = x
// Remove element at i.
copy(s.l[i:], s.l[i+1:])
s.l = s.l[:len(s.l)-1]
xi -= 1
} else {
x = c
xi = i
}
r -= c.Width
}
}
func (s *stream) samples() Samples {
samples := make(Samples, len(s.l))
copy(samples, s.l)
return samples
}

View file

@ -176,7 +176,7 @@
END OF TERMS AND CONDITIONS
Copyright 2015 Docker, Inc.
Copyright The containerd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -0,0 +1,78 @@
/*
Copyright The containerd Authors.
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
http://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.
*/
// Package errdefs defines the common errors used throughout containerd
// packages.
//
// Use with errors.Wrap and error.Wrapf to add context to an error.
//
// To detect an error class, use the IsXXX functions to tell whether an error
// is of a certain type.
//
// The functions ToGRPC and FromGRPC can be used to map server-side and
// client-side errors to the correct types.
package errdefs
import "github.com/pkg/errors"
// Definitions of common error types used throughout containerd. All containerd
// errors returned by most packages will map into one of these errors classes.
// Packages should return errors of these types when they want to instruct a
// client to take a particular action.
//
// For the most part, we just try to provide local grpc errors. Most conditions
// map very well to those defined by grpc.
var (
ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
ErrInvalidArgument = errors.New("invalid argument")
ErrNotFound = errors.New("not found")
ErrAlreadyExists = errors.New("already exists")
ErrFailedPrecondition = errors.New("failed precondition")
ErrUnavailable = errors.New("unavailable")
ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
)
// IsInvalidArgument returns true if the error is due to an invalid argument
func IsInvalidArgument(err error) bool {
return errors.Cause(err) == ErrInvalidArgument
}
// IsNotFound returns true if the error is due to a missing object
func IsNotFound(err error) bool {
return errors.Cause(err) == ErrNotFound
}
// IsAlreadyExists returns true if the error is due to an already existing
// metadata item
func IsAlreadyExists(err error) bool {
return errors.Cause(err) == ErrAlreadyExists
}
// IsFailedPrecondition returns true if an operation could not proceed to the
// lack of a particular condition
func IsFailedPrecondition(err error) bool {
return errors.Cause(err) == ErrFailedPrecondition
}
// IsUnavailable returns true if the error is due to a resource being unavailable
func IsUnavailable(err error) bool {
return errors.Cause(err) == ErrUnavailable
}
// IsNotImplemented returns true if the error is due to not being implemented
func IsNotImplemented(err error) bool {
return errors.Cause(err) == ErrNotImplemented
}

138
vendor/github.com/containerd/containerd/errdefs/grpc.go generated vendored Normal file
View file

@ -0,0 +1,138 @@
/*
Copyright The containerd Authors.
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
http://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.
*/
package errdefs
import (
"strings"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ToGRPC will attempt to map the backend containerd error into a grpc error,
// using the original error message as a description.
//
// Further information may be extracted from certain errors depending on their
// type.
//
// If the error is unmapped, the original error will be returned to be handled
// by the regular grpc error handling stack.
func ToGRPC(err error) error {
if err == nil {
return nil
}
if isGRPCError(err) {
// error has already been mapped to grpc
return err
}
switch {
case IsInvalidArgument(err):
return status.Errorf(codes.InvalidArgument, err.Error())
case IsNotFound(err):
return status.Errorf(codes.NotFound, err.Error())
case IsAlreadyExists(err):
return status.Errorf(codes.AlreadyExists, err.Error())
case IsFailedPrecondition(err):
return status.Errorf(codes.FailedPrecondition, err.Error())
case IsUnavailable(err):
return status.Errorf(codes.Unavailable, err.Error())
case IsNotImplemented(err):
return status.Errorf(codes.Unimplemented, err.Error())
}
return err
}
// ToGRPCf maps the error to grpc error codes, assembling the formatting string
// and combining it with the target error string.
//
// This is equivalent to errors.ToGRPC(errors.Wrapf(err, format, args...))
func ToGRPCf(err error, format string, args ...interface{}) error {
return ToGRPC(errors.Wrapf(err, format, args...))
}
// FromGRPC returns the underlying error from a grpc service based on the grpc error code
func FromGRPC(err error) error {
if err == nil {
return nil
}
var cls error // divide these into error classes, becomes the cause
switch code(err) {
case codes.InvalidArgument:
cls = ErrInvalidArgument
case codes.AlreadyExists:
cls = ErrAlreadyExists
case codes.NotFound:
cls = ErrNotFound
case codes.Unavailable:
cls = ErrUnavailable
case codes.FailedPrecondition:
cls = ErrFailedPrecondition
case codes.Unimplemented:
cls = ErrNotImplemented
default:
cls = ErrUnknown
}
msg := rebaseMessage(cls, err)
if msg != "" {
err = errors.Wrapf(cls, msg)
} else {
err = errors.WithStack(cls)
}
return err
}
// rebaseMessage removes the repeats for an error at the end of an error
// string. This will happen when taking an error over grpc then remapping it.
//
// Effectively, we just remove the string of cls from the end of err if it
// appears there.
func rebaseMessage(cls error, err error) string {
desc := errDesc(err)
clss := cls.Error()
if desc == clss {
return ""
}
return strings.TrimSuffix(desc, ": "+clss)
}
func isGRPCError(err error) bool {
_, ok := status.FromError(err)
return ok
}
func code(err error) codes.Code {
if s, ok := status.FromError(err); ok {
return s.Code()
}
return codes.Unknown
}
func errDesc(err error) string {
if s, ok := status.FromError(err); ok {
return s.Message()
}
return err.Error()
}

View file

@ -1,16 +0,0 @@
Aaron Lehmann <aaron.lehmann@docker.com>
Akash Gupta <akagup@microsoft.com>
Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
Andrew Pennebaker <apennebaker@datapipe.com>
Brandon Philips <brandon.philips@coreos.com>
Christopher Jones <tophj@linux.vnet.ibm.com>
Daniel, Dao Quang Minh <dqminh89@gmail.com>
Derek McGowan <derek@mcgstyle.net>
Edward Pilatowicz <edward.pilatowicz@oracle.com>
Ian Campbell <ijc@docker.com>
Justin Cormack <justin.cormack@docker.com>
Justin Cummins <sul3n3t@gmail.com>
Phil Estes <estesp@gmail.com>
Stephen J Day <stephen.day@docker.com>
Tobias Klauser <tklauser@distanz.ch>
Tonis Tiigi <tonistiigi@gmail.com>

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://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
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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
http://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.

View file

@ -1,101 +0,0 @@
/*
Copyright The containerd Authors.
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
http://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.
*/
package pathdriver
import (
"path/filepath"
)
// PathDriver provides all of the path manipulation functions in a common
// interface. The context should call these and never use the `filepath`
// package or any other package to manipulate paths.
type PathDriver interface {
Join(paths ...string) string
IsAbs(path string) bool
Rel(base, target string) (string, error)
Base(path string) string
Dir(path string) string
Clean(path string) string
Split(path string) (dir, file string)
Separator() byte
Abs(path string) (string, error)
Walk(string, filepath.WalkFunc) error
FromSlash(path string) string
ToSlash(path string) string
Match(pattern, name string) (matched bool, err error)
}
// pathDriver is a simple default implementation calls the filepath package.
type pathDriver struct{}
// LocalPathDriver is the exported pathDriver struct for convenience.
var LocalPathDriver PathDriver = &pathDriver{}
func (*pathDriver) Join(paths ...string) string {
return filepath.Join(paths...)
}
func (*pathDriver) IsAbs(path string) bool {
return filepath.IsAbs(path)
}
func (*pathDriver) Rel(base, target string) (string, error) {
return filepath.Rel(base, target)
}
func (*pathDriver) Base(path string) string {
return filepath.Base(path)
}
func (*pathDriver) Dir(path string) string {
return filepath.Dir(path)
}
func (*pathDriver) Clean(path string) string {
return filepath.Clean(path)
}
func (*pathDriver) Split(path string) (dir, file string) {
return filepath.Split(path)
}
func (*pathDriver) Separator() byte {
return filepath.Separator
}
func (*pathDriver) Abs(path string) (string, error) {
return filepath.Abs(path)
}
// Note that filepath.Walk calls os.Stat, so if the context wants to
// to call Driver.Stat() for Walk, they need to create a new struct that
// overrides this method.
func (*pathDriver) Walk(root string, walkFn filepath.WalkFunc) error {
return filepath.Walk(root, walkFn)
}
func (*pathDriver) FromSlash(path string) string {
return filepath.FromSlash(path)
}
func (*pathDriver) ToSlash(path string) string {
return filepath.ToSlash(path)
}
func (*pathDriver) Match(pattern, name string) (bool, error) {
return filepath.Match(pattern, name)
}

0
vendor/github.com/coreos/clair/LICENSE generated vendored Executable file → Normal file
View file

0
vendor/github.com/coreos/clair/NOTICE generated vendored Executable file → Normal file
View file

View file

@ -0,0 +1,7 @@
FROM golang:alpine
RUN apk add --update --no-cache git bash protobuf-dev
RUN go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
RUN go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
RUN go get -u github.com/golang/protobuf/protoc-gen-go

View file

@ -9,6 +9,8 @@ It is generated from these files:
It has these top-level messages:
Vulnerability
Detector
Namespace
Feature
Layer
ClairStatus
@ -48,12 +50,37 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Detector_DType int32
const (
Detector_DETECTOR_D_TYPE_INVALID Detector_DType = 0
Detector_DETECTOR_D_TYPE_NAMESPACE Detector_DType = 1
Detector_DETECTOR_D_TYPE_FEATURE Detector_DType = 2
)
var Detector_DType_name = map[int32]string{
0: "DETECTOR_D_TYPE_INVALID",
1: "DETECTOR_D_TYPE_NAMESPACE",
2: "DETECTOR_D_TYPE_FEATURE",
}
var Detector_DType_value = map[string]int32{
"DETECTOR_D_TYPE_INVALID": 0,
"DETECTOR_D_TYPE_NAMESPACE": 1,
"DETECTOR_D_TYPE_FEATURE": 2,
}
func (x Detector_DType) String() string {
return proto.EnumName(Detector_DType_name, int32(x))
}
func (Detector_DType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
type Vulnerability struct {
// The name of the vulnerability.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The name of the namespace in which the vulnerability was detected.
NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName" json:"namespace_name,omitempty"`
// A description of the vulnerability according to the source for the namespace.
// A description of the vulnerability according to the source for the
// namespace.
Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
// A link to the vulnerability according to the source for the namespace.
Link string `protobuf:"bytes,4,opt,name=link" json:"link,omitempty"`
@ -130,23 +157,91 @@ func (m *Vulnerability) GetAffectedVersions() []*Feature {
return nil
}
type Detector struct {
// The name of the detector.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The version of the detector.
Version string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"`
// The type of the detector.
Dtype Detector_DType `protobuf:"varint,3,opt,name=dtype,enum=coreos.clair.Detector_DType" json:"dtype,omitempty"`
}
func (m *Detector) Reset() { *m = Detector{} }
func (m *Detector) String() string { return proto.CompactTextString(m) }
func (*Detector) ProtoMessage() {}
func (*Detector) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Detector) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Detector) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Detector) GetDtype() Detector_DType {
if m != nil {
return m.Dtype
}
return Detector_DETECTOR_D_TYPE_INVALID
}
type Namespace struct {
// The name of the namespace.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The detector used to detect the namespace. This only exists when present
// in an Ancestry Feature.
Detector *Detector `protobuf:"bytes,2,opt,name=detector" json:"detector,omitempty"`
}
func (m *Namespace) Reset() { *m = Namespace{} }
func (m *Namespace) String() string { return proto.CompactTextString(m) }
func (*Namespace) ProtoMessage() {}
func (*Namespace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *Namespace) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Namespace) GetDetector() *Detector {
if m != nil {
return m.Detector
}
return nil
}
type Feature struct {
// The name of the feature.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The name of the namespace in which the feature is detected.
NamespaceName string `protobuf:"bytes,2,opt,name=namespace_name,json=namespaceName" json:"namespace_name,omitempty"`
// The namespace in which the feature is detected.
Namespace *Namespace `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
// The specific version of this feature.
Version string `protobuf:"bytes,3,opt,name=version" json:"version,omitempty"`
// The format used to parse version numbers for the feature.
VersionFormat string `protobuf:"bytes,4,opt,name=version_format,json=versionFormat" json:"version_format,omitempty"`
// The detector used to detect this feature. This only exists when present
// in an Ancestry.
Detector *Detector `protobuf:"bytes,5,opt,name=detector" json:"detector,omitempty"`
// The list of vulnerabilities that affect the feature.
Vulnerabilities []*Vulnerability `protobuf:"bytes,5,rep,name=vulnerabilities" json:"vulnerabilities,omitempty"`
Vulnerabilities []*Vulnerability `protobuf:"bytes,6,rep,name=vulnerabilities" json:"vulnerabilities,omitempty"`
// The feature type indicates if the feature represents a source package or
// binary package.
FeatureType string `protobuf:"bytes,7,opt,name=feature_type,json=featureType" json:"feature_type,omitempty"`
}
func (m *Feature) Reset() { *m = Feature{} }
func (m *Feature) String() string { return proto.CompactTextString(m) }
func (*Feature) ProtoMessage() {}
func (*Feature) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (*Feature) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *Feature) GetName() string {
if m != nil {
@ -155,11 +250,11 @@ func (m *Feature) GetName() string {
return ""
}
func (m *Feature) GetNamespaceName() string {
func (m *Feature) GetNamespace() *Namespace {
if m != nil {
return m.NamespaceName
return m.Namespace
}
return ""
return nil
}
func (m *Feature) GetVersion() string {
@ -176,6 +271,13 @@ func (m *Feature) GetVersionFormat() string {
return ""
}
func (m *Feature) GetDetector() *Detector {
if m != nil {
return m.Detector
}
return nil
}
func (m *Feature) GetVulnerabilities() []*Vulnerability {
if m != nil {
return m.Vulnerabilities
@ -183,6 +285,13 @@ func (m *Feature) GetVulnerabilities() []*Vulnerability {
return nil
}
func (m *Feature) GetFeatureType() string {
if m != nil {
return m.FeatureType
}
return ""
}
type Layer struct {
// The sha256 tarsum for the layer.
Hash string `protobuf:"bytes,1,opt,name=hash" json:"hash,omitempty"`
@ -191,7 +300,7 @@ type Layer struct {
func (m *Layer) Reset() { *m = Layer{} }
func (m *Layer) String() string { return proto.CompactTextString(m) }
func (*Layer) ProtoMessage() {}
func (*Layer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (*Layer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *Layer) GetHash() string {
if m != nil {
@ -201,27 +310,18 @@ func (m *Layer) GetHash() string {
}
type ClairStatus struct {
// The configured list of feature listers used to scan an ancestry.
Listers []string `protobuf:"bytes,1,rep,name=listers" json:"listers,omitempty"`
// The configured list of namespace detectors used to scan an ancestry.
Detectors []string `protobuf:"bytes,2,rep,name=detectors" json:"detectors,omitempty"`
// The implemented detectors in this Clair instance
Detectors []*Detector `protobuf:"bytes,1,rep,name=detectors" json:"detectors,omitempty"`
// The time at which the updater last ran.
LastUpdateTime *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=last_update_time,json=lastUpdateTime" json:"last_update_time,omitempty"`
LastUpdateTime *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=last_update_time,json=lastUpdateTime" json:"last_update_time,omitempty"`
}
func (m *ClairStatus) Reset() { *m = ClairStatus{} }
func (m *ClairStatus) String() string { return proto.CompactTextString(m) }
func (*ClairStatus) ProtoMessage() {}
func (*ClairStatus) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (*ClairStatus) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *ClairStatus) GetListers() []string {
if m != nil {
return m.Listers
}
return nil
}
func (m *ClairStatus) GetDetectors() []string {
func (m *ClairStatus) GetDetectors() []*Detector {
if m != nil {
return m.Detectors
}
@ -243,7 +343,7 @@ type GetAncestryRequest struct {
func (m *GetAncestryRequest) Reset() { *m = GetAncestryRequest{} }
func (m *GetAncestryRequest) String() string { return proto.CompactTextString(m) }
func (*GetAncestryRequest) ProtoMessage() {}
func (*GetAncestryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (*GetAncestryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *GetAncestryRequest) GetAncestryName() string {
if m != nil {
@ -255,14 +355,14 @@ func (m *GetAncestryRequest) GetAncestryName() string {
type GetAncestryResponse struct {
// The ancestry requested.
Ancestry *GetAncestryResponse_Ancestry `protobuf:"bytes,1,opt,name=ancestry" json:"ancestry,omitempty"`
// The status of Clair at the time of the request.
// The status of Clair at the time of the request
Status *ClairStatus `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"`
}
func (m *GetAncestryResponse) Reset() { *m = GetAncestryResponse{} }
func (m *GetAncestryResponse) String() string { return proto.CompactTextString(m) }
func (*GetAncestryResponse) ProtoMessage() {}
func (*GetAncestryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (*GetAncestryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (m *GetAncestryResponse) GetAncestry() *GetAncestryResponse_Ancestry {
if m != nil {
@ -289,7 +389,7 @@ func (m *GetAncestryResponse_AncestryLayer) Reset() { *m = GetAncestryRe
func (m *GetAncestryResponse_AncestryLayer) String() string { return proto.CompactTextString(m) }
func (*GetAncestryResponse_AncestryLayer) ProtoMessage() {}
func (*GetAncestryResponse_AncestryLayer) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{5, 0}
return fileDescriptor0, []int{7, 0}
}
func (m *GetAncestryResponse_AncestryLayer) GetLayer() *Layer {
@ -309,18 +409,14 @@ func (m *GetAncestryResponse_AncestryLayer) GetDetectedFeatures() []*Feature {
type GetAncestryResponse_Ancestry struct {
// The name of the desired ancestry.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The configured list of feature listers used to scan this ancestry.
ScannedListers []string `protobuf:"bytes,4,rep,name=scanned_listers,json=scannedListers" json:"scanned_listers,omitempty"`
// The configured list of namespace detectors used to scan an ancestry.
ScannedDetectors []string `protobuf:"bytes,5,rep,name=scanned_detectors,json=scannedDetectors" json:"scanned_detectors,omitempty"`
// The list of layers along with detected features in each.
Layers []*GetAncestryResponse_AncestryLayer `protobuf:"bytes,6,rep,name=layers" json:"layers,omitempty"`
Layers []*GetAncestryResponse_AncestryLayer `protobuf:"bytes,3,rep,name=layers" json:"layers,omitempty"`
}
func (m *GetAncestryResponse_Ancestry) Reset() { *m = GetAncestryResponse_Ancestry{} }
func (m *GetAncestryResponse_Ancestry) String() string { return proto.CompactTextString(m) }
func (*GetAncestryResponse_Ancestry) ProtoMessage() {}
func (*GetAncestryResponse_Ancestry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5, 1} }
func (*GetAncestryResponse_Ancestry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7, 1} }
func (m *GetAncestryResponse_Ancestry) GetName() string {
if m != nil {
@ -329,20 +425,6 @@ func (m *GetAncestryResponse_Ancestry) GetName() string {
return ""
}
func (m *GetAncestryResponse_Ancestry) GetScannedListers() []string {
if m != nil {
return m.ScannedListers
}
return nil
}
func (m *GetAncestryResponse_Ancestry) GetScannedDetectors() []string {
if m != nil {
return m.ScannedDetectors
}
return nil
}
func (m *GetAncestryResponse_Ancestry) GetLayers() []*GetAncestryResponse_AncestryLayer {
if m != nil {
return m.Layers
@ -364,7 +446,7 @@ type PostAncestryRequest struct {
func (m *PostAncestryRequest) Reset() { *m = PostAncestryRequest{} }
func (m *PostAncestryRequest) String() string { return proto.CompactTextString(m) }
func (*PostAncestryRequest) ProtoMessage() {}
func (*PostAncestryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (*PostAncestryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *PostAncestryRequest) GetAncestryName() string {
if m != nil {
@ -390,9 +472,10 @@ func (m *PostAncestryRequest) GetLayers() []*PostAncestryRequest_PostLayer {
type PostAncestryRequest_PostLayer struct {
// The hash of the layer.
Hash string `protobuf:"bytes,1,opt,name=hash" json:"hash,omitempty"`
// The location of the layer (URL or filepath).
// The location of the layer (URL or file path).
Path string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
// Any HTTP Headers that need to be used if requesting a layer over HTTP(S).
// Any HTTP Headers that need to be used if requesting a layer over
// HTTP(S).
Headers map[string]string `protobuf:"bytes,3,rep,name=headers" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
}
@ -400,7 +483,7 @@ func (m *PostAncestryRequest_PostLayer) Reset() { *m = PostAncestryReque
func (m *PostAncestryRequest_PostLayer) String() string { return proto.CompactTextString(m) }
func (*PostAncestryRequest_PostLayer) ProtoMessage() {}
func (*PostAncestryRequest_PostLayer) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{6, 0}
return fileDescriptor0, []int{8, 0}
}
func (m *PostAncestryRequest_PostLayer) GetHash() string {
@ -432,7 +515,7 @@ type PostAncestryResponse struct {
func (m *PostAncestryResponse) Reset() { *m = PostAncestryResponse{} }
func (m *PostAncestryResponse) String() string { return proto.CompactTextString(m) }
func (*PostAncestryResponse) ProtoMessage() {}
func (*PostAncestryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (*PostAncestryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
func (m *PostAncestryResponse) GetStatus() *ClairStatus {
if m != nil {
@ -457,7 +540,7 @@ type GetNotificationRequest struct {
func (m *GetNotificationRequest) Reset() { *m = GetNotificationRequest{} }
func (m *GetNotificationRequest) String() string { return proto.CompactTextString(m) }
func (*GetNotificationRequest) ProtoMessage() {}
func (*GetNotificationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (*GetNotificationRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
func (m *GetNotificationRequest) GetOldVulnerabilityPage() string {
if m != nil {
@ -495,7 +578,7 @@ type GetNotificationResponse struct {
func (m *GetNotificationResponse) Reset() { *m = GetNotificationResponse{} }
func (m *GetNotificationResponse) String() string { return proto.CompactTextString(m) }
func (*GetNotificationResponse) ProtoMessage() {}
func (*GetNotificationResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
func (*GetNotificationResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (m *GetNotificationResponse) GetNotification() *GetNotificationResponse_Notification {
if m != nil {
@ -513,9 +596,11 @@ type GetNotificationResponse_Notification struct {
Notified string `protobuf:"bytes,3,opt,name=notified" json:"notified,omitempty"`
// The time at which a notification has been deleted.
Deleted string `protobuf:"bytes,4,opt,name=deleted" json:"deleted,omitempty"`
// The previous vulnerability and a paginated view of the ancestries it affects.
// The previous vulnerability and a paginated view of the ancestries it
// affects.
Old *PagedVulnerableAncestries `protobuf:"bytes,5,opt,name=old" json:"old,omitempty"`
// The newly updated vulnerability and a paginated view of the ancestries it affects.
// The newly updated vulnerability and a paginated view of the
// ancestries it affects.
New *PagedVulnerableAncestries `protobuf:"bytes,6,opt,name=new" json:"new,omitempty"`
}
@ -523,7 +608,7 @@ func (m *GetNotificationResponse_Notification) Reset() { *m = GetNotific
func (m *GetNotificationResponse_Notification) String() string { return proto.CompactTextString(m) }
func (*GetNotificationResponse_Notification) ProtoMessage() {}
func (*GetNotificationResponse_Notification) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{9, 0}
return fileDescriptor0, []int{11, 0}
}
func (m *GetNotificationResponse_Notification) GetName() string {
@ -585,7 +670,7 @@ type PagedVulnerableAncestries struct {
func (m *PagedVulnerableAncestries) Reset() { *m = PagedVulnerableAncestries{} }
func (m *PagedVulnerableAncestries) String() string { return proto.CompactTextString(m) }
func (*PagedVulnerableAncestries) ProtoMessage() {}
func (*PagedVulnerableAncestries) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
func (*PagedVulnerableAncestries) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *PagedVulnerableAncestries) GetCurrentPage() string {
if m != nil {
@ -623,8 +708,9 @@ func (m *PagedVulnerableAncestries) GetAncestries() []*PagedVulnerableAncestries
}
type PagedVulnerableAncestries_IndexedAncestryName struct {
// The index is an ever increasing number associated with the particular ancestry.
// This is useful if you're processing notifications, and need to keep track of the progress of paginating the results.
// The index is an ever increasing number associated with the particular
// ancestry. This is useful if you're processing notifications, and need
// to keep track of the progress of paginating the results.
Index int32 `protobuf:"varint,1,opt,name=index" json:"index,omitempty"`
// The name of the ancestry.
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
@ -638,7 +724,7 @@ func (m *PagedVulnerableAncestries_IndexedAncestryName) String() string {
}
func (*PagedVulnerableAncestries_IndexedAncestryName) ProtoMessage() {}
func (*PagedVulnerableAncestries_IndexedAncestryName) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{10, 0}
return fileDescriptor0, []int{12, 0}
}
func (m *PagedVulnerableAncestries_IndexedAncestryName) GetIndex() int32 {
@ -663,7 +749,7 @@ type MarkNotificationAsReadRequest struct {
func (m *MarkNotificationAsReadRequest) Reset() { *m = MarkNotificationAsReadRequest{} }
func (m *MarkNotificationAsReadRequest) String() string { return proto.CompactTextString(m) }
func (*MarkNotificationAsReadRequest) ProtoMessage() {}
func (*MarkNotificationAsReadRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (*MarkNotificationAsReadRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (m *MarkNotificationAsReadRequest) GetName() string {
if m != nil {
@ -678,7 +764,7 @@ type MarkNotificationAsReadResponse struct {
func (m *MarkNotificationAsReadResponse) Reset() { *m = MarkNotificationAsReadResponse{} }
func (m *MarkNotificationAsReadResponse) String() string { return proto.CompactTextString(m) }
func (*MarkNotificationAsReadResponse) ProtoMessage() {}
func (*MarkNotificationAsReadResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (*MarkNotificationAsReadResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
type GetStatusRequest struct {
}
@ -686,7 +772,7 @@ type GetStatusRequest struct {
func (m *GetStatusRequest) Reset() { *m = GetStatusRequest{} }
func (m *GetStatusRequest) String() string { return proto.CompactTextString(m) }
func (*GetStatusRequest) ProtoMessage() {}
func (*GetStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (*GetStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
type GetStatusResponse struct {
// The status of the current Clair instance.
@ -696,7 +782,7 @@ type GetStatusResponse struct {
func (m *GetStatusResponse) Reset() { *m = GetStatusResponse{} }
func (m *GetStatusResponse) String() string { return proto.CompactTextString(m) }
func (*GetStatusResponse) ProtoMessage() {}
func (*GetStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (*GetStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (m *GetStatusResponse) GetStatus() *ClairStatus {
if m != nil {
@ -707,6 +793,8 @@ func (m *GetStatusResponse) GetStatus() *ClairStatus {
func init() {
proto.RegisterType((*Vulnerability)(nil), "coreos.clair.Vulnerability")
proto.RegisterType((*Detector)(nil), "coreos.clair.Detector")
proto.RegisterType((*Namespace)(nil), "coreos.clair.Namespace")
proto.RegisterType((*Feature)(nil), "coreos.clair.Feature")
proto.RegisterType((*Layer)(nil), "coreos.clair.Layer")
proto.RegisterType((*ClairStatus)(nil), "coreos.clair.ClairStatus")
@ -726,6 +814,7 @@ func init() {
proto.RegisterType((*MarkNotificationAsReadResponse)(nil), "coreos.clair.MarkNotificationAsReadResponse")
proto.RegisterType((*GetStatusRequest)(nil), "coreos.clair.GetStatusRequest")
proto.RegisterType((*GetStatusResponse)(nil), "coreos.clair.GetStatusResponse")
proto.RegisterEnum("coreos.clair.Detector_DType", Detector_DType_name, Detector_DType_value)
}
// Reference imports to suppress errors if they are not otherwise used.
@ -837,6 +926,72 @@ var _AncestryService_serviceDesc = grpc.ServiceDesc{
Metadata: "api/v3/clairpb/clair.proto",
}
// Client API for StatusService service
type StatusServiceClient interface {
// The RPC used to show the internal state of current Clair instance.
GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error)
}
type statusServiceClient struct {
cc *grpc.ClientConn
}
func NewStatusServiceClient(cc *grpc.ClientConn) StatusServiceClient {
return &statusServiceClient{cc}
}
func (c *statusServiceClient) GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error) {
out := new(GetStatusResponse)
err := grpc.Invoke(ctx, "/coreos.clair.StatusService/GetStatus", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for StatusService service
type StatusServiceServer interface {
// The RPC used to show the internal state of current Clair instance.
GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error)
}
func RegisterStatusServiceServer(s *grpc.Server, srv StatusServiceServer) {
s.RegisterService(&_StatusService_serviceDesc, srv)
}
func _StatusService_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StatusServiceServer).GetStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/coreos.clair.StatusService/GetStatus",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StatusServiceServer).GetStatus(ctx, req.(*GetStatusRequest))
}
return interceptor(ctx, in, info, handler)
}
var _StatusService_serviceDesc = grpc.ServiceDesc{
ServiceName: "coreos.clair.StatusService",
HandlerType: (*StatusServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetStatus",
Handler: _StatusService_GetStatus_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v3/clairpb/clair.proto",
}
// Client API for NotificationService service
type NotificationServiceClient interface {
@ -938,152 +1093,93 @@ var _NotificationService_serviceDesc = grpc.ServiceDesc{
Metadata: "api/v3/clairpb/clair.proto",
}
// Client API for StatusService service
type StatusServiceClient interface {
// The RPC used to show the internal state of current Clair instance.
GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error)
}
type statusServiceClient struct {
cc *grpc.ClientConn
}
func NewStatusServiceClient(cc *grpc.ClientConn) StatusServiceClient {
return &statusServiceClient{cc}
}
func (c *statusServiceClient) GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error) {
out := new(GetStatusResponse)
err := grpc.Invoke(ctx, "/coreos.clair.StatusService/GetStatus", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for StatusService service
type StatusServiceServer interface {
// The RPC used to show the internal state of current Clair instance.
GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error)
}
func RegisterStatusServiceServer(s *grpc.Server, srv StatusServiceServer) {
s.RegisterService(&_StatusService_serviceDesc, srv)
}
func _StatusService_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StatusServiceServer).GetStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/coreos.clair.StatusService/GetStatus",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StatusServiceServer).GetStatus(ctx, req.(*GetStatusRequest))
}
return interceptor(ctx, in, info, handler)
}
var _StatusService_serviceDesc = grpc.ServiceDesc{
ServiceName: "coreos.clair.StatusService",
HandlerType: (*StatusServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetStatus",
Handler: _StatusService_GetStatus_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v3/clairpb/clair.proto",
}
func init() { proto.RegisterFile("api/v3/clairpb/clair.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 1237 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x57, 0x4b, 0x6f, 0x1b, 0xd5,
0x17, 0xd7, 0xd8, 0x71, 0x1c, 0x1f, 0xdb, 0x49, 0x7a, 0x93, 0xa6, 0x93, 0x49, 0x1f, 0xc9, 0xfc,
0xff, 0x55, 0x4b, 0x8b, 0x6c, 0xe1, 0xb2, 0x68, 0xcb, 0x02, 0xa5, 0x8f, 0x84, 0x4a, 0xa5, 0xaa,
0xa6, 0xd0, 0x05, 0x08, 0x59, 0xd7, 0x33, 0xc7, 0xc9, 0x28, 0xe3, 0x19, 0x33, 0xf7, 0xda, 0x89,
0x55, 0x95, 0x05, 0x5b, 0x76, 0xb0, 0xe0, 0x33, 0xb0, 0xe1, 0x1b, 0xb0, 0x62, 0xcb, 0x02, 0xc1,
0x16, 0x76, 0x2c, 0xf8, 0x02, 0xec, 0xd1, 0x7d, 0x4d, 0x66, 0x12, 0xe7, 0xd1, 0xb2, 0xf2, 0x9c,
0xf7, 0xeb, 0x77, 0xcf, 0x49, 0xc0, 0xa1, 0xc3, 0xb0, 0x3d, 0xbe, 0xd3, 0xf6, 0x23, 0x1a, 0xa6,
0xc3, 0x9e, 0xfa, 0x6d, 0x0d, 0xd3, 0x84, 0x27, 0xa4, 0xe1, 0x27, 0x29, 0x26, 0xac, 0x25, 0x79,
0xce, 0xb5, 0x9d, 0x24, 0xd9, 0x89, 0xb0, 0x2d, 0x65, 0xbd, 0x51, 0xbf, 0xcd, 0xc3, 0x01, 0x32,
0x4e, 0x07, 0x43, 0xa5, 0xee, 0x5c, 0xd6, 0x0a, 0xc2, 0x23, 0x8d, 0xe3, 0x84, 0x53, 0x1e, 0x26,
0x31, 0x53, 0x52, 0xf7, 0xfb, 0x12, 0x34, 0x5f, 0x8e, 0xa2, 0x18, 0x53, 0xda, 0x0b, 0xa3, 0x90,
0x4f, 0x08, 0x81, 0x99, 0x98, 0x0e, 0xd0, 0xb6, 0xd6, 0xad, 0x9b, 0x35, 0x4f, 0x7e, 0x93, 0xeb,
0x30, 0x2f, 0x7e, 0xd9, 0x90, 0xfa, 0xd8, 0x95, 0xd2, 0x92, 0x94, 0x36, 0x33, 0xee, 0x33, 0xa1,
0xb6, 0x0e, 0xf5, 0x00, 0x99, 0x9f, 0x86, 0x43, 0x11, 0xc2, 0x2e, 0x4b, 0x9d, 0x3c, 0x4b, 0x38,
0x8f, 0xc2, 0x78, 0xcf, 0x9e, 0x51, 0xce, 0xc5, 0x37, 0x71, 0x60, 0x8e, 0xe1, 0x18, 0xd3, 0x90,
0x4f, 0xec, 0x8a, 0xe4, 0x67, 0xb4, 0x90, 0x0d, 0x90, 0xd3, 0x80, 0x72, 0x6a, 0xcf, 0x2a, 0x99,
0xa1, 0xc9, 0x2a, 0xcc, 0xf5, 0xc3, 0x03, 0x0c, 0xba, 0xbd, 0x89, 0x5d, 0x95, 0xb2, 0xaa, 0xa4,
0x1f, 0x4c, 0xc8, 0x03, 0xb8, 0x40, 0xfb, 0x7d, 0xf4, 0x39, 0x06, 0xdd, 0x31, 0xa6, 0x4c, 0x14,
0x6c, 0xcf, 0xad, 0x97, 0x6f, 0xd6, 0x3b, 0x17, 0x5b, 0xf9, 0xf6, 0xb5, 0xb6, 0x90, 0xf2, 0x51,
0x8a, 0xde, 0xa2, 0xd1, 0x7f, 0xa9, 0xd5, 0xdd, 0x5f, 0x2c, 0xa8, 0x6a, 0xe9, 0x7f, 0xe9, 0x89,
0x0d, 0x55, 0x9d, 0x81, 0xee, 0x87, 0x21, 0x85, 0x03, 0xfd, 0xd9, 0xed, 0x27, 0xe9, 0x80, 0x72,
0xdd, 0x95, 0xa6, 0xe6, 0x6e, 0x49, 0x26, 0x79, 0x0c, 0x0b, 0xe3, 0xdc, 0x80, 0x42, 0x64, 0x76,
0x45, 0x56, 0xb2, 0x56, 0xac, 0xa4, 0x30, 0x45, 0xef, 0xa8, 0x8d, 0xbb, 0x06, 0x95, 0xa7, 0x74,
0x82, 0xa9, 0xa8, 0x65, 0x97, 0xb2, 0x5d, 0x53, 0x8b, 0xf8, 0x76, 0xbf, 0xb1, 0xa0, 0xfe, 0x50,
0x78, 0x79, 0xc1, 0x29, 0x1f, 0x31, 0x91, 0x74, 0x14, 0x32, 0x8e, 0x29, 0xb3, 0xad, 0xf5, 0xb2,
0x48, 0x5a, 0x93, 0xe4, 0x32, 0xd4, 0x02, 0xe4, 0xe8, 0xf3, 0x24, 0x65, 0x76, 0x49, 0xca, 0x0e,
0x19, 0xe4, 0x11, 0x2c, 0x46, 0x94, 0xf1, 0xee, 0x68, 0x18, 0x50, 0x8e, 0x5d, 0x01, 0x45, 0x59,
0x75, 0xbd, 0xe3, 0xb4, 0x14, 0x0c, 0x5b, 0x06, 0xa7, 0xad, 0x4f, 0x0c, 0x4e, 0xbd, 0x79, 0x61,
0xf3, 0xa9, 0x34, 0x11, 0x4c, 0xf7, 0x1e, 0x90, 0x6d, 0xe4, 0x9b, 0xb1, 0x8f, 0x8c, 0xa7, 0x13,
0x0f, 0xbf, 0x1c, 0x21, 0xe3, 0xe4, 0x7f, 0xd0, 0xa4, 0x9a, 0xd5, 0xcd, 0x0d, 0xa3, 0x61, 0x98,
0xa2, 0xdb, 0xee, 0xaf, 0x65, 0x58, 0x2a, 0xd8, 0xb2, 0x61, 0x12, 0x33, 0x24, 0x5b, 0x30, 0x67,
0xf4, 0xa4, 0x5d, 0xbd, 0x73, 0xab, 0xd8, 0xbd, 0x29, 0x46, 0xad, 0x8c, 0x91, 0xd9, 0x92, 0xf7,
0x60, 0x96, 0xc9, 0x16, 0xc9, 0x61, 0xd7, 0x3b, 0xab, 0x45, 0x2f, 0xb9, 0x1e, 0x7a, 0x5a, 0xd1,
0xf9, 0x0a, 0x9a, 0xc6, 0x91, 0x1a, 0xc0, 0x3b, 0x50, 0x89, 0xc4, 0x87, 0x4e, 0x64, 0xa9, 0xe8,
0x42, 0xea, 0x78, 0x4a, 0x43, 0xe0, 0x58, 0x35, 0x17, 0x83, 0x6e, 0x5f, 0x61, 0x51, 0x75, 0xfd,
0x64, 0x1c, 0x1b, 0x7d, 0xcd, 0x60, 0xce, 0x4f, 0x16, 0xcc, 0x99, 0x04, 0xa6, 0x02, 0xf9, 0x06,
0x2c, 0x30, 0x9f, 0xc6, 0x31, 0x06, 0x5d, 0x33, 0xf4, 0x19, 0x39, 0xd8, 0x79, 0xcd, 0x7e, 0xaa,
0x67, 0x7f, 0x1b, 0x2e, 0x18, 0xc5, 0x43, 0x0c, 0x54, 0xa4, 0xea, 0xa2, 0x16, 0x3c, 0xca, 0xa0,
0xb0, 0x0d, 0xb3, 0xb2, 0x06, 0x66, 0xcf, 0xca, 0x7c, 0xdb, 0xe7, 0xef, 0xb7, 0x6a, 0x81, 0x36,
0x77, 0xff, 0x2c, 0xc1, 0xd2, 0xf3, 0x84, 0xbd, 0x15, 0x1e, 0xc8, 0x0a, 0xcc, 0xea, 0xb7, 0xa5,
0x1e, 0xa7, 0xa6, 0xc8, 0xc3, 0x2c, 0xbb, 0xb2, 0xcc, 0xee, 0x76, 0x31, 0xbb, 0x29, 0xf1, 0x24,
0xaf, 0x90, 0x99, 0xf3, 0xb3, 0x05, 0xb5, 0x8c, 0x3b, 0xed, 0x5d, 0x09, 0xde, 0x90, 0xf2, 0x5d,
0x1d, 0x5c, 0x7e, 0x13, 0x0f, 0xaa, 0xbb, 0x48, 0x83, 0xc3, 0xd8, 0x77, 0xdf, 0x20, 0x76, 0xeb,
0x23, 0x65, 0xfa, 0x38, 0x16, 0x52, 0xe3, 0xc8, 0xb9, 0x0f, 0x8d, 0xbc, 0x80, 0x2c, 0x42, 0x79,
0x0f, 0x27, 0x3a, 0x15, 0xf1, 0x49, 0x96, 0xa1, 0x32, 0xa6, 0xd1, 0xc8, 0x2c, 0x29, 0x45, 0xdc,
0x2f, 0xdd, 0xb5, 0xdc, 0x27, 0xb0, 0x5c, 0x0c, 0xa9, 0x9f, 0xcc, 0x21, 0xd4, 0xad, 0x73, 0x42,
0xdd, 0xfd, 0xd1, 0x82, 0x95, 0x6d, 0xe4, 0xcf, 0x12, 0x1e, 0xf6, 0x43, 0x5f, 0xde, 0x19, 0x33,
0xad, 0xf7, 0x61, 0x25, 0x89, 0x82, 0x6e, 0x7e, 0x2b, 0x4d, 0xba, 0x43, 0xba, 0x63, 0xc6, 0xb6,
0x9c, 0x44, 0x41, 0x61, 0x83, 0x3d, 0xa7, 0x3b, 0x28, 0xac, 0x62, 0xdc, 0x9f, 0x66, 0xa5, 0xca,
0x58, 0x8e, 0x71, 0xff, 0xb8, 0xd5, 0x32, 0x54, 0xa2, 0x70, 0x10, 0x72, 0xb9, 0x7a, 0x2a, 0x9e,
0x22, 0x32, 0xe8, 0xcf, 0x1c, 0x42, 0xdf, 0xfd, 0xa3, 0x04, 0x97, 0x8e, 0x25, 0xac, 0xeb, 0x7f,
0x09, 0x8d, 0x38, 0xc7, 0xd7, 0x5d, 0xe8, 0x1c, 0x83, 0xf1, 0x34, 0xe3, 0x56, 0x81, 0x59, 0xf0,
0xe3, 0xfc, 0x6d, 0x41, 0x23, 0x2f, 0x9e, 0xfa, 0x26, 0x6d, 0xa8, 0xfa, 0x29, 0x52, 0x8e, 0x81,
0xae, 0xd4, 0x90, 0xe2, 0x22, 0x2a, 0x77, 0x18, 0xe8, 0x83, 0x92, 0xd1, 0xc2, 0x2a, 0xc0, 0x08,
0x85, 0x95, 0xaa, 0xd2, 0x90, 0xe4, 0x1e, 0x94, 0x93, 0x28, 0x90, 0xe7, 0xb5, 0xde, 0xb9, 0x71,
0x04, 0x70, 0x74, 0x07, 0xb3, 0xde, 0x47, 0xa8, 0x81, 0x10, 0x22, 0xf3, 0x84, 0x8d, 0x30, 0x8d,
0x71, 0x5f, 0x5e, 0xdf, 0x37, 0x31, 0x8d, 0x71, 0xdf, 0xfd, 0xad, 0x04, 0xab, 0x27, 0xaa, 0x90,
0x0d, 0x68, 0xf8, 0xa3, 0x34, 0xc5, 0x98, 0xe7, 0x81, 0x50, 0xd7, 0x3c, 0x39, 0xc9, 0x35, 0xa8,
0xc5, 0x78, 0xc0, 0xf3, 0x23, 0x9f, 0x13, 0x8c, 0x53, 0xc6, 0xbc, 0x09, 0xcd, 0x02, 0x5c, 0x64,
0x27, 0xce, 0x38, 0x96, 0x45, 0x0b, 0xf2, 0x39, 0x00, 0xcd, 0xd2, 0xd4, 0xc7, 0xf6, 0x83, 0x73,
0x16, 0xde, 0x7a, 0x12, 0x07, 0x78, 0x80, 0xc1, 0x66, 0x6e, 0x0b, 0x79, 0x39, 0x77, 0xce, 0x87,
0xb0, 0x34, 0x45, 0x45, 0x14, 0x13, 0x0a, 0xb6, 0xec, 0x42, 0xc5, 0x53, 0x44, 0x06, 0x8d, 0x52,
0x0e, 0xb3, 0x77, 0xe0, 0xca, 0xc7, 0x34, 0xdd, 0xcb, 0x43, 0x68, 0x93, 0x79, 0x48, 0x03, 0xf3,
0xd4, 0xa6, 0xe0, 0xc9, 0x5d, 0x87, 0xab, 0x27, 0x19, 0x29, 0xc4, 0xba, 0x04, 0x16, 0xb7, 0x91,
0xeb, 0x07, 0xad, 0x3c, 0xb9, 0x5b, 0x70, 0x21, 0xc7, 0x7b, 0xeb, 0xbd, 0xd0, 0xf9, 0xc7, 0x82,
0x05, 0x53, 0xed, 0x0b, 0x4c, 0xc7, 0xa1, 0x8f, 0x64, 0x04, 0xf5, 0xdc, 0x0d, 0x20, 0xeb, 0xa7,
0x9c, 0x07, 0x99, 0x8c, 0xb3, 0x71, 0xe6, 0x01, 0x71, 0x37, 0xbe, 0xfe, 0xfd, 0xaf, 0xef, 0x4a,
0x6b, 0x64, 0xb5, 0x6d, 0x8e, 0x40, 0xfb, 0x55, 0xe1, 0x46, 0xbc, 0x26, 0x7b, 0xd0, 0xc8, 0x6f,
0x3b, 0xb2, 0x71, 0xe6, 0xf2, 0x75, 0xdc, 0xd3, 0x54, 0x74, 0xe4, 0x65, 0x19, 0x79, 0xde, 0xad,
0x65, 0x91, 0xef, 0x5b, 0xb7, 0x3a, 0x3f, 0x94, 0x60, 0x29, 0xdf, 0x72, 0x53, 0xfb, 0x6b, 0x58,
0x38, 0xb2, 0x38, 0xc8, 0xff, 0xcf, 0xd8, 0x2b, 0x2a, 0x95, 0xeb, 0xe7, 0xda, 0x3e, 0xee, 0x15,
0x99, 0xcd, 0x25, 0x72, 0xb1, 0x9d, 0xdf, 0x3c, 0xac, 0xfd, 0x4a, 0xf5, 0xe0, 0x5b, 0x0b, 0x56,
0xa6, 0xa3, 0x81, 0x1c, 0xb9, 0x83, 0xa7, 0x02, 0xcd, 0x79, 0xf7, 0x7c, 0xca, 0xc5, 0xa4, 0x6e,
0x4d, 0x4f, 0xaa, 0x13, 0x43, 0x53, 0xa1, 0xc6, 0x34, 0xe9, 0x0b, 0xa8, 0x65, 0xe0, 0x23, 0x57,
0x8f, 0x15, 0x5e, 0x40, 0xaa, 0x73, 0xed, 0x44, 0xb9, 0x8e, 0xbe, 0x20, 0xa3, 0xd7, 0x48, 0xb5,
0xad, 0x30, 0xf9, 0xe0, 0x2a, 0x2c, 0xf9, 0xc9, 0xa0, 0x68, 0x36, 0xec, 0x7d, 0x56, 0xd5, 0xff,
0x71, 0xf5, 0x66, 0xe5, 0x1f, 0xaa, 0x77, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x7b, 0xd2, 0x74,
0xfa, 0x8a, 0x0d, 0x00, 0x00,
// 1345 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4b, 0x6f, 0xdb, 0xc6,
0x16, 0xbe, 0x94, 0x23, 0x4b, 0x3a, 0x92, 0x6c, 0x65, 0xec, 0xd8, 0x32, 0x9d, 0x87, 0xcd, 0x7b,
0x83, 0x9b, 0x9b, 0x5b, 0x48, 0xa8, 0x92, 0x02, 0x49, 0xba, 0x28, 0x14, 0x5b, 0x76, 0x0d, 0x24,
0xae, 0x41, 0x3b, 0x06, 0xda, 0xa2, 0x60, 0xc7, 0xe2, 0x91, 0x4d, 0x98, 0x22, 0x59, 0x72, 0x64,
0x47, 0x08, 0xd2, 0x45, 0x77, 0xdd, 0xb6, 0x8b, 0xfe, 0x86, 0x6e, 0xba, 0xe9, 0x4f, 0xe8, 0xbe,
0x8b, 0x76, 0xdb, 0xee, 0xba, 0xe8, 0x1f, 0xe8, 0xa2, 0xbb, 0x62, 0x1e, 0xa4, 0x49, 0x9b, 0x7e,
0x24, 0x2b, 0xcd, 0x9c, 0x39, 0xcf, 0x6f, 0xbe, 0x39, 0x87, 0x02, 0x9d, 0x06, 0x4e, 0xfb, 0xe8,
0x41, 0xbb, 0xef, 0x52, 0x27, 0x0c, 0xf6, 0xe4, 0x6f, 0x2b, 0x08, 0x7d, 0xe6, 0x93, 0x5a, 0xdf,
0x0f, 0xd1, 0x8f, 0x5a, 0x42, 0xa6, 0xdf, 0xd9, 0xf7, 0xfd, 0x7d, 0x17, 0xdb, 0xe2, 0x6c, 0x6f,
0x34, 0x68, 0x33, 0x67, 0x88, 0x11, 0xa3, 0xc3, 0x40, 0xaa, 0xeb, 0x37, 0x95, 0x02, 0xf7, 0x48,
0x3d, 0xcf, 0x67, 0x94, 0x39, 0xbe, 0x17, 0xc9, 0x53, 0xe3, 0xbb, 0x02, 0xd4, 0x77, 0x47, 0xae,
0x87, 0x21, 0xdd, 0x73, 0x5c, 0x87, 0x8d, 0x09, 0x81, 0x6b, 0x1e, 0x1d, 0x62, 0x53, 0x5b, 0xd2,
0xee, 0x55, 0x4c, 0xb1, 0x26, 0x77, 0x61, 0x8a, 0xff, 0x46, 0x01, 0xed, 0xa3, 0x25, 0x4e, 0x0b,
0xe2, 0xb4, 0x9e, 0x48, 0x37, 0xb9, 0xda, 0x12, 0x54, 0x6d, 0x8c, 0xfa, 0xa1, 0x13, 0xf0, 0x10,
0xcd, 0x09, 0xa1, 0x93, 0x16, 0x71, 0xe7, 0xae, 0xe3, 0x1d, 0x36, 0xaf, 0x49, 0xe7, 0x7c, 0x4d,
0x74, 0x28, 0x47, 0x78, 0x84, 0xa1, 0xc3, 0xc6, 0xcd, 0xa2, 0x90, 0x27, 0x7b, 0x7e, 0x36, 0x44,
0x46, 0x6d, 0xca, 0x68, 0x73, 0x52, 0x9e, 0xc5, 0x7b, 0xb2, 0x00, 0xe5, 0x81, 0xf3, 0x12, 0x6d,
0x6b, 0x6f, 0xdc, 0x2c, 0x89, 0xb3, 0x92, 0xd8, 0x3f, 0x1d, 0x93, 0xa7, 0x70, 0x9d, 0x0e, 0x06,
0xd8, 0x67, 0x68, 0x5b, 0x47, 0x18, 0x46, 0xbc, 0xe0, 0x66, 0x79, 0x69, 0xe2, 0x5e, 0xb5, 0x73,
0xa3, 0x95, 0x86, 0xaf, 0xb5, 0x86, 0x94, 0x8d, 0x42, 0x34, 0x1b, 0xb1, 0xfe, 0xae, 0x52, 0x37,
0x7e, 0xd6, 0xa0, 0xbc, 0x8a, 0x0c, 0xfb, 0xcc, 0x0f, 0x73, 0x41, 0x69, 0x42, 0x49, 0xf9, 0x56,
0x68, 0xc4, 0x5b, 0xd2, 0x81, 0xa2, 0xcd, 0xc6, 0x01, 0x0a, 0x04, 0xa6, 0x3a, 0x37, 0xb3, 0x21,
0x63, 0xa7, 0xad, 0xd5, 0x9d, 0x71, 0x80, 0xa6, 0x54, 0x35, 0x3e, 0x87, 0xa2, 0xd8, 0x93, 0x45,
0x98, 0x5f, 0xed, 0xed, 0xf4, 0x56, 0x76, 0x3e, 0x32, 0xad, 0x55, 0x6b, 0xe7, 0xe3, 0xad, 0x9e,
0xb5, 0xb1, 0xb9, 0xdb, 0x7d, 0xb6, 0xb1, 0xda, 0xf8, 0x17, 0xb9, 0x05, 0x0b, 0xa7, 0x0f, 0x37,
0xbb, 0xcf, 0x7b, 0xdb, 0x5b, 0xdd, 0x95, 0x5e, 0x43, 0xcb, 0xb3, 0x5d, 0xeb, 0x75, 0x77, 0x5e,
0x98, 0xbd, 0x46, 0xc1, 0xd8, 0x86, 0xca, 0x66, 0x7c, 0x5d, 0xb9, 0x05, 0x75, 0xa0, 0x6c, 0xab,
0xdc, 0x44, 0x45, 0xd5, 0xce, 0x5c, 0x7e, 0xe6, 0x66, 0xa2, 0x67, 0xfc, 0x58, 0x80, 0x92, 0xc2,
0x30, 0xd7, 0xe7, 0x7b, 0x50, 0x49, 0x38, 0xa2, 0x9c, 0xce, 0x67, 0x9d, 0x26, 0x39, 0x99, 0x27,
0x9a, 0x69, 0x6c, 0x27, 0xb2, 0xd8, 0xde, 0x85, 0x29, 0xb5, 0xb4, 0x06, 0x7e, 0x38, 0xa4, 0x4c,
0x71, 0xa9, 0xae, 0xa4, 0x6b, 0x42, 0x98, 0xa9, 0xa5, 0x78, 0xb5, 0x5a, 0x48, 0x0f, 0xa6, 0x8f,
0x52, 0x4f, 0xc1, 0xc1, 0xa8, 0x39, 0x29, 0x38, 0xb3, 0x98, 0x35, 0xcd, 0xbc, 0x17, 0xf3, 0xb4,
0x0d, 0x59, 0x86, 0xda, 0x40, 0x22, 0x62, 0x09, 0x12, 0x48, 0x6e, 0x56, 0x95, 0x8c, 0xdf, 0xb1,
0xb1, 0x08, 0xc5, 0x67, 0x74, 0x8c, 0x82, 0x57, 0x07, 0x34, 0x3a, 0x88, 0x21, 0xe3, 0x6b, 0xe3,
0x6b, 0x0d, 0xaa, 0x2b, 0x3c, 0xd0, 0x36, 0xa3, 0x6c, 0x14, 0x91, 0x87, 0x50, 0x89, 0x53, 0x8c,
0x9a, 0x9a, 0x48, 0xe8, 0xbc, 0x5a, 0x4e, 0x14, 0xc9, 0x2a, 0x34, 0x5c, 0x1a, 0x31, 0x6b, 0x14,
0xd8, 0x94, 0xa1, 0xc5, 0xbb, 0x82, 0xc2, 0x5f, 0x6f, 0xc9, 0x8e, 0xd0, 0x8a, 0x5b, 0x46, 0x6b,
0x27, 0x6e, 0x19, 0xe6, 0x14, 0xb7, 0x79, 0x21, 0x4c, 0xb8, 0xd0, 0x78, 0x0c, 0x64, 0x1d, 0x59,
0xd7, 0xeb, 0x63, 0xc4, 0xc2, 0xb1, 0x89, 0x5f, 0x8c, 0x30, 0x62, 0xe4, 0xdf, 0x50, 0xa7, 0x4a,
0x64, 0xa5, 0x6e, 0xbc, 0x16, 0x0b, 0xf9, 0x95, 0x1a, 0x7f, 0x17, 0x60, 0x26, 0x63, 0x1b, 0x05,
0xbe, 0x17, 0x21, 0x59, 0x83, 0x72, 0xac, 0x27, 0xec, 0xaa, 0x9d, 0xfb, 0xd9, 0x6a, 0x72, 0x8c,
0x5a, 0x89, 0x20, 0xb1, 0x25, 0xef, 0xc2, 0x64, 0x24, 0x00, 0x52, 0x65, 0x2d, 0x64, 0xbd, 0xa4,
0x10, 0x34, 0x95, 0xa2, 0xfe, 0x25, 0xd4, 0x63, 0x47, 0x12, 0xfe, 0xff, 0x41, 0xd1, 0xe5, 0x0b,
0x95, 0xc8, 0x4c, 0xd6, 0x85, 0xd0, 0x31, 0xa5, 0x06, 0x6f, 0x29, 0x12, 0x5c, 0xb4, 0x2d, 0x75,
0x95, 0x3c, 0xf2, 0x45, 0x2d, 0x25, 0xd6, 0x57, 0x82, 0x48, 0xdf, 0x87, 0x72, 0x1c, 0x3f, 0xf7,
0xb1, 0xac, 0xc3, 0xa4, 0x08, 0x16, 0x35, 0x27, 0x84, 0xe3, 0xf6, 0xd5, 0x81, 0x91, 0xb9, 0x2a,
0x73, 0xe3, 0xf7, 0x02, 0xcc, 0x6c, 0xf9, 0xd1, 0x5b, 0x5d, 0x1c, 0x99, 0x83, 0x49, 0xf5, 0xb2,
0x64, 0x5b, 0x53, 0x3b, 0xb2, 0x72, 0x2a, 0xbb, 0xff, 0x67, 0xb3, 0xcb, 0x89, 0x27, 0x64, 0x99,
0xcc, 0xf4, 0x9f, 0x34, 0xa8, 0x24, 0xd2, 0x3c, 0xfa, 0x73, 0x59, 0x40, 0xd9, 0x81, 0x0a, 0x2e,
0xd6, 0xc4, 0x84, 0xd2, 0x01, 0x52, 0xfb, 0x24, 0xf6, 0xa3, 0x37, 0x88, 0xdd, 0xfa, 0x50, 0x9a,
0xf6, 0x3c, 0x7e, 0x1a, 0x3b, 0xd2, 0x9f, 0x40, 0x2d, 0x7d, 0x40, 0x1a, 0x30, 0x71, 0x88, 0x63,
0x95, 0x0a, 0x5f, 0x92, 0x59, 0x28, 0x1e, 0x51, 0x77, 0x14, 0x0f, 0x3b, 0xb9, 0x79, 0x52, 0x78,
0xa4, 0x19, 0x1b, 0x30, 0x9b, 0x0d, 0xa9, 0xb8, 0x7d, 0xc2, 0x49, 0xed, 0x8a, 0x9c, 0x34, 0x7e,
0xd0, 0x60, 0x6e, 0x1d, 0xd9, 0xa6, 0xcf, 0x9c, 0x81, 0xd3, 0x17, 0xb3, 0x39, 0xbe, 0xad, 0x87,
0x30, 0xe7, 0xbb, 0xb6, 0x95, 0xee, 0x2f, 0x63, 0x2b, 0xa0, 0xfb, 0xf1, 0xb5, 0xcd, 0xfa, 0xae,
0x9d, 0xe9, 0x45, 0x5b, 0x74, 0x1f, 0xb9, 0x95, 0x87, 0xc7, 0x79, 0x56, 0xb2, 0x8c, 0x59, 0x0f,
0x8f, 0xcf, 0x5a, 0xcd, 0x42, 0xd1, 0x75, 0x86, 0x0e, 0x13, 0xed, 0xb6, 0x68, 0xca, 0x4d, 0x42,
0xd2, 0x6b, 0x27, 0x24, 0x35, 0x7e, 0x2b, 0xc0, 0xfc, 0x99, 0x84, 0x55, 0xfd, 0xbb, 0x50, 0xf3,
0x52, 0x72, 0x85, 0x42, 0xe7, 0x0c, 0x8d, 0xf3, 0x8c, 0x5b, 0x19, 0x61, 0xc6, 0x8f, 0xfe, 0xa7,
0x06, 0xb5, 0xf4, 0xf1, 0x79, 0xf3, 0xb8, 0x1f, 0x22, 0x65, 0x68, 0xc7, 0xf3, 0x58, 0x6d, 0xf9,
0x57, 0x84, 0x74, 0x87, 0xb6, 0x1a, 0x27, 0xc9, 0x9e, 0x5b, 0xd9, 0xe8, 0x22, 0xb7, 0x92, 0x55,
0xc6, 0x5b, 0xf2, 0x18, 0x26, 0x7c, 0xd7, 0x56, 0xd3, 0xe3, 0xbf, 0xa7, 0x08, 0x47, 0xf7, 0x31,
0xc1, 0xde, 0x45, 0x45, 0x04, 0x07, 0x23, 0x93, 0xdb, 0x70, 0x53, 0x0f, 0x8f, 0xc5, 0x17, 0xcb,
0x9b, 0x98, 0x7a, 0x78, 0x6c, 0xfc, 0x52, 0x80, 0x85, 0x73, 0x55, 0xf8, 0x6c, 0xe9, 0x8f, 0xc2,
0x10, 0x3d, 0x96, 0x26, 0x42, 0x55, 0xc9, 0xc4, 0x4d, 0x2e, 0x42, 0xc5, 0xc3, 0x97, 0x2c, 0x7d,
0xe5, 0x65, 0x2e, 0xb8, 0xe0, 0x9a, 0xbb, 0x50, 0xcf, 0xd0, 0x45, 0x20, 0x71, 0xc9, 0xd8, 0xcb,
0x5a, 0x90, 0x4f, 0x01, 0x68, 0x92, 0x66, 0xb3, 0x28, 0x1e, 0xe9, 0xfb, 0x57, 0x2c, 0xbc, 0xb5,
0xe1, 0xd9, 0xf8, 0x12, 0xed, 0x6e, 0xaa, 0x0b, 0x99, 0x29, 0x77, 0xfa, 0x07, 0x30, 0x93, 0xa3,
0xc2, 0x8b, 0x71, 0xb8, 0x58, 0xa0, 0x50, 0x34, 0xe5, 0x26, 0xa1, 0x46, 0x21, 0xc5, 0xd9, 0x07,
0x70, 0xeb, 0x39, 0x0d, 0x0f, 0xd3, 0x14, 0xea, 0x46, 0x26, 0x52, 0x3b, 0x7e, 0x6a, 0x39, 0x7c,
0x32, 0x96, 0xe0, 0xf6, 0x79, 0x46, 0x92, 0xb1, 0x06, 0x81, 0xc6, 0x3a, 0x32, 0xf5, 0xa0, 0xa5,
0x27, 0x63, 0x0d, 0xae, 0xa7, 0x64, 0x6f, 0xdd, 0x17, 0x3a, 0x7f, 0x69, 0x30, 0x1d, 0x57, 0xbb,
0x8d, 0xe1, 0x91, 0xd3, 0x47, 0x32, 0x82, 0x6a, 0x6a, 0x06, 0x90, 0xa5, 0x0b, 0xc6, 0x83, 0x48,
0x46, 0x5f, 0xbe, 0x74, 0x80, 0x18, 0xcb, 0x5f, 0xfd, 0xfa, 0xc7, 0xb7, 0x85, 0x45, 0xb2, 0xd0,
0x8e, 0x87, 0x40, 0xfb, 0x55, 0x66, 0x46, 0xbc, 0x26, 0x87, 0x50, 0x4b, 0x77, 0x3b, 0xb2, 0x7c,
0x69, 0xf3, 0xd5, 0x8d, 0x8b, 0x54, 0x54, 0xe4, 0x59, 0x11, 0x79, 0xca, 0xa8, 0x24, 0x91, 0x9f,
0x68, 0xf7, 0x3b, 0x1e, 0xd4, 0x25, 0x12, 0x71, 0xd1, 0x9f, 0x41, 0x25, 0x01, 0x94, 0xdc, 0x3e,
0x53, 0x50, 0x06, 0x7d, 0xfd, 0xce, 0xb9, 0xe7, 0x2a, 0xe8, 0xb4, 0x08, 0x5a, 0x21, 0xa5, 0xb6,
0xc2, 0xf9, 0xfb, 0x02, 0xcc, 0xa4, 0xaf, 0x38, 0x0e, 0xfb, 0x1a, 0xa6, 0x4f, 0x35, 0x2a, 0xf2,
0x9f, 0x4b, 0xfa, 0x98, 0x4c, 0xe1, 0xee, 0x95, 0xba, 0x9d, 0x71, 0x4b, 0x24, 0x32, 0x4f, 0x6e,
0xb4, 0xd3, 0x9d, 0x2e, 0x6a, 0xbf, 0x92, 0x98, 0x7f, 0xa3, 0xc1, 0x5c, 0x3e, 0xfb, 0xc8, 0xa9,
0xb9, 0x7b, 0x21, 0xb1, 0xf5, 0x77, 0xae, 0xa6, 0x9c, 0x4d, 0xea, 0x7e, 0x7e, 0x52, 0x4f, 0x6f,
0xc3, 0x4c, 0xdf, 0x1f, 0x66, 0x3d, 0x06, 0x7b, 0x9f, 0x94, 0xd4, 0xbf, 0xd4, 0xbd, 0x49, 0xf1,
0x45, 0xf9, 0xe0, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1b, 0x4b, 0xee, 0xfe, 0xbe, 0x0e, 0x00,
0x00,
}

View file

@ -70,6 +70,15 @@ func request_AncestryService_PostAncestry_0(ctx context.Context, marshaler runti
}
func request_StatusService_GetStatus_0(ctx context.Context, marshaler runtime.Marshaler, client StatusServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetStatusRequest
var metadata runtime.ServerMetadata
msg, err := client.GetStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_NotificationService_GetNotification_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
@ -132,15 +141,6 @@ func request_NotificationService_MarkNotificationAsRead_0(ctx context.Context, m
}
func request_StatusService_GetStatus_0(ctx context.Context, marshaler runtime.Marshaler, client StatusServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetStatusRequest
var metadata runtime.ServerMetadata
msg, err := client.GetStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterAncestryServiceHandlerFromEndpoint is same as RegisterAncestryServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAncestryServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@ -252,6 +252,84 @@ var (
forward_AncestryService_PostAncestry_0 = runtime.ForwardResponseMessage
)
// RegisterStatusServiceHandlerFromEndpoint is same as RegisterStatusServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterStatusServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterStatusServiceHandler(ctx, mux, conn)
}
// RegisterStatusServiceHandler registers the http handlers for service StatusService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterStatusServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterStatusServiceHandlerClient(ctx, mux, NewStatusServiceClient(conn))
}
// RegisterStatusServiceHandler registers the http handlers for service StatusService to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "StatusServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "StatusServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "StatusServiceClient" to call the correct interceptors.
func RegisterStatusServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client StatusServiceClient) error {
mux.Handle("GET", pattern_StatusService_GetStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_StatusService_GetStatus_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_StatusService_GetStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_StatusService_GetStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"status"}, ""))
)
var (
forward_StatusService_GetStatus_0 = runtime.ForwardResponseMessage
)
// RegisterNotificationServiceHandlerFromEndpoint is same as RegisterNotificationServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterNotificationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@ -362,81 +440,3 @@ var (
forward_NotificationService_MarkNotificationAsRead_0 = runtime.ForwardResponseMessage
)
// RegisterStatusServiceHandlerFromEndpoint is same as RegisterStatusServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterStatusServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterStatusServiceHandler(ctx, mux, conn)
}
// RegisterStatusServiceHandler registers the http handlers for service StatusService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterStatusServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterStatusServiceHandlerClient(ctx, mux, NewStatusServiceClient(conn))
}
// RegisterStatusServiceHandler registers the http handlers for service StatusService to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "StatusServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "StatusServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "StatusServiceClient" to call the correct interceptors.
func RegisterStatusServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client StatusServiceClient) error {
mux.Handle("GET", pattern_StatusService_GetStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_StatusService_GetStatus_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_StatusService_GetStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_StatusService_GetStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"status"}, ""))
)
var (
forward_StatusService_GetStatus_0 = runtime.ForwardResponseMessage
)

View file

@ -0,0 +1,260 @@
// Copyright 2018 clair authors
//
// 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
//
// http://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.
syntax = "proto3";
import "google/protobuf/timestamp.proto";
import "google/api/annotations.proto";
package coreos.clair;
option go_package = "clairpb";
option java_package = "com.coreos.clair.pb";
service AncestryService {
// The RPC used to read the results of scanning for a particular ancestry.
rpc GetAncestry(GetAncestryRequest) returns (GetAncestryResponse) {
option (google.api.http) = {
get: "/ancestry/{ancestry_name}"
};
}
// The RPC used to create a new scan of an ancestry.
rpc PostAncestry(PostAncestryRequest) returns (PostAncestryResponse) {
option (google.api.http) = {
post: "/ancestry"
body: "*"
};
}
}
service StatusService {
// The RPC used to show the internal state of current Clair instance.
rpc GetStatus(GetStatusRequest) returns (GetStatusResponse) {
option (google.api.http) = {
get: "/status"
};
}
}
service NotificationService {
// The RPC used to get a particularly Notification.
rpc GetNotification(GetNotificationRequest)
returns (GetNotificationResponse) {
option (google.api.http) = {
get: "/notifications/{name}"
};
}
// The RPC used to mark a Notification as read after it has been processed.
rpc MarkNotificationAsRead(MarkNotificationAsReadRequest)
returns (MarkNotificationAsReadResponse) {
option (google.api.http) = {
delete: "/notifications/{name}"
};
}
}
message Vulnerability {
// The name of the vulnerability.
string name = 1;
// The name of the namespace in which the vulnerability was detected.
string namespace_name = 2;
// A description of the vulnerability according to the source for the
// namespace.
string description = 3;
// A link to the vulnerability according to the source for the namespace.
string link = 4;
// How dangerous the vulnerability is.
string severity = 5;
// Namespace agnostic metadata about the vulnerability.
string metadata = 6;
// The feature that fixes this vulnerability.
// This field only exists when a vulnerability is a part of a Feature.
string fixed_by = 7;
// The Features that are affected by the vulnerability.
// This field only exists when a vulnerability is a part of a Notification.
repeated Feature affected_versions = 8;
}
message Detector {
enum DType {
DETECTOR_D_TYPE_INVALID = 0;
DETECTOR_D_TYPE_NAMESPACE = 1;
DETECTOR_D_TYPE_FEATURE = 2;
}
// The name of the detector.
string name = 1;
// The version of the detector.
string version = 2;
// The type of the detector.
DType dtype = 3;
}
message Namespace {
// The name of the namespace.
string name = 1;
// The detector used to detect the namespace. This only exists when present
// in an Ancestry Feature.
Detector detector = 2;
}
message Feature {
// The name of the feature.
string name = 1;
// The namespace in which the feature is detected.
Namespace namespace = 2;
// The specific version of this feature.
string version = 3;
// The format used to parse version numbers for the feature.
string version_format = 4;
// The detector used to detect this feature. This only exists when present
// in an Ancestry.
Detector detector = 5;
// The list of vulnerabilities that affect the feature.
repeated Vulnerability vulnerabilities = 6;
// The feature type indicates if the feature represents a source package or
// binary package.
string feature_type = 7;
}
message Layer {
// The sha256 tarsum for the layer.
string hash = 1;
}
message ClairStatus {
// The implemented detectors in this Clair instance
repeated Detector detectors = 1;
// The time at which the updater last ran.
google.protobuf.Timestamp last_update_time = 2;
}
message GetAncestryRequest {
// The name of the desired ancestry.
string ancestry_name = 1;
}
message GetAncestryResponse {
message AncestryLayer {
// The layer's information.
Layer layer = 1;
// The features detected in this layer.
repeated Feature detected_features = 2;
}
message Ancestry {
// The name of the desired ancestry.
string name = 1;
// The list of layers along with detected features in each.
repeated AncestryLayer layers = 3;
}
// The ancestry requested.
Ancestry ancestry = 1;
// The status of Clair at the time of the request
ClairStatus status = 2;
}
message PostAncestryRequest {
message PostLayer {
// The hash of the layer.
string hash = 1;
// The location of the layer (URL or file path).
string path = 2;
// Any HTTP Headers that need to be used if requesting a layer over
// HTTP(S).
map<string, string> headers = 3;
}
// The name of the ancestry being scanned.
// If scanning OCI images, this should be the hash of the manifest.
string ancestry_name = 1;
// The format of the image being uploaded.
string format = 2;
// The layers to be scanned for this Ancestry, ordered in the way that i th
// layer is the parent of i + 1 th layer.
repeated PostLayer layers = 3;
}
message PostAncestryResponse {
// The status of Clair at the time of the request.
ClairStatus status = 1;
}
message GetNotificationRequest {
// The current page of previous vulnerabilities for the ancestry.
// This will be empty when it is the first page.
string old_vulnerability_page = 1;
// The current page of vulnerabilities for the ancestry.
// This will be empty when it is the first page.
string new_vulnerability_page = 2;
// The requested maximum number of results per page.
int32 limit = 3;
// The name of the notification being requested.
string name = 4;
}
message GetNotificationResponse {
message Notification {
// The name of the requested notification.
string name = 1;
// The time at which the notification was created.
string created = 2;
// The time at which the notification was last sent out.
string notified = 3;
// The time at which a notification has been deleted.
string deleted = 4;
// The previous vulnerability and a paginated view of the ancestries it
// affects.
PagedVulnerableAncestries old = 5;
// The newly updated vulnerability and a paginated view of the
// ancestries it affects.
PagedVulnerableAncestries new = 6;
}
// The notification as requested.
Notification notification = 1;
}
message PagedVulnerableAncestries {
message IndexedAncestryName {
// The index is an ever increasing number associated with the particular
// ancestry. This is useful if you're processing notifications, and need
// to keep track of the progress of paginating the results.
int32 index = 1;
// The name of the ancestry.
string name = 2;
}
// The identifier for the current page.
string current_page = 1;
// The token used to request the next page.
// This will be empty when there are no more pages.
string next_page = 2;
// The requested maximum number of results per page.
int32 limit = 3;
// The vulnerability that affects a given set of ancestries.
Vulnerability vulnerability = 4;
// The ancestries affected by a vulnerability.
repeated IndexedAncestryName ancestries = 5;
}
message MarkNotificationAsReadRequest {
// The name of the Notification that has been processed.
string name = 1;
}
message MarkNotificationAsReadResponse {}
message GetStatusRequest {}
message GetStatusResponse {
// The status of the current Clair instance.
ClairStatus status = 1;
}

View file

@ -0,0 +1,488 @@
{
"swagger": "2.0",
"info": {
"title": "api/v3/clairpb/clair.proto",
"version": "version not set"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/ancestry": {
"post": {
"summary": "The RPC used to create a new scan of an ancestry.",
"operationId": "PostAncestry",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/clairPostAncestryResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/clairPostAncestryRequest"
}
}
],
"tags": [
"AncestryService"
]
}
},
"/ancestry/{ancestry_name}": {
"get": {
"summary": "The RPC used to read the results of scanning for a particular ancestry.",
"operationId": "GetAncestry",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/clairGetAncestryResponse"
}
}
},
"parameters": [
{
"name": "ancestry_name",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"AncestryService"
]
}
},
"/notifications/{name}": {
"get": {
"summary": "The RPC used to get a particularly Notification.",
"operationId": "GetNotification",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/clairGetNotificationResponse"
}
}
},
"parameters": [
{
"name": "name",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "old_vulnerability_page",
"description": "The current page of previous vulnerabilities for the ancestry.\nThis will be empty when it is the first page.",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "new_vulnerability_page",
"description": "The current page of vulnerabilities for the ancestry.\nThis will be empty when it is the first page.",
"in": "query",
"required": false,
"type": "string"
},
{
"name": "limit",
"description": "The requested maximum number of results per page.",
"in": "query",
"required": false,
"type": "integer",
"format": "int32"
}
],
"tags": [
"NotificationService"
]
},
"delete": {
"summary": "The RPC used to mark a Notification as read after it has been processed.",
"operationId": "MarkNotificationAsRead",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/clairMarkNotificationAsReadResponse"
}
}
},
"parameters": [
{
"name": "name",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"NotificationService"
]
}
},
"/status": {
"get": {
"summary": "The RPC used to show the internal state of current Clair instance.",
"operationId": "GetStatus",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/clairGetStatusResponse"
}
}
},
"tags": [
"StatusService"
]
}
}
},
"definitions": {
"DetectorDType": {
"type": "string",
"enum": [
"DETECTOR_D_TYPE_INVALID",
"DETECTOR_D_TYPE_NAMESPACE",
"DETECTOR_D_TYPE_FEATURE"
],
"default": "DETECTOR_D_TYPE_INVALID"
},
"GetAncestryResponseAncestry": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the desired ancestry."
},
"layers": {
"type": "array",
"items": {
"$ref": "#/definitions/GetAncestryResponseAncestryLayer"
},
"description": "The list of layers along with detected features in each."
}
}
},
"GetAncestryResponseAncestryLayer": {
"type": "object",
"properties": {
"layer": {
"$ref": "#/definitions/clairLayer",
"description": "The layer's information."
},
"detected_features": {
"type": "array",
"items": {
"$ref": "#/definitions/clairFeature"
},
"description": "The features detected in this layer."
}
}
},
"GetNotificationResponseNotification": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the requested notification."
},
"created": {
"type": "string",
"description": "The time at which the notification was created."
},
"notified": {
"type": "string",
"description": "The time at which the notification was last sent out."
},
"deleted": {
"type": "string",
"description": "The time at which a notification has been deleted."
},
"old": {
"$ref": "#/definitions/clairPagedVulnerableAncestries",
"description": "The previous vulnerability and a paginated view of the ancestries it\naffects."
},
"new": {
"$ref": "#/definitions/clairPagedVulnerableAncestries",
"description": "The newly updated vulnerability and a paginated view of the\nancestries it affects."
}
}
},
"PagedVulnerableAncestriesIndexedAncestryName": {
"type": "object",
"properties": {
"index": {
"type": "integer",
"format": "int32",
"description": "The index is an ever increasing number associated with the particular\nancestry. This is useful if you're processing notifications, and need\nto keep track of the progress of paginating the results."
},
"name": {
"type": "string",
"description": "The name of the ancestry."
}
}
},
"PostAncestryRequestPostLayer": {
"type": "object",
"properties": {
"hash": {
"type": "string",
"description": "The hash of the layer."
},
"path": {
"type": "string",
"description": "The location of the layer (URL or file path)."
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "Any HTTP Headers that need to be used if requesting a layer over\nHTTP(S)."
}
}
},
"clairClairStatus": {
"type": "object",
"properties": {
"detectors": {
"type": "array",
"items": {
"$ref": "#/definitions/clairDetector"
},
"title": "The implemented detectors in this Clair instance"
},
"last_update_time": {
"type": "string",
"format": "date-time",
"description": "The time at which the updater last ran."
}
}
},
"clairDetector": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the detector."
},
"version": {
"type": "string",
"description": "The version of the detector."
},
"dtype": {
"$ref": "#/definitions/DetectorDType",
"description": "The type of the detector."
}
}
},
"clairFeature": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the feature."
},
"namespace": {
"$ref": "#/definitions/clairNamespace",
"description": "The namespace in which the feature is detected."
},
"version": {
"type": "string",
"description": "The specific version of this feature."
},
"version_format": {
"type": "string",
"description": "The format used to parse version numbers for the feature."
},
"detector": {
"$ref": "#/definitions/clairDetector",
"description": "The detector used to detect this feature. This only exists when present\nin an Ancestry."
},
"vulnerabilities": {
"type": "array",
"items": {
"$ref": "#/definitions/clairVulnerability"
},
"description": "The list of vulnerabilities that affect the feature."
},
"feature_type": {
"type": "string",
"description": "The feature type indicates if the feature represents a source package or\nbinary package."
}
}
},
"clairGetAncestryResponse": {
"type": "object",
"properties": {
"ancestry": {
"$ref": "#/definitions/GetAncestryResponseAncestry",
"description": "The ancestry requested."
},
"status": {
"$ref": "#/definitions/clairClairStatus",
"title": "The status of Clair at the time of the request"
}
}
},
"clairGetNotificationResponse": {
"type": "object",
"properties": {
"notification": {
"$ref": "#/definitions/GetNotificationResponseNotification",
"description": "The notification as requested."
}
}
},
"clairGetStatusResponse": {
"type": "object",
"properties": {
"status": {
"$ref": "#/definitions/clairClairStatus",
"description": "The status of the current Clair instance."
}
}
},
"clairLayer": {
"type": "object",
"properties": {
"hash": {
"type": "string",
"description": "The sha256 tarsum for the layer."
}
}
},
"clairMarkNotificationAsReadResponse": {
"type": "object"
},
"clairNamespace": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the namespace."
},
"detector": {
"$ref": "#/definitions/clairDetector",
"description": "The detector used to detect the namespace. This only exists when present\nin an Ancestry Feature."
}
}
},
"clairPagedVulnerableAncestries": {
"type": "object",
"properties": {
"current_page": {
"type": "string",
"description": "The identifier for the current page."
},
"next_page": {
"type": "string",
"description": "The token used to request the next page.\nThis will be empty when there are no more pages."
},
"limit": {
"type": "integer",
"format": "int32",
"description": "The requested maximum number of results per page."
},
"vulnerability": {
"$ref": "#/definitions/clairVulnerability",
"description": "The vulnerability that affects a given set of ancestries."
},
"ancestries": {
"type": "array",
"items": {
"$ref": "#/definitions/PagedVulnerableAncestriesIndexedAncestryName"
},
"description": "The ancestries affected by a vulnerability."
}
}
},
"clairPostAncestryRequest": {
"type": "object",
"properties": {
"ancestry_name": {
"type": "string",
"description": "The name of the ancestry being scanned.\nIf scanning OCI images, this should be the hash of the manifest."
},
"format": {
"type": "string",
"description": "The format of the image being uploaded."
},
"layers": {
"type": "array",
"items": {
"$ref": "#/definitions/PostAncestryRequestPostLayer"
},
"description": "The layers to be scanned for this Ancestry, ordered in the way that i th\nlayer is the parent of i + 1 th layer."
}
}
},
"clairPostAncestryResponse": {
"type": "object",
"properties": {
"status": {
"$ref": "#/definitions/clairClairStatus",
"description": "The status of Clair at the time of the request."
}
}
},
"clairVulnerability": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the vulnerability."
},
"namespace_name": {
"type": "string",
"description": "The name of the namespace in which the vulnerability was detected."
},
"description": {
"type": "string",
"description": "A description of the vulnerability according to the source for the\nnamespace."
},
"link": {
"type": "string",
"description": "A link to the vulnerability according to the source for the namespace."
},
"severity": {
"type": "string",
"description": "How dangerous the vulnerability is."
},
"metadata": {
"type": "string",
"description": "Namespace agnostic metadata about the vulnerability."
},
"fixed_by": {
"type": "string",
"description": "The feature that fixes this vulnerability.\nThis field only exists when a vulnerability is a part of a Feature."
},
"affected_versions": {
"type": "array",
"items": {
"$ref": "#/definitions/clairFeature"
},
"description": "The Features that are affected by the vulnerability.\nThis field only exists when a vulnerability is a part of a Notification."
}
}
}
}
}

View file

@ -22,6 +22,13 @@ import (
"github.com/coreos/clair/ext/versionfmt"
)
// DatabaseDetectorTypeMapping maps the database detector type to the integer
// enum proto.
var DatabaseDetectorTypeMapping = map[database.DetectorType]Detector_DType{
database.NamespaceDetectorType: Detector_DType(1),
database.FeatureDetectorType: Detector_DType(2),
}
// PagedVulnerableAncestriesFromDatabaseModel converts database
// PagedVulnerableAncestries to api PagedVulnerableAncestries and assigns
// indexes to ancestries.
@ -92,6 +99,7 @@ func NotificationFromDatabaseModel(dbNotification database.VulnerabilityNotifica
return &noti, nil
}
// VulnerabilityFromDatabaseModel converts database Vulnerability to api Vulnerability.
func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability) (*Vulnerability, error) {
metaString := ""
if dbVuln.Metadata != nil {
@ -112,6 +120,7 @@ func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability) (*Vulnerabili
}, nil
}
// VulnerabilityWithFixedInFromDatabaseModel converts database VulnerabilityWithFixedIn to api Vulnerability.
func VulnerabilityWithFixedInFromDatabaseModel(dbVuln database.VulnerabilityWithFixedIn) (*Vulnerability, error) {
vuln, err := VulnerabilityFromDatabaseModel(dbVuln.Vulnerability)
if err != nil {
@ -122,14 +131,8 @@ func VulnerabilityWithFixedInFromDatabaseModel(dbVuln database.VulnerabilityWith
return vuln, nil
}
// LayerFromDatabaseModel converts database layer to api layer.
func LayerFromDatabaseModel(dbLayer database.LayerMetadata) *Layer {
layer := Layer{Hash: dbLayer.Hash}
return &layer
}
// NamespacedFeatureFromDatabaseModel converts database namespacedFeature to api Feature.
func NamespacedFeatureFromDatabaseModel(feature database.NamespacedFeature) *Feature {
func NamespacedFeatureFromDatabaseModel(feature database.AncestryFeature) *Feature {
version := feature.Feature.Version
if version == versionfmt.MaxVersion {
version = "None"
@ -137,8 +140,35 @@ func NamespacedFeatureFromDatabaseModel(feature database.NamespacedFeature) *Fea
return &Feature{
Name: feature.Feature.Name,
NamespaceName: feature.Namespace.Name,
Namespace: &Namespace{
Name: feature.Namespace.Name,
Detector: DetectorFromDatabaseModel(feature.NamespaceBy),
},
VersionFormat: feature.Namespace.VersionFormat,
Version: version,
Detector: DetectorFromDatabaseModel(feature.FeatureBy),
FeatureType: string(feature.Type),
}
}
// DetectorFromDatabaseModel converts database detector to api detector.
func DetectorFromDatabaseModel(detector database.Detector) *Detector {
if !detector.Valid() {
return nil
}
return &Detector{
Name: detector.Name,
Version: detector.Version,
Dtype: DatabaseDetectorTypeMapping[detector.DType],
}
}
// DetectorsFromDatabaseModel converts database detectors to api detectors.
func DetectorsFromDatabaseModel(dbDetectors []database.Detector) []*Detector {
detectors := make([]*Detector, 0, len(dbDetectors))
for _, d := range dbDetectors {
detectors = append(detectors, DetectorFromDatabaseModel(d))
}
return detectors
}

View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Copyright 2018 clair authors
#
# 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
#
# http://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.
set -o errexit
set -o nounset
set -o pipefail
DOCKER_REPO_ROOT="$GOPATH/src/github.com/coreos/clair"
IMAGE=${IMAGE:-"quay.io/coreos/clair-gen-proto"}
docker run --rm -it \
-v "$DOCKER_REPO_ROOT":"$DOCKER_REPO_ROOT" \
-w "$DOCKER_REPO_ROOT" \
"$IMAGE" \
"./api/v3/clairpb/run_in_docker.sh"

View file

@ -0,0 +1,3 @@
protoc_version: 3.5.1
protoc_includes:
- ../../../vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Copyright 2018 clair authors
#
# 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
#
# http://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.
set -o errexit
set -o nounset
set -o pipefail
protoc -I/usr/include -I. \
-I"${GOPATH}/src" \
-I"${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis" \
--go_out=plugins=grpc:. \
--grpc-gateway_out=logtostderr=true:. \
--swagger_out=logtostderr=true:. \
./api/v3/clairpb/clair.proto
go generate .

96
vendor/github.com/coreos/clair/database/ancestry.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
// Copyright 2019 clair authors
//
// 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
//
// http://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.
package database
// Ancestry is a manifest that keeps all layers in an image in order.
type Ancestry struct {
// Name is a globally unique value for a set of layers. This is often the
// sha256 digest of an OCI/Docker manifest.
Name string `json:"name"`
// By contains the processors that are used when computing the
// content of this ancestry.
By []Detector `json:"by"`
// Layers should be ordered and i_th layer is the parent of i+1_th layer in
// the slice.
Layers []AncestryLayer `json:"layers"`
}
// Valid checks if the ancestry is compliant to spec.
func (a *Ancestry) Valid() bool {
if a == nil {
return false
}
if a.Name == "" {
return false
}
for _, d := range a.By {
if !d.Valid() {
return false
}
}
for _, l := range a.Layers {
if !l.Valid() {
return false
}
}
return true
}
// AncestryLayer is a layer with all detected namespaced features.
type AncestryLayer struct {
// Hash is the sha-256 tarsum on the layer's blob content.
Hash string `json:"hash"`
// Features are the features introduced by this layer when it was
// processed.
Features []AncestryFeature `json:"features"`
}
// Valid checks if the Ancestry Layer is compliant to the spec.
func (l *AncestryLayer) Valid() bool {
if l == nil {
return false
}
if l.Hash == "" {
return false
}
return true
}
// GetFeatures returns the Ancestry's features.
func (l *AncestryLayer) GetFeatures() []NamespacedFeature {
nsf := make([]NamespacedFeature, 0, len(l.Features))
for _, f := range l.Features {
nsf = append(nsf, f.NamespacedFeature)
}
return nsf
}
// AncestryFeature is a namespaced feature with the detectors used to
// find this feature.
type AncestryFeature struct {
NamespacedFeature `json:"namespacedFeature"`
// FeatureBy is the detector that detected the feature.
FeatureBy Detector `json:"featureBy"`
// NamespaceBy is the detector that detected the namespace.
NamespaceBy Detector `json:"namespaceBy"`
}

View file

@ -1,4 +1,4 @@
// Copyright 2017 clair authors
// Copyright 2019 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package database
import (
"errors"
"fmt"
"time"
@ -27,12 +26,20 @@ import (
var (
// ErrBackendException is an error that occurs when the database backend
// does not work properly (ie. unreachable).
ErrBackendException = errors.New("database: an error occurred when querying the backend")
ErrBackendException = NewStorageError("an error occurred when querying the backend")
// ErrInconsistent is an error that occurs when a database consistency check
// fails (i.e. when an entity which is supposed to be unique is detected
// twice)
ErrInconsistent = errors.New("database: inconsistent database")
ErrInconsistent = NewStorageError("inconsistent database")
// ErrInvalidParameters is an error that occurs when the parameters are not valid.
ErrInvalidParameters = NewStorageError("parameters are not valid")
// ErrMissingEntities is an error that occurs when an associated immutable
// entity doesn't exist in the database. This error can indicate a wrong
// implementation or corrupted database.
ErrMissingEntities = NewStorageError("associated immutable entities are missing in the database")
)
// RegistrableComponentConfig is a configuration block that can be used to
@ -99,6 +106,9 @@ type Session interface {
// namespaced features. If the ancestry is not found, return false.
FindAncestry(name string) (ancestry Ancestry, found bool, err error)
// PersistDetector inserts a slice of detectors if not in the database.
PersistDetectors(detectors []Detector) error
// PersistFeatures inserts a set of features if not in the database.
PersistFeatures(features []Feature) error
@ -120,12 +130,10 @@ type Session interface {
// PersistNamespaces inserts a set of namespaces if not in the database.
PersistNamespaces([]Namespace) error
// PersistLayer persists a layer's content in the database. The given
// namespaces and features can be partial content of this layer.
// PersistLayer appends a layer's content in the database.
//
// The layer, namespaces and features are expected to be already existing
// in the database.
PersistLayer(hash string, namespaces []Namespace, features []Feature, processedBy Processors) error
// If any feature, namespace, or detector is not in the database, it returns not found error.
PersistLayer(hash string, features []LayerFeature, namespaces []LayerNamespace, detectedBy []Detector) error
// FindLayer returns a layer with all detected features and
// namespaces.
@ -157,8 +165,8 @@ type Session interface {
// affected ancestries affected by old or new vulnerability.
//
// Because the number of affected ancestries maybe large, they are paginated
// and their pages are specified by the paination token, which, if empty, are
// always considered first page.
// and their pages are specified by the pagination token, which should be
// considered first page when it's empty.
FindVulnerabilityNotification(name string, limit int, oldVulnerabilityPage pagination.Token, newVulnerabilityPage pagination.Token) (noti VulnerabilityNotificationWithVulnerable, found bool, err error)
// MarkNotificationAsRead marks a Notification as notified now, assuming
@ -174,23 +182,22 @@ type Session interface {
// FindKeyValue retrieves a value from the given key.
FindKeyValue(key string) (value string, found bool, err error)
// Lock creates or renew a Lock in the database with the given name, owner
// and duration.
// AcquireLock acquires a brand new lock in the database with a given name
// for the given duration.
//
// After the specified duration, the Lock expires by itself if it hasn't been
// unlocked, and thus, let other users create a Lock with the same name.
// However, the owner can renew its Lock by setting renew to true.
// Lock should not block, it should instead returns whether the Lock has been
// successfully acquired/renewed. If it's the case, the expiration time of
// that Lock is returned as well.
Lock(name string, owner string, duration time.Duration, renew bool) (success bool, expiration time.Time, err error)
// A lock can only have one owner.
// This method should NOT block until a lock is acquired.
AcquireLock(name, owner string, duration time.Duration) (acquired bool, expiration time.Time, err error)
// Unlock releases an existing Lock.
Unlock(name, owner string) error
// ExtendLock extends an existing lock such that the lock will expire at the
// current time plus the provided duration.
//
// This method should return immediately with an error if the lock does not
// exist.
ExtendLock(name, owner string, duration time.Duration) (extended bool, expiration time.Time, err error)
// FindLock returns the owner of a Lock specified by the name, and its
// expiration time if it exists.
FindLock(name string) (owner string, expiration time.Time, found bool, err error)
// ReleaseLock releases an existing lock.
ReleaseLock(name, owner string) error
}
// Datastore represents a persistent data store

538
vendor/github.com/coreos/clair/database/dbutil.go generated vendored Normal file
View file

@ -0,0 +1,538 @@
// Copyright 2018 clair authors
//
// 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
//
// http://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.
package database
import (
"encoding/json"
"time"
log "github.com/sirupsen/logrus"
"github.com/coreos/clair/pkg/commonerr"
"github.com/coreos/clair/pkg/pagination"
"github.com/deckarep/golang-set"
)
// DeduplicateNamespaces deduplicates a list of namespaces.
func DeduplicateNamespaces(namespaces ...Namespace) []Namespace {
nsSet := mapset.NewSet()
for _, ns := range namespaces {
nsSet.Add(ns)
}
uniqueNamespaces := make([]Namespace, 0, nsSet.Cardinality())
for ns := range nsSet.Iter() {
uniqueNamespaces = append(uniqueNamespaces, ns.(Namespace))
}
return uniqueNamespaces
}
// DeduplicateFeatures deduplicates a list of list of features.
func DeduplicateFeatures(features ...Feature) []Feature {
fSet := mapset.NewSet()
for _, f := range features {
fSet.Add(f)
}
return ConvertFeatureSetToFeatures(fSet)
}
// ConvertFeatureSetToFeatures converts a feature set to an array of features
func ConvertFeatureSetToFeatures(features mapset.Set) []Feature {
uniqueFeatures := make([]Feature, 0, features.Cardinality())
for f := range features.Iter() {
uniqueFeatures = append(uniqueFeatures, f.(Feature))
}
return uniqueFeatures
}
func ConvertFeatureSetToLayerFeatures(features mapset.Set) []LayerFeature {
uniqueLayerFeatures := make([]LayerFeature, 0, features.Cardinality())
for f := range features.Iter() {
feature := f.(Feature)
layerFeature := LayerFeature{
Feature: feature,
}
uniqueLayerFeatures = append(uniqueLayerFeatures, layerFeature)
}
return uniqueLayerFeatures
}
// FindKeyValueAndRollback wraps session FindKeyValue function with begin and
// roll back.
func FindKeyValueAndRollback(datastore Datastore, key string) (value string, ok bool, err error) {
var tx Session
tx, err = datastore.Begin()
if err != nil {
return
}
defer tx.Rollback()
value, ok, err = tx.FindKeyValue(key)
return
}
// PersistPartialLayerAndCommit wraps session PersistLayer function with begin and
// commit.
func PersistPartialLayerAndCommit(datastore Datastore, layer *Layer) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.PersistLayer(layer.Hash, layer.Features, layer.Namespaces, layer.By); err != nil {
return err
}
return tx.Commit()
}
// PersistFeaturesAndCommit wraps session PersistFeaturesAndCommit function with begin and commit.
func PersistFeaturesAndCommit(datastore Datastore, features []Feature) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.PersistFeatures(features); err != nil {
serialized, _ := json.Marshal(features)
log.WithError(err).WithField("feature", string(serialized)).Error("failed to store features")
return err
}
return tx.Commit()
}
// PersistNamespacesAndCommit wraps session PersistNamespaces function with
// begin and commit.
func PersistNamespacesAndCommit(datastore Datastore, namespaces []Namespace) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.PersistNamespaces(namespaces); err != nil {
return err
}
return tx.Commit()
}
// FindAncestryAndRollback wraps session FindAncestry function with begin and
// rollback.
func FindAncestryAndRollback(datastore Datastore, name string) (Ancestry, bool, error) {
tx, err := datastore.Begin()
if err != nil {
return Ancestry{}, false, err
}
defer tx.Rollback()
return tx.FindAncestry(name)
}
// FindLayerAndRollback wraps session FindLayer function with begin and rollback.
func FindLayerAndRollback(datastore Datastore, hash string) (layer *Layer, ok bool, err error) {
var tx Session
if tx, err = datastore.Begin(); err != nil {
return
}
defer tx.Rollback()
// TODO(sidac): In order to make the session interface more idiomatic, we'll
// return the pointer value in the future.
var dereferencedLayer Layer
dereferencedLayer, ok, err = tx.FindLayer(hash)
layer = &dereferencedLayer
return
}
// DeduplicateNamespacedFeatures returns a copy of all unique features in the
// input.
func DeduplicateNamespacedFeatures(features []NamespacedFeature) []NamespacedFeature {
nsSet := mapset.NewSet()
for _, ns := range features {
nsSet.Add(ns)
}
uniqueFeatures := make([]NamespacedFeature, 0, nsSet.Cardinality())
for ns := range nsSet.Iter() {
uniqueFeatures = append(uniqueFeatures, ns.(NamespacedFeature))
}
return uniqueFeatures
}
// GetAncestryFeatures returns a list of unique namespaced features in the
// ancestry.
func GetAncestryFeatures(ancestry Ancestry) []NamespacedFeature {
features := []NamespacedFeature{}
for _, layer := range ancestry.Layers {
features = append(features, layer.GetFeatures()...)
}
return DeduplicateNamespacedFeatures(features)
}
// UpsertAncestryAndCommit wraps session UpsertAncestry function with begin and commit.
func UpsertAncestryAndCommit(datastore Datastore, ancestry *Ancestry) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
if err = tx.UpsertAncestry(*ancestry); err != nil {
log.WithError(err).Error("failed to upsert the ancestry")
serialized, _ := json.Marshal(ancestry)
log.Debug(string(serialized))
tx.Rollback()
return err
}
if err = tx.Commit(); err != nil {
return err
}
return nil
}
// PersistNamespacedFeaturesAndCommit wraps session PersistNamespacedFeatures function
// with begin and commit.
func PersistNamespacedFeaturesAndCommit(datastore Datastore, features []NamespacedFeature) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
if err := tx.PersistNamespacedFeatures(features); err != nil {
tx.Rollback()
return err
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
// CacheRelatedVulnerabilityAndCommit wraps session CacheAffectedNamespacedFeatures
// function with begin and commit.
func CacheRelatedVulnerabilityAndCommit(datastore Datastore, features []NamespacedFeature) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
if err := tx.CacheAffectedNamespacedFeatures(features); err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
// IntersectDetectors returns the detectors in both d1 and d2.
func IntersectDetectors(d1 []Detector, d2 []Detector) []Detector {
d1Set := mapset.NewSet()
for _, d := range d1 {
d1Set.Add(d)
}
d2Set := mapset.NewSet()
for _, d := range d2 {
d2Set.Add(d)
}
inter := d1Set.Intersect(d2Set)
detectors := make([]Detector, 0, inter.Cardinality())
for d := range inter.Iter() {
detectors = append(detectors, d.(Detector))
}
return detectors
}
// DiffDetectors returns the detectors belongs to d1 but not d2
func DiffDetectors(d1 []Detector, d2 []Detector) []Detector {
d1Set := mapset.NewSet()
for _, d := range d1 {
d1Set.Add(d)
}
d2Set := mapset.NewSet()
for _, d := range d2 {
d2Set.Add(d)
}
diff := d1Set.Difference(d2Set)
detectors := make([]Detector, 0, diff.Cardinality())
for d := range diff.Iter() {
detectors = append(detectors, d.(Detector))
}
return detectors
}
// MergeLayers merges all content in new layer to l, where the content is
// updated.
func MergeLayers(l *Layer, new *Layer) *Layer {
featureSet := mapset.NewSet()
namespaceSet := mapset.NewSet()
bySet := mapset.NewSet()
for _, f := range l.Features {
featureSet.Add(f)
}
for _, ns := range l.Namespaces {
namespaceSet.Add(ns)
}
for _, d := range l.By {
bySet.Add(d)
}
for _, feature := range new.Features {
if !featureSet.Contains(feature) {
l.Features = append(l.Features, feature)
featureSet.Add(feature)
}
}
for _, namespace := range new.Namespaces {
if !namespaceSet.Contains(namespace) {
l.Namespaces = append(l.Namespaces, namespace)
namespaceSet.Add(namespace)
}
}
for _, detector := range new.By {
if !bySet.Contains(detector) {
l.By = append(l.By, detector)
bySet.Add(detector)
}
}
return l
}
// AcquireLock acquires a named global lock for a duration.
func AcquireLock(datastore Datastore, name, owner string, duration time.Duration) (acquired bool, expiration time.Time) {
tx, err := datastore.Begin()
if err != nil {
return false, time.Time{}
}
defer tx.Rollback()
locked, t, err := tx.AcquireLock(name, owner, duration)
if err != nil {
return false, time.Time{}
}
if locked {
if err := tx.Commit(); err != nil {
return false, time.Time{}
}
}
return locked, t
}
// ExtendLock extends the duration of an existing global lock for the given
// duration.
func ExtendLock(ds Datastore, name, whoami string, desiredLockDuration time.Duration) (bool, time.Time) {
tx, err := ds.Begin()
if err != nil {
return false, time.Time{}
}
defer tx.Rollback()
locked, expiration, err := tx.ExtendLock(name, whoami, desiredLockDuration)
if err != nil {
return false, time.Time{}
}
if locked {
if err := tx.Commit(); err == nil {
return locked, expiration
}
}
return false, time.Time{}
}
// ReleaseLock releases a named global lock.
func ReleaseLock(datastore Datastore, name, owner string) {
tx, err := datastore.Begin()
if err != nil {
return
}
defer tx.Rollback()
if err := tx.ReleaseLock(name, owner); err != nil {
return
}
if err := tx.Commit(); err != nil {
return
}
}
// PersistDetectorsAndCommit stores the detectors in the data store.
func PersistDetectorsAndCommit(store Datastore, detectors []Detector) error {
tx, err := store.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.PersistDetectors(detectors); err != nil {
return err
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
// MarkNotificationAsReadAndCommit marks a notification as read.
func MarkNotificationAsReadAndCommit(store Datastore, name string) (bool, error) {
tx, err := store.Begin()
if err != nil {
return false, err
}
defer tx.Rollback()
err = tx.DeleteNotification(name)
if err == commonerr.ErrNotFound {
return false, nil
} else if err != nil {
return false, err
}
if err := tx.Commit(); err != nil {
return false, err
}
return true, nil
}
// FindAffectedNamespacedFeaturesAndRollback finds the vulnerabilities on each
// feature.
func FindAffectedNamespacedFeaturesAndRollback(store Datastore, features []NamespacedFeature) ([]NullableAffectedNamespacedFeature, error) {
tx, err := store.Begin()
if err != nil {
return nil, err
}
defer tx.Rollback()
nullableFeatures, err := tx.FindAffectedNamespacedFeatures(features)
if err != nil {
return nil, err
}
return nullableFeatures, nil
}
// FindVulnerabilityNotificationAndRollback finds the vulnerability notification
// and rollback.
func FindVulnerabilityNotificationAndRollback(store Datastore, name string, limit int, oldVulnerabilityPage pagination.Token, newVulnerabilityPage pagination.Token) (VulnerabilityNotificationWithVulnerable, bool, error) {
tx, err := store.Begin()
if err != nil {
return VulnerabilityNotificationWithVulnerable{}, false, err
}
defer tx.Rollback()
return tx.FindVulnerabilityNotification(name, limit, oldVulnerabilityPage, newVulnerabilityPage)
}
// FindNewNotification finds notifications either never notified or notified
// before the given time.
func FindNewNotification(store Datastore, notifiedBefore time.Time) (NotificationHook, bool, error) {
tx, err := store.Begin()
if err != nil {
return NotificationHook{}, false, err
}
defer tx.Rollback()
return tx.FindNewNotification(notifiedBefore)
}
// UpdateKeyValueAndCommit stores the key value to storage.
func UpdateKeyValueAndCommit(store Datastore, key, value string) error {
tx, err := store.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if err = tx.UpdateKeyValue(key, value); err != nil {
return err
}
return tx.Commit()
}
// InsertVulnerabilityNotificationsAndCommit inserts the notifications into db
// and commit.
func InsertVulnerabilityNotificationsAndCommit(store Datastore, notifications []VulnerabilityNotification) error {
tx, err := store.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if err := tx.InsertVulnerabilityNotifications(notifications); err != nil {
return err
}
return tx.Commit()
}
// FindVulnerabilitiesAndRollback finds the vulnerabilities based on given ids.
func FindVulnerabilitiesAndRollback(store Datastore, ids []VulnerabilityID) ([]NullableVulnerability, error) {
tx, err := store.Begin()
if err != nil {
return nil, err
}
defer tx.Rollback()
return tx.FindVulnerabilities(ids)
}
func UpdateVulnerabilitiesAndCommit(store Datastore, toRemove []VulnerabilityID, toAdd []VulnerabilityWithAffected) error {
tx, err := store.Begin()
if err != nil {
return err
}
if err := tx.DeleteVulnerabilities(toRemove); err != nil {
return err
}
if err := tx.InsertVulnerabilities(toAdd); err != nil {
return err
}
return tx.Commit()
}

144
vendor/github.com/coreos/clair/database/detector.go generated vendored Normal file
View file

@ -0,0 +1,144 @@
// Copyright 2018 clair authors
//
// 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
//
// http://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.
package database
import (
"database/sql/driver"
"errors"
"fmt"
"strings"
)
const (
// NamespaceDetectorType is a type of detector that extracts the namespaces.
NamespaceDetectorType DetectorType = "namespace"
// FeatureDetectorType is a type of detector that extracts the features.
FeatureDetectorType DetectorType = "feature"
)
// DetectorTypes contains all detector types.
var (
DetectorTypes = []DetectorType{
NamespaceDetectorType,
FeatureDetectorType,
}
// ErrFailedToParseDetectorType is the error returned when a detector type could
// not be parsed from a string.
ErrFailedToParseDetectorType = errors.New("failed to parse DetectorType from input")
// ErrInvalidDetector is the error returned when a detector from database has
// invalid name or version or type.
ErrInvalidDetector = errors.New("the detector has invalid metadata")
)
// DetectorType is the type of a detector.
type DetectorType string
// Value implements the database/sql/driver.Valuer interface.
func (s DetectorType) Value() (driver.Value, error) {
return string(s), nil
}
// Scan implements the database/sql.Scanner interface.
func (s *DetectorType) Scan(value interface{}) error {
val, ok := value.([]byte)
if !ok {
return errors.New("could not scan a Severity from a non-string input")
}
var err error
*s, err = NewDetectorType(string(val))
if err != nil {
return err
}
return nil
}
// NewDetectorType attempts to parse a string into a standard DetectorType
// value.
func NewDetectorType(s string) (DetectorType, error) {
for _, ss := range DetectorTypes {
if strings.EqualFold(s, string(ss)) {
return ss, nil
}
}
return "", ErrFailedToParseDetectorType
}
// Valid checks if a detector type is defined.
func (s DetectorType) Valid() bool {
for _, t := range DetectorTypes {
if s == t {
return true
}
}
return false
}
// Detector is an versioned Clair extension.
type Detector struct {
// Name of an extension should be non-empty and uniquely identifies the
// extension.
Name string `json:"name"`
// Version of an extension should be non-empty.
Version string `json:"version"`
// DType is the type of the extension and should be one of the types in
// DetectorTypes.
DType DetectorType `json:"dtype"`
}
// Valid checks if all fields in the detector satisfies the spec.
func (d Detector) Valid() bool {
if d.Name == "" || d.Version == "" || !d.DType.Valid() {
return false
}
return true
}
// String returns a unique string representation of the detector.
func (d Detector) String() string {
return fmt.Sprintf("%s:%s", d.Name, d.Version)
}
// NewNamespaceDetector returns a new namespace detector.
func NewNamespaceDetector(name, version string) Detector {
return Detector{
Name: name,
Version: version,
DType: NamespaceDetectorType,
}
}
// NewFeatureDetector returns a new feature detector.
func NewFeatureDetector(name, version string) Detector {
return Detector{
Name: name,
Version: version,
DType: FeatureDetectorType,
}
}
// SerializeDetectors returns the string representation of given detectors.
func SerializeDetectors(detectors []Detector) []string {
strDetectors := []string{}
for _, d := range detectors {
strDetectors = append(strDetectors, d.String())
}
return strDetectors
}

35
vendor/github.com/coreos/clair/database/error.go generated vendored Normal file
View file

@ -0,0 +1,35 @@
// Copyright 2019 clair authors
//
// 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
//
// http://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.
package database
// StorageError is database error
type StorageError struct {
reason string
original error
}
func (e *StorageError) Error() string {
return e.reason
}
// NewStorageErrorWithInternalError creates a new database error
func NewStorageErrorWithInternalError(reason string, originalError error) *StorageError {
return &StorageError{reason, originalError}
}
// NewStorageError creates a new database error
func NewStorageError(reason string) *StorageError {
return &StorageError{reason, nil}
}

96
vendor/github.com/coreos/clair/database/feature.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
// Copyright 2019 clair authors
//
// 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
//
// http://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.
package database
// Feature represents a package detected in a layer but the namespace is not
// determined.
//
// e.g. Name: Libssl1.0, Version: 1.0, VersionFormat: dpkg, Type: binary
// dpkg is the version format of the installer package manager, which in this
// case could be dpkg or apk.
type Feature struct {
Name string `json:"name"`
Version string `json:"version"`
VersionFormat string `json:"versionFormat"`
Type FeatureType `json:"type"`
}
// NamespacedFeature is a feature with determined namespace and can be affected
// by vulnerabilities.
//
// e.g. OpenSSL 1.0 dpkg Debian:7.
type NamespacedFeature struct {
Feature `json:"feature"`
Namespace Namespace `json:"namespace"`
}
// AffectedNamespacedFeature is a namespaced feature affected by the
// vulnerabilities with fixed-in versions for this feature.
type AffectedNamespacedFeature struct {
NamespacedFeature
AffectedBy []VulnerabilityWithFixedIn
}
// VulnerabilityWithFixedIn is used for AffectedNamespacedFeature to retrieve
// the affecting vulnerabilities and the fixed-in versions for the feature.
type VulnerabilityWithFixedIn struct {
Vulnerability
FixedInVersion string
}
// AffectedFeature is used to determine whether a namespaced feature is affected
// by a Vulnerability. Namespace and Feature Name is unique. Affected Feature is
// bound to vulnerability.
type AffectedFeature struct {
// FeatureType determines which type of package it affects.
FeatureType FeatureType
Namespace Namespace
FeatureName string
// FixedInVersion is known next feature version that's not affected by the
// vulnerability. Empty FixedInVersion means the unaffected version is
// unknown.
FixedInVersion string
// AffectedVersion contains the version range to determine whether or not a
// feature is affected.
AffectedVersion string
}
// NullableAffectedNamespacedFeature is an affectednamespacedfeature with
// whether it's found in datastore.
type NullableAffectedNamespacedFeature struct {
AffectedNamespacedFeature
Valid bool
}
func NewFeature(name string, version string, versionFormat string, featureType FeatureType) *Feature {
return &Feature{name, version, versionFormat, featureType}
}
func NewBinaryPackage(name string, version string, versionFormat string) *Feature {
return &Feature{name, version, versionFormat, BinaryPackage}
}
func NewSourcePackage(name string, version string, versionFormat string) *Feature {
return &Feature{name, version, versionFormat, SourcePackage}
}
func NewNamespacedFeature(namespace *Namespace, feature *Feature) *NamespacedFeature {
// TODO: namespaced feature should use pointer values
return &NamespacedFeature{*feature, *namespace}
}

View file

@ -0,0 +1,52 @@
// Copyright 2019 clair authors
//
// 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
//
// http://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.
package database
import (
"database/sql/driver"
"fmt"
)
// FeatureType indicates the type of feature that a vulnerability
// affects.
type FeatureType string
const (
SourcePackage FeatureType = "source"
BinaryPackage FeatureType = "binary"
)
var featureTypes = []FeatureType{
SourcePackage,
BinaryPackage,
}
// Scan implements the database/sql.Scanner interface.
func (t *FeatureType) Scan(value interface{}) error {
val := value.(string)
for _, ft := range featureTypes {
if string(ft) == val {
*t = ft
return nil
}
}
panic(fmt.Sprintf("invalid feature type received from database: '%s'", val))
}
// Value implements the database/sql/driver.Valuer interface.
func (t *FeatureType) Value() (driver.Value, error) {
return string(*t), nil
}

Some files were not shown because too many files have changed in this diff Show more