update vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
19a32db84d
commit
94d1cfbfbf
10501 changed files with 2307943 additions and 29279 deletions
10
vendor/github.com/genuinetools/reg/.gitignore
generated
vendored
10
vendor/github.com/genuinetools/reg/.gitignore
generated
vendored
|
@ -43,11 +43,15 @@ Icon
|
|||
.Trashes
|
||||
|
||||
reg
|
||||
server/server
|
||||
testreg
|
||||
.certs
|
||||
cross/
|
||||
|
||||
# Go coverage results
|
||||
coverage.txt
|
||||
profile.out
|
||||
|
||||
!go.mod
|
||||
|
||||
testreg
|
||||
.certs
|
||||
server/static/index.html
|
||||
server/static/repo/
|
||||
|
|
10
vendor/github.com/genuinetools/reg/.goosarch
generated
vendored
Normal file
10
vendor/github.com/genuinetools/reg/.goosarch
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
darwin/amd64
|
||||
darwin/386
|
||||
freebsd/amd64
|
||||
freebsd/386
|
||||
linux/arm
|
||||
linux/arm64
|
||||
linux/amd64
|
||||
linux/386
|
||||
windows/amd64
|
||||
windows/386
|
118
vendor/github.com/genuinetools/reg/.travis.yml
generated
vendored
118
vendor/github.com/genuinetools/reg/.travis.yml
generated
vendored
|
@ -1,62 +1,56 @@
|
|||
---
|
||||
language: go
|
||||
sudo: true
|
||||
notifications:
|
||||
email: true
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
global:
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
fast_finish: true
|
||||
install:
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get honnef.co/go/tools/cmd/staticcheck
|
||||
script:
|
||||
- go build -v
|
||||
- go vet $(go list ./... | grep -v vendor)
|
||||
- staticcheck $(go list ./... | grep -v vendor)
|
||||
- test -z "$(golint ./... | grep -v vendor | tee /dev/stderr)"
|
||||
- test -z "$(gofmt -s -l . | grep -v vendor | tee /dev/stderr)"
|
||||
- DOCKER_API_VERSION=1.35 make dind dtest
|
||||
- make release
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "xz4uJ+vrF5+u3zucCPdnoXR/a0i8/oUzzDABuKiaB9AFFjrM8obLYo2AgMlP5zj/YHpGgTP51m/sx/qwJKfNvCoR2alBb2taYzJnhCfXzOuviZ0RZbM2LqA72lutdAzZ5eyMPCXcqvOjf6INnCmqQeJjDWo8UzGKSlWP2cqU/Qovs1vzurImME86DjqQ4EDaYlZS3tVc5BtEqmhylT2q0aO7gNJcMunDJpIEwb3vo8bbOoS6heQO2DVFf553lnZTSheEOERiF8r/O3vdMBCIqq7Xr2WIzJ4WGoNqzCk4sVcOZYP1yWa4Je/J09TaM8Uam+SZCG8p2lG+lr9toNv9jDHAA3Z986hAj+1NhRXTbwtRYM/KfL38UegvGfFCRvOAc+3AQhQaw1p2hX599in4zl/IcSVjF6IytJGj+JrCHU1p5Bd9qphFQKlXAXQKZwH+TKt3QTnrUQIUOn0QwcfgbvDUaA2XMsR9f0BWNshILvz79JJZmwXY7C7ufVSKdL+T+9dNn/5N7dMn6fWb7ZruwK3N6gLyVSulMinSYyNIHGiEH3mdoBr020KYD1w1+cfK4Ov6B8vf9k7atzHDPRklm2X0hvda2T0UXOv5+hr+OlvdhpqZKDB2HkVOUQUUfk7cL88u+FpU6pktlhJVLSCl292jWS05I1AYOiHChEFONeE="
|
||||
file:
|
||||
- cross/reg-linux-amd64.md5
|
||||
- cross/reg-windows-386.sha256
|
||||
- cross/reg-linux-arm
|
||||
- cross/reg-darwin-amd64.md5
|
||||
- cross/reg-darwin-amd64
|
||||
- cross/reg-linux-arm64.sha256
|
||||
- cross/reg-linux-arm.sha256
|
||||
- cross/reg-linux-386.sha256
|
||||
- cross/reg-darwin-386.md5
|
||||
- cross/reg-windows-386.md5
|
||||
- cross/reg-linux-arm64.md5
|
||||
- cross/reg-linux-arm64
|
||||
- cross/reg-linux-amd64.sha256
|
||||
- cross/reg-linux-386.md5
|
||||
- cross/reg-windows-amd64
|
||||
- cross/reg-windows-amd64.md5
|
||||
- cross/reg-windows-amd64.sha256
|
||||
- cross/reg-linux-arm.md5
|
||||
- cross/reg-darwin-386.sha256
|
||||
- cross/reg-darwin-amd64.sha256
|
||||
- cross/reg-windows-386
|
||||
- cross/reg-darwin-386
|
||||
- cross/reg-linux-386
|
||||
- cross/reg-linux-amd64
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
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="
|
||||
|
|
1
vendor/github.com/genuinetools/reg/.traviskey
generated
vendored
Normal file
1
vendor/github.com/genuinetools/reg/.traviskey
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
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=
|
4
vendor/github.com/genuinetools/reg/Dockerfile
generated
vendored
4
vendor/github.com/genuinetools/reg/Dockerfile
generated
vendored
|
@ -23,10 +23,12 @@ RUN set -x \
|
|||
&& rm -rf /go \
|
||||
&& echo "Build complete."
|
||||
|
||||
FROM scratch
|
||||
FROM alpine:latest
|
||||
|
||||
COPY --from=builder /usr/bin/reg /usr/bin/reg
|
||||
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
ENTRYPOINT [ "reg" ]
|
||||
CMD [ "--help" ]
|
||||
|
|
11
vendor/github.com/genuinetools/reg/Dockerfile.clair
generated
vendored
Normal file
11
vendor/github.com/genuinetools/reg/Dockerfile.clair
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
FROM quay.io/coreos/clair
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates
|
||||
|
||||
COPY testutils/snakeoil/cert.pem /usr/local/share/ca-certificates/clair.pem
|
||||
|
||||
# normally we'd use update-ca-certificates, but something about running it in
|
||||
# Alpine is off, and the certs don't get added. Fortunately, we only need to
|
||||
# add ca-certificates to the global store and it's all plain text.
|
||||
RUN cat /usr/local/share/ca-certificates/* >> /etc/ssl/certs/ca-certificates.crt
|
310
vendor/github.com/genuinetools/reg/Gopkg.lock
generated
vendored
310
vendor/github.com/genuinetools/reg/Gopkg.lock
generated
vendored
|
@ -1,310 +0,0 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Azure/go-ansiterm"
|
||||
packages = [
|
||||
".",
|
||||
"winterm"
|
||||
]
|
||||
revision = "d6e3b3328b783f23731bc4d058875b0371ff8109"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Microsoft/go-winio"
|
||||
packages = ["."]
|
||||
revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
|
||||
version = "v0.4.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Nvveen/Gotty"
|
||||
packages = ["."]
|
||||
revision = "cd527374f1e5bff4938207604a14f2e38a9cf512"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/beorn7/perks"
|
||||
packages = ["quantile"]
|
||||
revision = "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/containerd/continuity"
|
||||
packages = ["pathdriver"]
|
||||
revision = "d8fb8589b0e8e85b8c8bbaa8840226d0dfeb7371"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/cli"
|
||||
packages = [
|
||||
"cli/config/configfile",
|
||||
"cli/config/credentials",
|
||||
"opts"
|
||||
]
|
||||
revision = "2731c71c993a4d1e43101eea56e7c343315024f1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
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"
|
||||
]
|
||||
revision = "6fca8d6e6713acbdf3f9ca40cf6370fc5ee5ee53"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
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"
|
||||
]
|
||||
revision = "a575b0b1384b2ba89b79cbd7e770fbeb616758b3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/docker-ce"
|
||||
packages = ["components/cli/cli/config"]
|
||||
revision = "0b63fed1587e3fedd1ab72d9f6e80f71e8c52f8b"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/docker/docker-credential-helpers"
|
||||
packages = [
|
||||
"client",
|
||||
"credentials",
|
||||
"pass"
|
||||
]
|
||||
revision = "d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1"
|
||||
version = "v0.6.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/go-connections"
|
||||
packages = [
|
||||
"nat",
|
||||
"sockets",
|
||||
"tlsconfig"
|
||||
]
|
||||
revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/go-metrics"
|
||||
packages = ["."]
|
||||
revision = "399ea8c73916000c64c2c76e8da00ca82f8387ab"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/go-units"
|
||||
packages = ["."]
|
||||
revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/libtrust"
|
||||
packages = ["."]
|
||||
revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = ["proto"]
|
||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/go-cmp"
|
||||
packages = [
|
||||
"cmp",
|
||||
"cmp/internal/diff",
|
||||
"cmp/internal/function",
|
||||
"cmp/internal/value"
|
||||
]
|
||||
revision = "3af367b6b30c263d47e8895973edcca9a49cf029"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/mux"
|
||||
packages = ["."]
|
||||
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
|
||||
version = "v1.6.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||
packages = ["pbutil"]
|
||||
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/go-wordwrap"
|
||||
packages = ["."]
|
||||
revision = "ad45545899c7b13c020ea92b2072220eefad42b8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opencontainers/go-digest"
|
||||
packages = ["."]
|
||||
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
||||
version = "v1.0.0-rc1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opencontainers/image-spec"
|
||||
packages = [
|
||||
"specs-go",
|
||||
"specs-go/v1"
|
||||
]
|
||||
revision = "d60099175f88c47cd379c4738d158884749ed235"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/opencontainers/runc"
|
||||
packages = ["libcontainer/user"]
|
||||
revision = "baf6536d6259209c3edfa2b22237af82942d3dfa"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/peterhellberg/link"
|
||||
packages = ["."]
|
||||
revision = "d1cebc7ea14a5fc0de7cb4a45acae773161642c6"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
"prometheus/promhttp"
|
||||
]
|
||||
revision = "c3324c1198cf3374996e9d3098edd46a6b55afc9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/client_model"
|
||||
packages = ["go"]
|
||||
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
"internal/bitbucket.org/ww/goautoneg",
|
||||
"model"
|
||||
]
|
||||
revision = "6fb6fce6f8b75884b92e1889c150403fc0872c5e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
"internal/util",
|
||||
"nfs",
|
||||
"xfs"
|
||||
]
|
||||
revision = "54d17b57dd7d4a3aa092476596b3f8a933bde349"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
|
||||
version = "v1.0.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/urfave/cli"
|
||||
packages = ["."]
|
||||
revision = "8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
revision = "c7dcf104e3a7a1417abc0230cb0d5240d764159d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"context/ctxhttp",
|
||||
"proxy"
|
||||
]
|
||||
revision = "d0aafc73d5cdc42264b0af071c261abac580695e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows"
|
||||
]
|
||||
revision = "7dca6fe1f43775aa6d1334576870ff63f978f539"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "5cdf10e821ede3dcf4207f6e2b0e5abe31ac803e81704a82c798b0199c057882"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
65
vendor/github.com/genuinetools/reg/Gopkg.toml
generated
vendored
65
vendor/github.com/genuinetools/reg/Gopkg.toml
generated
vendored
|
@ -1,65 +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"
|
||||
|
||||
|
||||
[[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/urfave/cli"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/opencontainers/go-digest"
|
||||
branch = "master"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
branch = "master"
|
184
vendor/github.com/genuinetools/reg/Makefile
generated
vendored
184
vendor/github.com/genuinetools/reg/Makefile
generated
vendored
|
@ -1,165 +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"
|
||||
|
||||
# List the GOOS and GOARCH to build
|
||||
GOOSARCHES = darwin/amd64 darwin/386 linux/arm linux/arm64 linux/amd64 linux/386 windows/amd64 windows/386
|
||||
|
||||
all: clean build fmt lint test staticcheck vet install ## Runs a clean, build, fmt, lint, test, staticcheck, vet and install
|
||||
|
||||
.PHONY: build
|
||||
build: $(NAME) ## Builds a dynamic executable or package
|
||||
|
||||
$(NAME): *.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) .
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Verifies all files have men `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 | 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: clean
|
||||
clean: ## Cleanup any build binaries or packages
|
||||
@echo "+ $@"
|
||||
$(RM) $(NAME)
|
||||
$(RM) -r $(BUILDDIR)
|
||||
$(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: ## 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 \
|
||||
|
@ -170,23 +31,32 @@ dind: ## Starts a docker-in-docker container for running the tests with
|
|||
--tlskey=/etc/docker/ssl/server.key \
|
||||
--tlscert=/etc/docker/ssl/server.cert
|
||||
|
||||
.PHONY: stop-dind
|
||||
stop-dind: ## Stops the docker-in-docker container.
|
||||
@docker rm -f $(NAME)-dind >/dev/null 2>&1 || true
|
||||
|
||||
.PHONY: image-dev
|
||||
image-dev:
|
||||
docker build --rm --force-rm -f Dockerfile.dev -t $(REGISTRY)/$(NAME):dev .
|
||||
|
||||
.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 \
|
||||
--net container:$(DIND_CONTAINER) \
|
||||
--disable-content-trust=true \
|
||||
--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) \
|
||||
make test cover
|
||||
$(REGISTRY)/$(NAME):dev \
|
||||
make test
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
.PHONY: snakeoil
|
||||
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
|
||||
|
|
205
vendor/github.com/genuinetools/reg/README.md
generated
vendored
205
vendor/github.com/genuinetools/reg/README.md
generated
vendored
|
@ -1,84 +1,83 @@
|
|||
# reg
|
||||
|
||||
[](https://travis-ci.org/genuinetools/reg)
|
||||
[](https://travis-ci.org/genuinetools/reg)
|
||||
[](https://godoc.org/github.com/genuinetools/reg)
|
||||
[](https://github.com/genuinetools/reg/releases)
|
||||
|
||||
Docker registry v2 command line client.
|
||||
Docker registry v2 command line client and repo listing generator with security checks.
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Auth](#auth)
|
||||
- [List Repositories and Tags](#list-repositories-and-tags)
|
||||
- [Get a Manifest](#get-a-manifest)
|
||||
- [Download a Layer](#download-a-layer)
|
||||
- [Delete an Image](#delete-an-image)
|
||||
- [Vulnerability Reports](#vulnerability-reports)
|
||||
- [Testing](#testing)
|
||||
* [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)
|
||||
|
||||
## Installation
|
||||
|
||||
#### Binaries
|
||||
|
||||
- **darwin** [386](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-darwin-386) / [amd64](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-darwin-amd64)
|
||||
- **linux** [386](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-linux-386) / [amd64](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-linux-amd64) / [arm](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-linux-arm) / [arm64](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-linux-arm64)
|
||||
- **windows** [386](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-windows-386) / [amd64](https://github.com/genuinetools/reg/releases/download/v0.13.0/reg-windows-amd64)
|
||||
For installation instructions from binaries please visit the [Releases Page](https://github.com/genuinetools/reg/releases).
|
||||
|
||||
#### Via Go
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ go get github.com/genuinetools/reg
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
$ reg
|
||||
NAME:
|
||||
reg - Docker registry v2 client.
|
||||
$ reg -h
|
||||
reg - Docker registry v2 client.
|
||||
|
||||
USAGE:
|
||||
reg [global options] command [command options] [arguments...]
|
||||
Usage: reg <command>
|
||||
|
||||
VERSION:
|
||||
version v0.13.0, build 3b7dafb
|
||||
Flags:
|
||||
|
||||
AUTHOR:
|
||||
The Genuinetools Authors <no-reply@butts.com>
|
||||
-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)
|
||||
-p, --password password for the registry (default: <none>)
|
||||
--skip-ping skip pinging the registry while establishing connection (default: false)
|
||||
--timeout timeout for HTTP requests (default: 1m0s)
|
||||
-u, --username username for the registry (default: <none>)
|
||||
|
||||
COMMANDS:
|
||||
delete, rm delete a specific reference of a repository
|
||||
layer, download download a layer for the specific reference of a repository
|
||||
list, ls list all repositories
|
||||
manifest get the json manifest for the specific reference of a repository
|
||||
tags get the tags for a repository
|
||||
vulns get a vulnerability report for the image from CoreOS Clair
|
||||
help, h Shows a list of commands or help for one command
|
||||
Commands:
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--debug, -d run in debug mode
|
||||
--insecure, -k do not verify tls certificates
|
||||
--force-non-ssl, -f force allow use of non-ssl
|
||||
--username value, -u value username for the registry
|
||||
--password value, -p value password for the registry
|
||||
--registry value, -r value URL to the private registry (ex. r.j3ss.co) (default: "https://registry-1.docker.io") [$REG_REGISTRY]
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
digest Get the digest for a repository.
|
||||
layer Download a layer for a repository.
|
||||
ls List all repositories.
|
||||
manifest Get the json manifest for a repository.
|
||||
rm Delete a specific reference of a repository.
|
||||
server Run a static UI server for a registry.
|
||||
tags Get the tags for a repository.
|
||||
vulns Get a vulnerability report for a repository from a CoreOS Clair server.
|
||||
version Show the version information.
|
||||
```
|
||||
|
||||
Note that the `--registry` can be set by an environment variable `REG_REGISTRY`, so you can set this in your shell login scripts.
|
||||
Specifying the registry on the command-line will override an environment variable setting.
|
||||
**NOTE:** Be aware that `reg ls` doesn't work with `hub.docker.com` as it has a different API than the [OSS Docker Registry](https://github.com/docker/distribution).
|
||||
|
||||
## Auth
|
||||
### Auth
|
||||
|
||||
`reg` will automatically try to parse your docker config credentials, but if
|
||||
not present, you can pass through flags directly.
|
||||
|
||||
## List Repositories and Tags
|
||||
### List Repositories and Tags
|
||||
|
||||
**Repositories**
|
||||
|
||||
```console
|
||||
# this command might take a while if you have hundreds of images like I do
|
||||
$ reg -r r.j3ss.co ls
|
||||
$ reg ls r.j3ss.co
|
||||
Repositories for r.j3ss.co
|
||||
REPO TAGS
|
||||
awscli latest
|
||||
|
@ -91,17 +90,31 @@ chrome beta, latest, stable
|
|||
**Tags**
|
||||
|
||||
```console
|
||||
$ reg tags tor-browser
|
||||
$ reg tags r.j3ss.co/tor-browser
|
||||
alpha
|
||||
hardened
|
||||
latest
|
||||
stable
|
||||
|
||||
# or for an offical image
|
||||
$ reg tags debian
|
||||
6
|
||||
6.0
|
||||
6.0.10
|
||||
6.0.8
|
||||
6.0.9
|
||||
7
|
||||
7-slim
|
||||
7.10
|
||||
7.11
|
||||
7.11-slim
|
||||
...
|
||||
```
|
||||
|
||||
## Get a Manifest
|
||||
### Get a Manifest
|
||||
|
||||
```console
|
||||
$ reg manifest htop
|
||||
$ reg manifest r.j3ss.co/htop
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "htop",
|
||||
|
@ -119,26 +132,32 @@ $ reg manifest htop
|
|||
}
|
||||
```
|
||||
|
||||
## Download a Layer
|
||||
### Get the Digest
|
||||
```console
|
||||
$ reg digest r.j3ss.co/htop
|
||||
sha256:791158756cc0f5b27ef8c5c546284568fc9b7f4cf1429fb736aff3ee2d2e340f
|
||||
```
|
||||
|
||||
### Download a Layer
|
||||
|
||||
```console
|
||||
$ reg layer -o chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
$ reg layer -o r.j3ss.co/chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
OR
|
||||
$ reg layer chrome@sha256:a3ed95caeb0.. > layer.tar
|
||||
$ reg layer r.j3ss.co/chrome@sha256:a3ed95caeb0.. > layer.tar
|
||||
```
|
||||
|
||||
|
||||
## Delete an Image
|
||||
### Delete an Image
|
||||
|
||||
```console
|
||||
$ reg rm chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
$ reg rm r.j3ss.co/chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
Deleted chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
```
|
||||
|
||||
## Vulnerability Reports
|
||||
### Vulnerability Reports
|
||||
|
||||
```console
|
||||
$ reg vulns --clair https://clair.j3ss.co chrome
|
||||
$ reg vulns --clair https://clair.j3ss.co r.j3ss.co/chrome
|
||||
Found 32 vulnerabilities
|
||||
CVE-2015-5180: [Low]
|
||||
|
||||
|
@ -175,7 +194,75 @@ Medium: 3
|
|||
High: 1
|
||||
```
|
||||
|
||||
## Testing
|
||||
### Generating Static Website for a Registry
|
||||
|
||||
`reg` bundles a HTTP server that periodically generates a static website
|
||||
with a list of registry images and serves it to the web.
|
||||
|
||||
It will run vulnerability scanning if you
|
||||
have a [CoreOS Clair](https://github.com/coreos/clair) server set up
|
||||
and pass the url with the `--clair` flag.
|
||||
|
||||
It is possible to run `reg server` just as a one time static generator.
|
||||
`--once` flag makes the `server` command exit after it builds the HTML listing.
|
||||
|
||||
There is a demo at [r.j3ss.co](https://r.j3ss.co).
|
||||
|
||||
**Usage:**
|
||||
|
||||
```console
|
||||
$ reg server -h
|
||||
Usage: reg server [OPTIONS]
|
||||
|
||||
Run a static UI server for a registry.
|
||||
|
||||
Flags:
|
||||
|
||||
-u, --username username for the registry (default: <none>)
|
||||
--listen-address address to listen on (default: <none>)
|
||||
--asset-path Path to assets and templates (default: <none>)
|
||||
-f, --force-non-ssl force allow use of non-ssl (default: false)
|
||||
--once generate the templates once and then exit (default: false)
|
||||
--skip-ping skip pinging the registry while establishing connection (default: false)
|
||||
--timeout timeout for HTTP requests (default: 1m0s)
|
||||
--cert path to ssl cert (default: <none>)
|
||||
-d enable debug logging (default: false)
|
||||
--key path to ssl key (default: <none>)
|
||||
--port port for server to run on (default: 8080)
|
||||
-r, --registry URL to the private registry (ex. r.j3ss.co) (default: <none>)
|
||||
--clair url to clair instance (default: <none>)
|
||||
-k, --insecure do not verify tls certificates (default: false)
|
||||
--interval interval to generate new index.html's at (default: 1h0m0s)
|
||||
-p, --password password for the registry (default: <none>)
|
||||
```
|
||||
|
||||
**Screenshots:**
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Using Self-Signed Certs with a Registry
|
||||
|
||||
We do not allow users to pass all the custom certificate flags on commands
|
||||
because it is unnecessarily messy and can be handled through Linux itself.
|
||||
Which we believe is a better user experience than having to pass three
|
||||
different flags just to communicate with a registry using self-signed or
|
||||
private certificates.
|
||||
|
||||
Below are instructions on adding a self-signed or private certificate to your
|
||||
trusted ca-certificates on Linux.
|
||||
|
||||
Make sure you have the package `ca-certificates` installed.
|
||||
|
||||
Copy the public half of your CA certificate (the one used to sign the CSR) into
|
||||
the CA certificate directory (as root):
|
||||
|
||||
```console
|
||||
$ cp cacert.pem /usr/share/ca-certificates
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you plan on contributing you should be able to run the tests locally. The
|
||||
tests run for CI via docker-in-docker. But running locally with `go test`, you
|
||||
|
@ -186,5 +273,7 @@ Add the flag `--insecure-registry localhost:5000` to your docker daemon,
|
|||
documented [here](https://docs.docker.com/registry/insecure/) for testing
|
||||
against an insecure registry.
|
||||
|
||||
OR run `make dind dtest` to avoid having to change your local docker config and
|
||||
**OR**
|
||||
|
||||
Run `make dind dtest` to avoid having to change your local docker config and
|
||||
to run the tests as docker-in-docker.
|
||||
|
|
2
vendor/github.com/genuinetools/reg/VERSION.txt
generated
vendored
2
vendor/github.com/genuinetools/reg/VERSION.txt
generated
vendored
|
@ -1 +1 @@
|
|||
v0.13.0
|
||||
v0.15.8
|
||||
|
|
172
vendor/github.com/genuinetools/reg/basic.mk
generated
vendored
Normal file
172
vendor/github.com/genuinetools/reg/basic.mk
generated
vendored
Normal file
|
@ -0,0 +1,172 @@
|
|||
# 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 "+ $@"
|
||||
@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 | tee /dev/stderr
|
||||
|
||||
.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 "+ $@"
|
||||
@$(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: 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 # 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."
|
||||
|
||||
REGISTRY := r.j3ss.co
|
||||
.PHONY: image
|
||||
image: ## Create the docker image from the Dockerfile.
|
||||
@docker build --rm --force-rm -t $(REGISTRY)/$(NAME) .
|
||||
|
||||
.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) Gopkg.toml Gopkg.lock
|
||||
@$(RM) go.mod go.sum
|
||||
@$(RM) -r vendor
|
||||
@GO111MODULE=on $(GO) mod init
|
||||
@GO111MODULE=on $(GO) mod tidy
|
||||
@GO111MODULE=on $(GO) mod vendor
|
||||
|
||||
.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}'
|
71
vendor/github.com/genuinetools/reg/clair/ancestry.go
generated
vendored
Normal file
71
vendor/github.com/genuinetools/reg/clair/ancestry.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/coreos/clair/api/v3/clairpb"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNilGRPCConn holds the error for when the grpc connection is nil.
|
||||
ErrNilGRPCConn = errors.New("grpcConn cannot be nil")
|
||||
)
|
||||
|
||||
// GetAncestry displays an ancestry and all of its features and vulnerabilities.
|
||||
func (c *Clair) GetAncestry(name string) (*clairpb.GetAncestryResponse_Ancestry, error) {
|
||||
c.Logf("clair.ancestry.get name=%s", name)
|
||||
|
||||
if c.grpcConn == nil {
|
||||
return nil, ErrNilGRPCConn
|
||||
}
|
||||
|
||||
client := clairpb.NewAncestryServiceClient(c.grpcConn)
|
||||
|
||||
resp, err := client.GetAncestry(context.Background(), &clairpb.GetAncestryRequest{
|
||||
AncestryName: name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return nil, errors.New("ancestry response was nil")
|
||||
}
|
||||
|
||||
if resp.GetStatus() != nil {
|
||||
c.Logf("clair.ancestry.get ClairStatus=%#v", *resp.GetStatus())
|
||||
}
|
||||
|
||||
return resp.GetAncestry(), nil
|
||||
}
|
||||
|
||||
// PostAncestry performs the analysis of all layers from the provided path.
|
||||
func (c *Clair) PostAncestry(name string, layers []*clairpb.PostAncestryRequest_PostLayer) error {
|
||||
c.Logf("clair.ancestry.post name=%s", name)
|
||||
|
||||
if c.grpcConn == nil {
|
||||
return ErrNilGRPCConn
|
||||
}
|
||||
|
||||
client := clairpb.NewAncestryServiceClient(c.grpcConn)
|
||||
|
||||
resp, err := client.PostAncestry(context.Background(), &clairpb.PostAncestryRequest{
|
||||
AncestryName: name,
|
||||
Layers: layers,
|
||||
Format: "Docker",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return errors.New("ancestry response was nil")
|
||||
}
|
||||
|
||||
if resp.GetStatus() != nil {
|
||||
c.Logf("clair.ancestry.post ClairStatus=%#v", *resp.GetStatus())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
105
vendor/github.com/genuinetools/reg/clair/clair.go
generated
vendored
Normal file
105
vendor/github.com/genuinetools/reg/clair/clair.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Clair defines the client for retriving information from the clair API.
|
||||
type Clair struct {
|
||||
URL string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
grpcConn *grpc.ClientConn
|
||||
}
|
||||
|
||||
// LogfCallback is the callback for formatting logs.
|
||||
type LogfCallback func(format string, args ...interface{})
|
||||
|
||||
// Quiet discards logs silently.
|
||||
func Quiet(format string, args ...interface{}) {}
|
||||
|
||||
// Log passes log messages to the logging package.
|
||||
func Log(format string, args ...interface{}) {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Opt holds the options for a new clair client.
|
||||
type Opt struct {
|
||||
Debug bool
|
||||
Insecure bool
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// New creates a new Clair struct with the given URL and credentials.
|
||||
func New(url string, opt Opt) (*Clair, error) {
|
||||
transport := http.DefaultTransport
|
||||
|
||||
grpcOpt := []grpc.DialOption{}
|
||||
|
||||
if opt.Insecure {
|
||||
transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
|
||||
grpcOpt = append(grpcOpt, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
errorTransport := &ErrorTransport{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
// set the logging
|
||||
logf := Quiet
|
||||
if opt.Debug {
|
||||
logf = Log
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(url, grpcOpt...)
|
||||
if err != nil {
|
||||
logf("grpc dial %s failed: %v", url, err)
|
||||
}
|
||||
|
||||
registry := &Clair{
|
||||
URL: url,
|
||||
Client: &http.Client{
|
||||
Timeout: opt.Timeout,
|
||||
Transport: errorTransport,
|
||||
},
|
||||
Logf: logf,
|
||||
grpcConn: conn,
|
||||
}
|
||||
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
// url returns a clair URL with the passed arguements 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
c.Logf("clair.clair resp.Status=%s", resp.Status)
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
||||
c.Logf("clair.clair resp.Status=%s, body=%s", resp.Status, response)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Header, nil
|
||||
}
|
46
vendor/github.com/genuinetools/reg/clair/errortransport.go
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/clair/errortransport.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type httpStatusError struct {
|
||||
Response *http.Response
|
||||
Body []byte // Copied from `Response.Body` to avoid problems with unclosed bodies later. Nobody calls `err.Response.Body.Close()`, ever.
|
||||
}
|
||||
|
||||
func (err *httpStatusError) Error() string {
|
||||
return fmt.Sprintf("http: non-successful response (status=%v body=%q)", err.Response.StatusCode, err.Body)
|
||||
}
|
||||
|
||||
var _ error = &httpStatusError{}
|
||||
|
||||
// ErrorTransport defines the data structure for returning errors from the round tripper.
|
||||
type ErrorTransport struct {
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for the error transport.
|
||||
func (t *ErrorTransport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
resp, err := t.Transport.RoundTrip(request)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 500 {
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("http: failed to read response body (status=%v, err=%q)", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
return nil, &httpStatusError{
|
||||
Response: resp,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
81
vendor/github.com/genuinetools/reg/clair/layer.go
generated
vendored
Normal file
81
vendor/github.com/genuinetools/reg/clair/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respLayer.Error != nil {
|
||||
return nil, fmt.Errorf("clair error: %s", respLayer.Error.Message)
|
||||
}
|
||||
|
||||
return respLayer.Layer, nil
|
||||
}
|
||||
|
||||
// PostLayer performs the analysis of a Layer from the provided path.
|
||||
func (c *Clair) PostLayer(layer *Layer) (*Layer, error) {
|
||||
url := c.url("/v1/layers")
|
||||
c.Logf("clair.layers.post url=%s name=%s", url, layer.Name)
|
||||
|
||||
b, err := json.Marshal(layerEnvelope{Layer: layer})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Logf("clair.layers.post req.Body=%s", string(b))
|
||||
|
||||
resp, err := c.Client.Post(url, "application/json", bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
c.Logf("clair.layers.post resp.Status=%s", resp.Status)
|
||||
|
||||
var respLayer layerEnvelope
|
||||
if err := json.NewDecoder(resp.Body).Decode(&respLayer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respLayer.Error != nil {
|
||||
return nil, fmt.Errorf("clair error: %s", respLayer.Error.Message)
|
||||
}
|
||||
|
||||
return respLayer.Layer, err
|
||||
}
|
||||
|
||||
// DeleteLayer removes a layer reference from clair.
|
||||
func (c *Clair) DeleteLayer(name string) error {
|
||||
url := c.url("/v1/layers/%s", name)
|
||||
c.Logf("clair.layers.delete url=%s name=%s", url, name)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
c.Logf("clair.clair resp.Status=%s", resp.Status)
|
||||
|
||||
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
95
vendor/github.com/genuinetools/reg/clair/layerutil.go
generated
vendored
Normal file
95
vendor/github.com/genuinetools/reg/clair/layerutil.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/clair/api/v3/clairpb"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
var parentName string
|
||||
if index < len(fsLayers)-1 {
|
||||
parentName = fsLayers[index+1].Digest.String()
|
||||
}
|
||||
|
||||
// Form the path.
|
||||
p := strings.Join([]string{r.URL, "v2", image, "blobs", fsLayers[index].Digest.String()}, "/")
|
||||
|
||||
// Get the headers.
|
||||
h, err := r.Headers(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Layer{
|
||||
Name: fsLayers[index].Digest.String(),
|
||||
Path: p,
|
||||
ParentName: parentName,
|
||||
Format: "Docker",
|
||||
Headers: h,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Form the path.
|
||||
p := strings.Join([]string{r.URL, "v2", image, "blobs", fsLayer.Digest.String()}, "/")
|
||||
|
||||
// Get the headers.
|
||||
h, err := r.Headers(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &clairpb.PostAncestryRequest_PostLayer{
|
||||
Hash: fsLayer.Digest.String(),
|
||||
Path: p,
|
||||
Headers: h,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Clair) getLayers(r *registry.Registry, repo, tag string, filterEmpty bool) (map[int]distribution.Descriptor, error) {
|
||||
ok := true
|
||||
// Get the manifest to pass to clair.
|
||||
mf, err := r.ManifestV2(repo, tag)
|
||||
if err != nil {
|
||||
ok = false
|
||||
c.Logf("couldn't retrieve manifest v2, falling back to v1")
|
||||
}
|
||||
|
||||
filteredLayers := map[int]distribution.Descriptor{}
|
||||
|
||||
// Filter out the empty layers.
|
||||
if ok {
|
||||
for i := 0; i < len(mf.Layers); i++ {
|
||||
if filterEmpty && IsEmptyLayer(mf.Layers[i].Digest) {
|
||||
continue
|
||||
} else {
|
||||
filteredLayers[len(mf.Layers)-i-1] = mf.Layers[i]
|
||||
}
|
||||
}
|
||||
|
||||
return filteredLayers, nil
|
||||
}
|
||||
|
||||
m, err := r.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting the v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.FSLayers); i++ {
|
||||
if filterEmpty && IsEmptyLayer(m.FSLayers[i].BlobSum) {
|
||||
continue
|
||||
} else {
|
||||
filteredLayers[i] = distribution.Descriptor{
|
||||
Digest: m.FSLayers[i].BlobSum,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filteredLayers, nil
|
||||
}
|
77
vendor/github.com/genuinetools/reg/clair/types.go
generated
vendored
Normal file
77
vendor/github.com/genuinetools/reg/clair/types.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
package clair
|
||||
|
||||
import "github.com/opencontainers/go-digest"
|
||||
|
||||
const (
|
||||
// EmptyLayerBlobSum is the blob sum of empty layers.
|
||||
EmptyLayerBlobSum = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
|
||||
|
||||
// LegacyEmptyLayerBlobSum is the blob sum of empty layers used by docker
|
||||
// before it could support a truly empty layer.
|
||||
LegacyEmptyLayerBlobSum = "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
)
|
||||
|
||||
// IsEmptyLayer determines whether the blob sum is one of the known empty
|
||||
// layers.
|
||||
func IsEmptyLayer(blobSum digest.Digest) bool {
|
||||
return blobSum == EmptyLayerBlobSum || blobSum == LegacyEmptyLayerBlobSum
|
||||
}
|
||||
|
||||
var (
|
||||
// Priorities are the vulnerability priority labels.
|
||||
Priorities = []string{"Unknown", "Negligible", "Low", "Medium", "High", "Critical", "Defcon1", "Fixable"}
|
||||
)
|
||||
|
||||
// Error describes the structure of a clair error.
|
||||
type Error struct {
|
||||
Message string `json:"Message,omitempty"`
|
||||
}
|
||||
|
||||
// Layer represents an image layer.
|
||||
type Layer struct {
|
||||
Name string `json:"Name,omitempty"`
|
||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||
Path string `json:"Path,omitempty"`
|
||||
Headers map[string]string `json:"Headers,omitempty"`
|
||||
ParentName string `json:"ParentName,omitempty"`
|
||||
Format string `json:"Format,omitempty"`
|
||||
IndexedByVersion int `json:"IndexedByVersion,omitempty"`
|
||||
Features []feature `json:"Features,omitempty"`
|
||||
}
|
||||
|
||||
type layerEnvelope struct {
|
||||
Layer *Layer `json:"Layer,omitempty"`
|
||||
Error *Error `json:"Error,omitempty"`
|
||||
}
|
||||
|
||||
// Vulnerability represents vulnerability entity returned by Clair.
|
||||
type Vulnerability struct {
|
||||
Name string `json:"Name,omitempty"`
|
||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||
Description string `json:"Description,omitempty"`
|
||||
Link string `json:"Link,omitempty"`
|
||||
Severity string `json:"Severity,omitempty"`
|
||||
Metadata map[string]interface{} `json:"Metadata,omitempty"`
|
||||
FixedBy string `json:"FixedBy,omitempty"`
|
||||
FixedIn []feature `json:"FixedIn,omitempty"`
|
||||
}
|
||||
|
||||
// VulnerabilityReport represents the result of a vulnerability scan of a repo.
|
||||
type VulnerabilityReport struct {
|
||||
Name string
|
||||
RegistryURL string
|
||||
Repo string
|
||||
Tag string
|
||||
Date string
|
||||
Vulns []Vulnerability
|
||||
VulnsBySeverity map[string][]Vulnerability
|
||||
BadVulns int
|
||||
}
|
||||
type feature struct {
|
||||
Name string `json:"Name,omitempty"`
|
||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||
VersionFormat string `json:"VersionFormat,omitempty"`
|
||||
Version string `json:"Version,omitempty"`
|
||||
Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
|
||||
AddedBy string `json:"AddedBy,omitempty"`
|
||||
}
|
163
vendor/github.com/genuinetools/reg/clair/vulns.go
generated
vendored
Normal file
163
vendor/github.com/genuinetools/reg/clair/vulns.go
generated
vendored
Normal file
|
@ -0,0 +1,163 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/clair/api/v3/clairpb"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
// Vulnerabilities scans the given repo and tag.
|
||||
func (c *Clair) Vulnerabilities(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
|
||||
report := VulnerabilityReport{
|
||||
RegistryURL: r.Domain,
|
||||
Repo: repo,
|
||||
Tag: tag,
|
||||
Date: time.Now().Local().Format(time.RFC1123),
|
||||
VulnsBySeverity: make(map[string][]Vulnerability),
|
||||
}
|
||||
|
||||
filteredLayers, err := c.getLayers(r, repo, tag, true)
|
||||
if err != nil {
|
||||
return report, fmt.Errorf("getting filtered layers failed: %v", err)
|
||||
}
|
||||
|
||||
if len(filteredLayers) == 0 {
|
||||
fmt.Printf("No need to analyse image %s:%s as there is no non-emtpy layer", repo, tag)
|
||||
return report, nil
|
||||
}
|
||||
|
||||
for i := len(filteredLayers) - 1; i >= 0; i-- {
|
||||
// Form the clair layer.
|
||||
l, err := c.NewClairLayer(r, repo, filteredLayers, i)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
// Post the layer.
|
||||
if _, err := c.PostLayer(l); err != nil {
|
||||
return report, err
|
||||
}
|
||||
}
|
||||
|
||||
report.Name = filteredLayers[0].Digest.String()
|
||||
|
||||
vl, err := c.GetLayer(filteredLayers[0].Digest.String(), true, true)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
// Get the vulns.
|
||||
for _, f := range vl.Features {
|
||||
report.Vulns = append(report.Vulns, f.Vulnerabilities...)
|
||||
}
|
||||
|
||||
vulnsBy := func(sev string, store map[string][]Vulnerability) []Vulnerability {
|
||||
items, found := store[sev]
|
||||
if !found {
|
||||
items = make([]Vulnerability, 0)
|
||||
store[sev] = items
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// group by severity
|
||||
for _, v := range report.Vulns {
|
||||
sevRow := vulnsBy(v.Severity, report.VulnsBySeverity)
|
||||
report.VulnsBySeverity[v.Severity] = append(sevRow, v)
|
||||
}
|
||||
|
||||
// calculate number of bad vulns
|
||||
report.BadVulns = len(report.VulnsBySeverity["High"]) + len(report.VulnsBySeverity["Critical"]) + len(report.VulnsBySeverity["Defcon1"])
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
// VulnerabilitiesV3 scans the given repo and tag using the clair v3 API.
|
||||
func (c *Clair) VulnerabilitiesV3(r *registry.Registry, repo, tag string) (VulnerabilityReport, error) {
|
||||
report := VulnerabilityReport{
|
||||
RegistryURL: r.Domain,
|
||||
Repo: repo,
|
||||
Tag: tag,
|
||||
Date: time.Now().Local().Format(time.RFC1123),
|
||||
VulnsBySeverity: make(map[string][]Vulnerability),
|
||||
}
|
||||
|
||||
layers, err := c.getLayers(r, repo, tag, false)
|
||||
if err != nil {
|
||||
return report, fmt.Errorf("getting filtered layers failed: %v", err)
|
||||
}
|
||||
|
||||
if len(layers) == 0 {
|
||||
fmt.Printf("No need to analyse image %s:%s as there is no non-empty layer", repo, tag)
|
||||
return report, nil
|
||||
}
|
||||
|
||||
report.Name = layers[0].Digest.String()
|
||||
|
||||
clairLayers := []*clairpb.PostAncestryRequest_PostLayer{}
|
||||
for i := len(layers) - 1; i >= 0; i-- {
|
||||
// Form the clair layer.
|
||||
l, err := c.NewClairV3Layer(r, repo, layers[i])
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
// Append the layer.
|
||||
clairLayers = append(clairLayers, l)
|
||||
}
|
||||
|
||||
// Post the ancestry.
|
||||
if err := c.PostAncestry(layers[0].Digest.String(), clairLayers); err != nil {
|
||||
return report, fmt.Errorf("posting ancestry failed: %v", err)
|
||||
}
|
||||
|
||||
// Get the ancestry.
|
||||
vl, err := c.GetAncestry(layers[0].Digest.String())
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
if vl == nil {
|
||||
return report, errors.New("ancestry response was nil")
|
||||
}
|
||||
|
||||
// Get the vulns.
|
||||
for _, l := range vl.GetLayers() {
|
||||
for _, f := range l.GetDetectedFeatures() {
|
||||
for _, v := range f.GetVulnerabilities() {
|
||||
report.Vulns = append(report.Vulns, Vulnerability{
|
||||
Name: v.Name,
|
||||
NamespaceName: v.NamespaceName,
|
||||
Description: v.Description,
|
||||
Link: v.Link,
|
||||
Severity: v.Severity,
|
||||
Metadata: map[string]interface{}{v.Metadata: ""},
|
||||
FixedBy: v.FixedBy,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vulnsBy := func(sev string, store map[string][]Vulnerability) []Vulnerability {
|
||||
items, found := store[sev]
|
||||
if !found {
|
||||
items = make([]Vulnerability, 0)
|
||||
store[sev] = items
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Group by severity.
|
||||
for _, v := range report.Vulns {
|
||||
sevRow := vulnsBy(v.Severity, report.VulnsBySeverity)
|
||||
report.VulnsBySeverity[v.Severity] = append(sevRow, v)
|
||||
}
|
||||
|
||||
// calculate number of bad vulns
|
||||
report.BadVulns = len(report.VulnsBySeverity["High"]) + len(report.VulnsBySeverity["Critical"]) + len(report.VulnsBySeverity["Defcon1"])
|
||||
|
||||
return report, nil
|
||||
}
|
31
vendor/github.com/genuinetools/reg/delete.go
generated
vendored
31
vendor/github.com/genuinetools/reg/delete.go
generated
vendored
|
@ -1,31 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var deleteCommand = cli.Command{
|
||||
Name: "delete",
|
||||
Aliases: []string{"rm"},
|
||||
Usage: "delete a specific reference of a repository",
|
||||
Action: func(c *cli.Context) error {
|
||||
if len(c.Args()) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.Delete(repo, ref); err != nil {
|
||||
return fmt.Errorf("Delete %s@%s failed: %v", repo, ref, err)
|
||||
}
|
||||
fmt.Printf("Deleted %s@%s\n", repo, ref)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
37
vendor/github.com/genuinetools/reg/delete_test.go
generated
vendored
37
vendor/github.com/genuinetools/reg/delete_test.go
generated
vendored
|
@ -1,37 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
// Make sure we have busybox in list.
|
||||
out, err := run("ls")
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(out), err)
|
||||
}
|
||||
expected := []string{"alpine latest", "busybox glibc, musl, latest"}
|
||||
for _, e := range expected {
|
||||
if !strings.Contains(out, e) {
|
||||
t.Logf("expected to contain: %s\ngot: %s", e, out)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove busybox image.
|
||||
if out, err := run("rm", "busybox"); err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(out), err)
|
||||
}
|
||||
|
||||
// Make sure there is no busybox in list.
|
||||
out, err = run("ls")
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(out), err)
|
||||
}
|
||||
expected = []string{"alpine latest", "busybox glibc, musl\n"}
|
||||
for _, e := range expected {
|
||||
if !strings.Contains(out, e) {
|
||||
t.Logf("expected to contain: %s\ngot: %s", e, out)
|
||||
}
|
||||
}
|
||||
}
|
48
vendor/github.com/genuinetools/reg/digest.go
generated
vendored
Normal file
48
vendor/github.com/genuinetools/reg/digest.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
const digestHelp = `Get the digest for a repository.`
|
||||
|
||||
func (cmd *digestCommand) Name() string { return "digest" }
|
||||
func (cmd *digestCommand) Args() string { return "[OPTIONS] NAME[:TAG]" }
|
||||
func (cmd *digestCommand) ShortHelp() string { return digestHelp }
|
||||
func (cmd *digestCommand) LongHelp() string { return digestHelp }
|
||||
func (cmd *digestCommand) Hidden() bool { return false }
|
||||
|
||||
func (cmd *digestCommand) Register(fs *flag.FlagSet) {}
|
||||
|
||||
type digestCommand struct{}
|
||||
|
||||
func (cmd *digestCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(image.Domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the digest.
|
||||
digest, err := r.Digest(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(digest.String())
|
||||
|
||||
return nil
|
||||
}
|
52
vendor/github.com/genuinetools/reg/go.mod
generated
vendored
Normal file
52
vendor/github.com/genuinetools/reg/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
module github.com/genuinetools/reg
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.11 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
|
||||
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb // indirect
|
||||
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c // indirect
|
||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f
|
||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f
|
||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43
|
||||
github.com/docker/docker-credential-helpers v0.6.1 // indirect
|
||||
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df // indirect
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect
|
||||
github.com/docker/go-units v0.3.3 // 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-20180910213200-1c141f661797
|
||||
github.com/gogo/protobuf v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/mux v1.6.2
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0
|
||||
github.com/onsi/gomega v1.4.2 // indirect
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2
|
||||
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/pkg/errors v0.8.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1 // indirect
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
|
||||
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371
|
||||
github.com/sirupsen/logrus v1.0.6
|
||||
github.com/stretchr/testify v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b // indirect
|
||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7 // indirect
|
||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e // indirect
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494 // indirect
|
||||
google.golang.org/grpc v1.15.0
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
|
||||
gotest.tools v2.1.0+incompatible // indirect
|
||||
)
|
134
vendor/github.com/genuinetools/reg/go.sum
generated
vendored
Normal file
134
vendor/github.com/genuinetools/reg/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
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/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb h1:qSMRxG547z/BgQmyVyADxaMADQXVAD9uleP2sQeClbo=
|
||||
github.com/containerd/continuity v0.0.0-20180921161001-7f53d412b9eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a h1:glxUtT0RlaVJU86kg78ygzfhwW6D+uj5H+aOK01QDgI=
|
||||
github.com/coreos/clair v0.0.0-20180919182544-44ae4bc9590a/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/docker/cli v0.0.0-20180920165730-54c19e67f69c h1:QlAVcyoF7QQVN7zV+xYBjgwtRVlRU3WCTCpb2mcqQrM=
|
||||
github.com/docker/cli v0.0.0-20180920165730-54c19e67f69c/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f h1:hYf+mPizfvpH6VgIxdntnOmQHd1F1mQUc1oG+j3Ol2g=
|
||||
github.com/docker/distribution v0.0.0-20180920194744-16128bbac47f/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f h1:BrSaWINJwChqVJoPy/AR82wCtE+Sa1MAB2UiO3n2zcY=
|
||||
github.com/docker/docker v0.0.0-20180924202107-a9c061deec0f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43 h1:NDAvq++bKWHxKDxEclVIgl737gMim2WDYoKHFQSBXfA=
|
||||
github.com/docker/docker-ce v0.0.0-20180924210327-f53bd8bb8e43/go.mod h1:l1FUGRYBvbjnZ8MS6A2xOji4aZFlY/Qmgz7p4oXH7ac=
|
||||
github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g=
|
||||
github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df h1:ADMjlaDGEn0OOQIieyxanhAt41jcngf8rf78X2eKNLw=
|
||||
github.com/docker/go-connections v0.0.0-20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 h1:yWHOI+vFjEsAakUTSrtqc/SAHrhSkmn48pqjidZX3QA=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797 h1:SGpZXDd/CFeDIY4Rq5cFO8K/uqDblHUxjlzOmjFpvRg=
|
||||
github.com/genuinetools/pkg v0.0.0-20180910213200-1c141f661797/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
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/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
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 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0 h1:WcmKMm43DR7RdtlkEXQJyo5ws8iTp98CyhCCbOHMvNI=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
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/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
|
||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2 h1:QhPf3A2AZW3tTGvHPg0TA+CR3oHbVLlXUhlghqISp1I=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/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.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/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/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1 h1:mEzWvBiJdUbhqHRT6kNSGzD6IDcWCWF2uAhrEEE740M=
|
||||
github.com/prometheus/client_golang v0.0.0-20180924113449-f69c853d21c1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7 h1:NgR6WN8nQ4SmFC1sSUHY8SriLuWCZ6cCIQtH4vDZN3c=
|
||||
github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
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-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7 h1:zKzVgSQ8WOSHzD7I4k8LQjrHUUCNOlBsgc0PcYLVNnY=
|
||||
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e h1:LSlw/Dbj0MkNvPYAAkGinYmGliq+aqS7eKPYlE4oWC4=
|
||||
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 h1:JG/0uqcGdTNgq7FdU+61l5Pdmb8putNZlXb65bJBROs=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494 h1:WIJ3k0fGJRrCVzZTuGmcBnUzWeSDpWiP+jUOxWkA8bo=
|
||||
google.golang.org/genproto v0.0.0-20180924164928-221a8d4f7494/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.15.0 h1:Az/KuahOM4NAidTEuJCv/RonAA7rYsTPkqXVjr+8OOw=
|
||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
|
||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
317
vendor/github.com/genuinetools/reg/handlers.go
generated
vendored
Normal file
317
vendor/github.com/genuinetools/reg/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,317 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type registryController struct {
|
||||
reg *registry.Registry
|
||||
cl *clair.Clair
|
||||
interval time.Duration
|
||||
l sync.Mutex
|
||||
tmpl *template.Template
|
||||
generateOnly bool
|
||||
}
|
||||
|
||||
type v1Compatibility struct {
|
||||
ID string `json:"id"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// A Repository holds data after a vulnerability scan of a single repo
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
Created time.Time `json:"created"`
|
||||
URI string `json:"uri"`
|
||||
VulnerabilityReport clair.VulnerabilityReport `json:"vulnerability"`
|
||||
}
|
||||
|
||||
// A AnalysisResult holds all vulnerabilities of a scan
|
||||
type AnalysisResult struct {
|
||||
Repositories []Repository `json:"repositories"`
|
||||
RegistryDomain string `json:"registryDomain"`
|
||||
Name string `json:"name"`
|
||||
LastUpdated string `json:"lastUpdated"`
|
||||
HasVulns bool `json:"hasVulns"`
|
||||
UpdateInterval time.Duration
|
||||
}
|
||||
|
||||
func (rc *registryController) repositories(staticDir string) error {
|
||||
rc.l.Lock()
|
||||
defer rc.l.Unlock()
|
||||
|
||||
logrus.Infof("fetching catalog for %s...", rc.reg.Domain)
|
||||
|
||||
result := AnalysisResult{
|
||||
RegistryDomain: rc.reg.Domain,
|
||||
LastUpdated: time.Now().Local().Format(time.RFC1123),
|
||||
UpdateInterval: rc.interval,
|
||||
}
|
||||
|
||||
repoList, err := rc.reg.Catalog("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting catalog for %s failed: %v", rc.reg.Domain, err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, repo := range repoList {
|
||||
repoURI := fmt.Sprintf("%s/%s", rc.reg.Domain, repo)
|
||||
r := Repository{
|
||||
Name: repo,
|
||||
URI: repoURI,
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, r)
|
||||
|
||||
if !rc.generateOnly {
|
||||
// Continue early because we don't need to generate the tags pages.
|
||||
continue
|
||||
}
|
||||
|
||||
// Generate the tags pages in a go routine.
|
||||
wg.Add(1)
|
||||
go func(repo string) {
|
||||
defer wg.Done()
|
||||
logrus.Infof("generating static tags page for repo %s", repo)
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
logrus.Warnf("generating tags template for repo %q failed: %v", repo, err)
|
||||
}
|
||||
// Create the directory for the static tags files.
|
||||
tagsDir := filepath.Join(staticDir, "repo", repo, "tags")
|
||||
if err := os.MkdirAll(tagsDir, 0755); err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
|
||||
// Write the tags file.
|
||||
tagsFile := filepath.Join(tagsDir, "index.html")
|
||||
if err := ioutil.WriteFile(tagsFile, b, 0755); err != nil {
|
||||
logrus.Warnf("writing tags template for repo %s to %sfailed: %v", repo, tagsFile, err)
|
||||
}
|
||||
}(repo)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Parse & execute the template.
|
||||
logrus.Info("executing the template repositories")
|
||||
|
||||
// Create the static directory.
|
||||
if err := os.MkdirAll(staticDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Creating the index file.
|
||||
path := filepath.Join(staticDir, "index.html")
|
||||
logrus.Debugf("creating/opening file %s", path)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Execute the template on the index.html file.
|
||||
if err := rc.tmpl.ExecuteTemplate(f, "repositories", result); err != nil {
|
||||
f.Close()
|
||||
return fmt.Errorf("execute template repositories failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rc *registryController) tagsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Info("fetching tags")
|
||||
|
||||
// Parse the query variables.
|
||||
vars := mux.Vars(r)
|
||||
repo, err := url.QueryUnescape(vars["repo"])
|
||||
if err != nil || repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
// Generate the tags template.
|
||||
b, err := rc.generateTagsTemplate(repo, rc.cl != nil)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("getting tags for %s failed: %v", repo, err)
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Getting tags for %s failed", repo)
|
||||
return
|
||||
}
|
||||
|
||||
// Write the template.
|
||||
fmt.Fprint(w, string(b))
|
||||
}
|
||||
|
||||
func (rc *registryController) generateTagsTemplate(repo string, hasVulns bool) ([]byte, error) {
|
||||
// Get the tags from the server.
|
||||
tags, err := rc.reg.Tags(repo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting tags for %s failed: %v", repo, err)
|
||||
}
|
||||
|
||||
// Error out if there are no tags / images
|
||||
// (the above err != nil does not error out when nothing has been found)
|
||||
if len(tags) == 0 {
|
||||
return nil, fmt.Errorf("no tags found for repo: %s", repo)
|
||||
}
|
||||
|
||||
result := AnalysisResult{
|
||||
RegistryDomain: rc.reg.Domain,
|
||||
LastUpdated: time.Now().Local().Format(time.RFC1123),
|
||||
UpdateInterval: rc.interval,
|
||||
Name: repo,
|
||||
HasVulns: hasVulns, // if we have a clair client we can return vulns
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
// get the manifest
|
||||
m1, err := rc.reg.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
}
|
||||
|
||||
var createdDate time.Time
|
||||
for _, h := range m1.History {
|
||||
var comp v1Compatibility
|
||||
|
||||
if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
}
|
||||
|
||||
createdDate = comp.Created
|
||||
break
|
||||
}
|
||||
|
||||
repoURI := fmt.Sprintf("%s/%s", rc.reg.Domain, repo)
|
||||
if tag != "latest" {
|
||||
repoURI += ":" + tag
|
||||
}
|
||||
rp := Repository{
|
||||
Name: repo,
|
||||
Tag: tag,
|
||||
URI: repoURI,
|
||||
Created: createdDate,
|
||||
}
|
||||
|
||||
result.Repositories = append(result.Repositories, rp)
|
||||
}
|
||||
|
||||
// Execute the template.
|
||||
var buf bytes.Buffer
|
||||
if err := rc.tmpl.ExecuteTemplate(&buf, "tags", result); err != nil {
|
||||
return nil, fmt.Errorf("template rendering failed: %v", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (rc *registryController) vulnerabilitiesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Info("fetching vulnerabilities")
|
||||
|
||||
// Parse the query variables.
|
||||
vars := mux.Vars(r)
|
||||
repo, err := url.QueryUnescape(vars["repo"])
|
||||
tag := vars["tag"]
|
||||
|
||||
if err != nil || repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
if tag == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty tag")
|
||||
return
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(repo + ":" + tag)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("parsing image %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the vulnerability report.
|
||||
result, err := rc.cl.VulnerabilitiesV3(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())
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("vulnerability scanning for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(r.URL.String(), ".json") {
|
||||
js, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("json marshal failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(js)
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the template.
|
||||
if err := rc.tmpl.ExecuteTemplate(w, "vulns", result); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "vulnerabilities",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
37
vendor/github.com/genuinetools/reg/internal/binutils/generate.go
generated
vendored
Normal file
37
vendor/github.com/genuinetools/reg/internal/binutils/generate.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/shurcooL/vfsgen"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
// Generate server assets.
|
||||
assets := http.Dir(filepath.Join(wd, "server/static"))
|
||||
if err := vfsgen.Generate(assets, vfsgen.Options{
|
||||
Filename: filepath.Join(wd, "internal/binutils/static", "static.go"),
|
||||
PackageName: "static",
|
||||
VariableName: "Assets",
|
||||
}); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
// Generate template assets.
|
||||
assets = http.Dir(filepath.Join(wd, "server/templates"))
|
||||
if err := vfsgen.Generate(assets, vfsgen.Options{
|
||||
Filename: filepath.Join(wd, "internal/binutils/templates", "templates.go"),
|
||||
PackageName: "templates",
|
||||
VariableName: "Assets",
|
||||
}); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
277
vendor/github.com/genuinetools/reg/internal/binutils/static/static.go
generated
vendored
Normal file
277
vendor/github.com/genuinetools/reg/internal/binutils/static/static.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
200
vendor/github.com/genuinetools/reg/internal/binutils/templates/templates.go
generated
vendored
Normal file
200
vendor/github.com/genuinetools/reg/internal/binutils/templates/templates.go
generated
vendored
Normal file
|
@ -0,0 +1,200 @@
|
|||
// Code generated by vfsgen; DO NOT EDIT.
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Assets statically implements the virtual filesystem provided to vfsgen.
|
||||
var Assets = func() http.FileSystem {
|
||||
fs := vfsgen۰FS{
|
||||
"/": &vfsgen۰DirInfo{
|
||||
name: "/",
|
||||
modTime: time.Date(2018, 9, 10, 21, 40, 58, 715425786, time.UTC),
|
||||
},
|
||||
"/repositories.html": &vfsgen۰CompressedFileInfo{
|
||||
name: "repositories.html",
|
||||
modTime: time.Date(2018, 9, 10, 21, 40, 58, 715425786, 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),
|
||||
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),
|
||||
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"),
|
||||
},
|
||||
}
|
||||
fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{
|
||||
fs["/repositories.html"].(os.FileInfo),
|
||||
fs["/tags.html"].(os.FileInfo),
|
||||
fs["/vulns.html"].(os.FileInfo),
|
||||
}
|
||||
|
||||
return fs
|
||||
}()
|
||||
|
||||
type vfsgen۰FS map[string]interface{}
|
||||
|
||||
func (fs vfsgen۰FS) Open(path string) (http.File, error) {
|
||||
path = pathpkg.Clean("/" + path)
|
||||
f, ok := fs[path]
|
||||
if !ok {
|
||||
return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
switch f := f.(type) {
|
||||
case *vfsgen۰CompressedFileInfo:
|
||||
gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent))
|
||||
if err != nil {
|
||||
// This should never happen because we generate the gzip bytes such that they are always valid.
|
||||
panic("unexpected error reading own gzip compressed bytes: " + err.Error())
|
||||
}
|
||||
return &vfsgen۰CompressedFile{
|
||||
vfsgen۰CompressedFileInfo: f,
|
||||
gr: gr,
|
||||
}, nil
|
||||
case *vfsgen۰DirInfo:
|
||||
return &vfsgen۰Dir{
|
||||
vfsgen۰DirInfo: f,
|
||||
}, nil
|
||||
default:
|
||||
// This should never happen because we generate only the above types.
|
||||
panic(fmt.Sprintf("unexpected type %T", f))
|
||||
}
|
||||
}
|
||||
|
||||
// vfsgen۰CompressedFileInfo is a static definition of a gzip compressed file.
|
||||
type vfsgen۰CompressedFileInfo struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
compressedContent []byte
|
||||
uncompressedSize int64
|
||||
}
|
||||
|
||||
func (f *vfsgen۰CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
|
||||
}
|
||||
func (f *vfsgen۰CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil }
|
||||
|
||||
func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte {
|
||||
return f.compressedContent
|
||||
}
|
||||
|
||||
func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name }
|
||||
func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize }
|
||||
func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 }
|
||||
func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime }
|
||||
func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false }
|
||||
func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// vfsgen۰CompressedFile is an opened compressedFile instance.
|
||||
type vfsgen۰CompressedFile struct {
|
||||
*vfsgen۰CompressedFileInfo
|
||||
gr *gzip.Reader
|
||||
grPos int64 // Actual gr uncompressed position.
|
||||
seekPos int64 // Seek uncompressed position.
|
||||
}
|
||||
|
||||
func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) {
|
||||
if f.grPos > f.seekPos {
|
||||
// Rewind to beginning.
|
||||
err = f.gr.Reset(bytes.NewReader(f.compressedContent))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.grPos = 0
|
||||
}
|
||||
if f.grPos < f.seekPos {
|
||||
// Fast-forward.
|
||||
_, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.grPos = f.seekPos
|
||||
}
|
||||
n, err = f.gr.Read(p)
|
||||
f.grPos += int64(n)
|
||||
f.seekPos = f.grPos
|
||||
return n, err
|
||||
}
|
||||
func (f *vfsgen۰CompressedFile) Seek(offset int64, whence int) (int64, error) {
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
f.seekPos = 0 + offset
|
||||
case io.SeekCurrent:
|
||||
f.seekPos += offset
|
||||
case io.SeekEnd:
|
||||
f.seekPos = f.uncompressedSize + offset
|
||||
default:
|
||||
panic(fmt.Errorf("invalid whence value: %v", whence))
|
||||
}
|
||||
return f.seekPos, nil
|
||||
}
|
||||
func (f *vfsgen۰CompressedFile) Close() error {
|
||||
return f.gr.Close()
|
||||
}
|
||||
|
||||
// vfsgen۰DirInfo is a static definition of a directory.
|
||||
type vfsgen۰DirInfo struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
entries []os.FileInfo
|
||||
}
|
||||
|
||||
func (d *vfsgen۰DirInfo) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("cannot Read from directory %s", d.name)
|
||||
}
|
||||
func (d *vfsgen۰DirInfo) Close() error { return nil }
|
||||
func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil }
|
||||
|
||||
func (d *vfsgen۰DirInfo) Name() string { return d.name }
|
||||
func (d *vfsgen۰DirInfo) Size() int64 { return 0 }
|
||||
func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
|
||||
func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime }
|
||||
func (d *vfsgen۰DirInfo) IsDir() bool { return true }
|
||||
func (d *vfsgen۰DirInfo) Sys() interface{} { return nil }
|
||||
|
||||
// vfsgen۰Dir is an opened dir instance.
|
||||
type vfsgen۰Dir struct {
|
||||
*vfsgen۰DirInfo
|
||||
pos int // Position within entries for Seek and Readdir.
|
||||
}
|
||||
|
||||
func (d *vfsgen۰Dir) Seek(offset int64, whence int) (int64, error) {
|
||||
if offset == 0 && whence == io.SeekStart {
|
||||
d.pos = 0
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
|
||||
}
|
||||
|
||||
func (d *vfsgen۰Dir) Readdir(count int) ([]os.FileInfo, error) {
|
||||
if d.pos >= len(d.entries) && count > 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if count <= 0 || count > len(d.entries)-d.pos {
|
||||
count = len(d.entries) - d.pos
|
||||
}
|
||||
e := d.entries[d.pos : d.pos+count]
|
||||
d.pos += count
|
||||
return e, nil
|
||||
}
|
99
vendor/github.com/genuinetools/reg/layer.go
generated
vendored
99
vendor/github.com/genuinetools/reg/layer.go
generated
vendored
|
@ -1,52 +1,71 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
var layerCommand = cli.Command{
|
||||
Name: "layer",
|
||||
Aliases: []string{"download"},
|
||||
Usage: "download a layer for the specific reference of a repository",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "output, o",
|
||||
Usage: "output file, default to stdout",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if len(c.Args()) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
const layerHelp = `Download a layer for a repository.`
|
||||
|
||||
repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (cmd *layerCommand) Name() string { return "layer" }
|
||||
func (cmd *layerCommand) Args() string { return "[OPTIONS] NAME[:TAG|@DIGEST]" }
|
||||
func (cmd *layerCommand) ShortHelp() string { return layerHelp }
|
||||
func (cmd *layerCommand) LongHelp() string { return layerHelp }
|
||||
func (cmd *layerCommand) Hidden() bool { return false }
|
||||
|
||||
layer, err := r.DownloadLayer(repo, digest.FromString(ref))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer layer.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(layer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.String("output") != "" {
|
||||
return ioutil.WriteFile(c.String("output"), b, 0644)
|
||||
}
|
||||
|
||||
fmt.Fprint(os.Stdout, string(b))
|
||||
|
||||
return nil
|
||||
},
|
||||
func (cmd *layerCommand) Register(fs *flag.FlagSet) {
|
||||
fs.StringVar(&cmd.output, "output", "", "output file, defaults to stdout")
|
||||
fs.StringVar(&cmd.output, "o", "", "output file, defaults to stdout")
|
||||
}
|
||||
|
||||
type layerCommand struct {
|
||||
output string
|
||||
}
|
||||
|
||||
func (cmd *layerCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(image.Domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the digest.
|
||||
digest, err := r.Digest(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Download the layer.
|
||||
layer, err := r.DownloadLayer(image.Path, digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer layer.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(layer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cmd.output) > 0 {
|
||||
return ioutil.WriteFile(cmd.output, b, 0644)
|
||||
}
|
||||
|
||||
fmt.Fprint(os.Stdout, string(b))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
33
vendor/github.com/genuinetools/reg/layer_test.go
generated
vendored
Normal file
33
vendor/github.com/genuinetools/reg/layer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLayer(t *testing.T) {
|
||||
// Get the digest.
|
||||
out, err := run("digest", fmt.Sprintf("%s/busybox", domain))
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
|
||||
tmpf := filepath.Join(os.TempDir(), "download-layer.tar")
|
||||
defer os.RemoveAll(tmpf)
|
||||
|
||||
// Download the layer.
|
||||
lines := strings.Split(strings.TrimSpace(out), "\n")
|
||||
layer := fmt.Sprintf("%s/busybox@%s", domain, strings.TrimSpace(lines[len(lines)-1]))
|
||||
out, err = run("layer", "-o", tmpf, layer)
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
|
||||
// Make sure the file exists
|
||||
if _, err := os.Stat(tmpf); os.IsNotExist(err) {
|
||||
t.Fatalf("%s should exist after downloading the layer but it didn't", tmpf)
|
||||
}
|
||||
}
|
108
vendor/github.com/genuinetools/reg/list.go
generated
vendored
108
vendor/github.com/genuinetools/reg/list.go
generated
vendored
|
@ -1,62 +1,86 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var listCommand = cli.Command{
|
||||
Name: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Usage: "list all repositories",
|
||||
Action: func(c *cli.Context) error {
|
||||
// Get the repositories via catalog.
|
||||
repos, err := r.Catalog("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
const listHelp = `List all repositories.`
|
||||
|
||||
fmt.Printf("Repositories for %s\n", auth.ServerAddress)
|
||||
func (cmd *listCommand) Name() string { return "ls" }
|
||||
func (cmd *listCommand) Args() string { return "[OPTIONS] REGISTRY_DOMAIN" }
|
||||
func (cmd *listCommand) ShortHelp() string { return listHelp }
|
||||
func (cmd *listCommand) LongHelp() string { return listHelp }
|
||||
func (cmd *listCommand) Hidden() bool { return false }
|
||||
|
||||
// Setup the tab writer.
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
func (cmd *listCommand) Register(fs *flag.FlagSet) {}
|
||||
|
||||
// Print header.
|
||||
fmt.Fprintln(w, "REPO\tTAGS")
|
||||
type listCommand struct{}
|
||||
|
||||
var (
|
||||
l sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
func (cmd *listCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the domain of the registry")
|
||||
}
|
||||
|
||||
wg.Add(len(repos))
|
||||
for _, repo := range repos {
|
||||
go func(repo string) {
|
||||
// Get the tags and print to stdout.
|
||||
tags, err := r.Tags(repo)
|
||||
if err != nil {
|
||||
fmt.Printf("Get tags of [%s] error: %s", repo, err)
|
||||
}
|
||||
out := fmt.Sprintf("%s\t%s\n", repo, strings.Join(tags, ", "))
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Get the repositories via catalog.
|
||||
repos, err := r.Catalog("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(repos)
|
||||
|
||||
// Lock around the tabwriter to prevent garbled output.
|
||||
// See: https://github.com/genuinetools/reg/issues/54
|
||||
l.Lock()
|
||||
w.Write([]byte(out))
|
||||
l.Unlock()
|
||||
fmt.Printf("Repositories for %s\n", r.Domain)
|
||||
|
||||
wg.Done()
|
||||
}(repo)
|
||||
}
|
||||
wg.Wait()
|
||||
var (
|
||||
l sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
repoTags = map[string][]string{}
|
||||
)
|
||||
|
||||
w.Flush()
|
||||
wg.Add(len(repos))
|
||||
for _, repo := range repos {
|
||||
go func(repo string) {
|
||||
// Get the tags.
|
||||
tags, err := r.Tags(repo)
|
||||
if err != nil {
|
||||
fmt.Printf("Get tags of [%s] error: %s", repo, err)
|
||||
}
|
||||
// Sort the tags
|
||||
sort.Strings(tags)
|
||||
|
||||
return nil
|
||||
},
|
||||
// Lock on the write to the map.
|
||||
l.Lock()
|
||||
repoTags[repo] = tags
|
||||
l.Unlock()
|
||||
|
||||
wg.Done()
|
||||
}(repo)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Setup the tab writer.
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
|
||||
// Print header.
|
||||
fmt.Fprintln(w, "REPO\tTAGS")
|
||||
|
||||
// Sort the repos.
|
||||
for _, repo := range repos {
|
||||
w.Write([]byte(fmt.Sprintf("%s\t%s\n", repo, strings.Join(repoTags[repo], ", "))))
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
15
vendor/github.com/genuinetools/reg/list_test.go
generated
vendored
15
vendor/github.com/genuinetools/reg/list_test.go
generated
vendored
|
@ -6,14 +6,15 @@ import (
|
|||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
out, err := run("ls")
|
||||
out, err := run("ls", domain)
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(out), err)
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
expected := []string{"alpine latest", "busybox glibc, musl"}
|
||||
for _, e := range expected {
|
||||
if !strings.Contains(out, e) {
|
||||
t.Logf("expected to contain: %s\ngot: %s", e, out)
|
||||
}
|
||||
|
||||
expected := `REPO TAGS
|
||||
alpine 3.5, latest
|
||||
busybox glibc, latest, musl`
|
||||
if !strings.HasSuffix(strings.TrimSpace(out), expected) {
|
||||
t.Fatalf("expected to contain: %s\ngot: %s", expected, out)
|
||||
}
|
||||
}
|
||||
|
|
175
vendor/github.com/genuinetools/reg/main.go
generated
vendored
175
vendor/github.com/genuinetools/reg/main.go
generated
vendored
|
@ -1,109 +1,120 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/genuinetools/pkg/cli"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/genuinetools/reg/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
auth types.AuthConfig
|
||||
r *registry.Registry
|
||||
insecure bool
|
||||
forceNonSSL bool
|
||||
skipPing bool
|
||||
|
||||
timeout time.Duration
|
||||
|
||||
username string
|
||||
password string
|
||||
|
||||
debug bool
|
||||
)
|
||||
|
||||
//go:generate go run internal/binutils/generate.go
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "reg"
|
||||
app.Version = fmt.Sprintf("version %s, build %s", version.VERSION, version.GITCOMMIT)
|
||||
app.Author = "The Genuinetools Authors"
|
||||
app.Email = "no-reply@butts.com"
|
||||
app.Usage = "Docker registry v2 client."
|
||||
// Create a new cli program.
|
||||
p := cli.NewProgram()
|
||||
p.Name = "reg"
|
||||
p.Description = "Docker registry v2 client"
|
||||
// Set the GitCommit and Version.
|
||||
p.GitCommit = version.GITCOMMIT
|
||||
p.Version = version.VERSION
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug, d",
|
||||
Usage: "run in debug mode",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "insecure, k",
|
||||
Usage: "do not verify tls certificates",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "force-non-ssl, f",
|
||||
Usage: "force allow use of non-ssl",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username, u",
|
||||
Usage: "username for the registry",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "password, p",
|
||||
Usage: "password for the registry",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "registry, r",
|
||||
Usage: "URL to the private registry (ex. r.j3ss.co)",
|
||||
Value: repoutils.DefaultDockerRegistry,
|
||||
EnvVar: "REG_REGISTRY",
|
||||
},
|
||||
// Build the list of available commands.
|
||||
p.Commands = []cli.Command{
|
||||
&digestCommand{},
|
||||
&layerCommand{},
|
||||
&listCommand{},
|
||||
&manifestCommand{},
|
||||
&removeCommand{},
|
||||
&serverCommand{},
|
||||
&tagsCommand{},
|
||||
&vulnsCommand{},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
deleteCommand,
|
||||
layerCommand,
|
||||
listCommand,
|
||||
manifestCommand,
|
||||
tagsCommand,
|
||||
vulnsCommand,
|
||||
}
|
||||
// Setup the global flags.
|
||||
p.FlagSet = flag.NewFlagSet("global", flag.ExitOnError)
|
||||
p.FlagSet.BoolVar(&insecure, "insecure", false, "do not verify tls certificates")
|
||||
p.FlagSet.BoolVar(&insecure, "k", false, "do not verify tls certificates")
|
||||
|
||||
app.Before = func(c *cli.Context) (err error) {
|
||||
// Preload initializes any global options and configuration
|
||||
// before the main or sub commands are run.
|
||||
if c.GlobalBool("debug") {
|
||||
p.FlagSet.BoolVar(&forceNonSSL, "force-non-ssl", false, "force allow use of non-ssl")
|
||||
p.FlagSet.BoolVar(&forceNonSSL, "f", false, "force allow use of non-ssl")
|
||||
|
||||
p.FlagSet.BoolVar(&skipPing, "skip-ping", false, "skip pinging the registry while establishing connection")
|
||||
|
||||
p.FlagSet.DurationVar(&timeout, "timeout", time.Minute, "timeout for HTTP requests")
|
||||
|
||||
p.FlagSet.StringVar(&username, "username", "", "username for the registry")
|
||||
p.FlagSet.StringVar(&username, "u", "", "username for the registry")
|
||||
|
||||
p.FlagSet.StringVar(&password, "password", "", "password for the registry")
|
||||
p.FlagSet.StringVar(&password, "p", "", "password for the registry")
|
||||
|
||||
p.FlagSet.BoolVar(&debug, "d", false, "enable debug logging")
|
||||
|
||||
// Set the before function.
|
||||
p.Before = func(ctx context.Context) error {
|
||||
// On ^C, or SIGTERM handle exit.
|
||||
signals := make(chan os.Signal, 0)
|
||||
signal.Notify(signals, os.Interrupt)
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
_, cancel := context.WithCancel(ctx)
|
||||
go func() {
|
||||
for sig := range signals {
|
||||
cancel()
|
||||
logrus.Infof("Received %s, exiting.", sig.String())
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
|
||||
// Set the log level.
|
||||
if debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
if len(c.Args()) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Args()[0] == "help" {
|
||||
return
|
||||
}
|
||||
|
||||
auth, err = repoutils.GetAuthConfig(c.GlobalString("username"), c.GlobalString("password"), c.GlobalString("registry"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prevent non-ssl unless explicitly forced
|
||||
if !c.GlobalBool("force-non-ssl") && strings.HasPrefix(auth.ServerAddress, "http:") {
|
||||
return fmt.Errorf("Attempt to use insecure protocol! Use non-ssl option to force")
|
||||
}
|
||||
|
||||
// create the registry client
|
||||
if c.GlobalBool("insecure") {
|
||||
r, err = registry.NewInsecure(auth, c.GlobalBool("debug"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
r, err = registry.New(auth, c.GlobalBool("debug"))
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
// Run our program.
|
||||
p.Run()
|
||||
}
|
||||
|
||||
func createRegistryClient(domain string) (*registry.Registry, error) {
|
||||
auth, err := repoutils.GetAuthConfig(username, password, domain)
|
||||
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")
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
return registry.New(auth, registry.Opt{
|
||||
Insecure: insecure,
|
||||
Debug: debug,
|
||||
SkipPing: skipPing,
|
||||
Timeout: timeout,
|
||||
})
|
||||
}
|
||||
|
|
30
vendor/github.com/genuinetools/reg/main_test.go
generated
vendored
30
vendor/github.com/genuinetools/reg/main_test.go
generated
vendored
|
@ -13,6 +13,10 @@ import (
|
|||
"github.com/genuinetools/reg/testutils"
|
||||
)
|
||||
|
||||
const (
|
||||
domain = "localhost:5000"
|
||||
)
|
||||
|
||||
var (
|
||||
exeSuffix string // ".exe" on Windows
|
||||
|
||||
|
@ -32,6 +36,7 @@ var (
|
|||
password: "testing",
|
||||
},
|
||||
}
|
||||
registryHelper *testutils.RegistryHelper
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -62,14 +67,26 @@ func TestMain(m *testing.M) {
|
|||
panic(fmt.Errorf("could not connect to docker: %v", err))
|
||||
}
|
||||
|
||||
// start the clair containers.
|
||||
dbID, clairID, err := testutils.StartClair(dcli)
|
||||
if err != nil {
|
||||
testutils.RemoveContainer(dcli, dbID, clairID)
|
||||
panic(fmt.Errorf("starting clair containers failed: %v", err))
|
||||
}
|
||||
|
||||
for _, regConfig := range registryConfigs {
|
||||
// start each registry
|
||||
regID, _, err := testutils.StartRegistry(dcli, regConfig.config, regConfig.username, regConfig.password)
|
||||
if err != nil {
|
||||
testutils.RemoveContainer(dcli, regID)
|
||||
testutils.RemoveContainer(dcli, dbID, clairID, regID)
|
||||
panic(fmt.Errorf("starting registry container %s failed: %v", regConfig.config, err))
|
||||
}
|
||||
|
||||
registryHelper, err = testutils.NewRegistryHelper(dcli, regConfig.username, regConfig.password, domain)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("creating registry helper %s failed: %v", regConfig.config, err))
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
merr := m.Run()
|
||||
|
||||
|
@ -79,18 +96,27 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
|
||||
if merr != 0 {
|
||||
testutils.RemoveContainer(dcli, dbID, clairID)
|
||||
fmt.Printf("testing config %s failed\n", regConfig.config)
|
||||
os.Exit(merr)
|
||||
}
|
||||
}
|
||||
|
||||
// remove clair containers.
|
||||
if err := testutils.RemoveContainer(dcli, dbID, clairID); err != nil {
|
||||
log.Printf("couldn't remove clair containers: %v", err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func run(args ...string) (string, error) {
|
||||
prog := "./testreg" + exeSuffix
|
||||
// always add trust insecure, and the registry
|
||||
newargs := append([]string{"-d", "-k", "-r", "localhost:5000"}, args...)
|
||||
newargs := []string{args[0], "-d", "-k"}
|
||||
if len(args) > 1 {
|
||||
newargs = append(newargs, args[1:]...)
|
||||
}
|
||||
cmd := exec.Command(prog, newargs...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
return string(out), err
|
||||
|
|
98
vendor/github.com/genuinetools/reg/manifest.go
generated
vendored
98
vendor/github.com/genuinetools/reg/manifest.go
generated
vendored
|
@ -1,54 +1,66 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
var manifestCommand = cli.Command{
|
||||
Name: "manifest",
|
||||
Usage: "get the json manifest for the specific reference of a repository",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "v1",
|
||||
Usage: "force the version of the manifest retreived to v1",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if len(c.Args()) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
const manifestHelp = `Get the json manifest for a repository.`
|
||||
|
||||
repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (cmd *manifestCommand) Name() string { return "manifest" }
|
||||
func (cmd *manifestCommand) Args() string { return "[OPTIONS] NAME[:TAG|@DIGEST]" }
|
||||
func (cmd *manifestCommand) ShortHelp() string { return manifestHelp }
|
||||
func (cmd *manifestCommand) LongHelp() string { return manifestHelp }
|
||||
func (cmd *manifestCommand) Hidden() bool { return false }
|
||||
|
||||
var manifest interface{}
|
||||
if c.Bool("v1") {
|
||||
// Get the v1 manifest if it was explicitly asked for.
|
||||
manifest, err = r.ManifestV1(repo, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Get the v2 manifest.
|
||||
manifest, err = r.Manifest(repo, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(manifest, " ", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
|
||||
return nil
|
||||
},
|
||||
func (cmd *manifestCommand) Register(fs *flag.FlagSet) {
|
||||
fs.BoolVar(&cmd.v1, "v1", false, "force the version of the manifest retrieved to v1")
|
||||
}
|
||||
|
||||
type manifestCommand struct {
|
||||
v1 bool
|
||||
}
|
||||
|
||||
func (cmd *manifestCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(image.Domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifest interface{}
|
||||
if cmd.v1 {
|
||||
// Get the v1 manifest if it was explicitly asked for.
|
||||
manifest, err = r.ManifestV1(image.Path, image.Reference())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Get the v2 manifest.
|
||||
manifest, err = r.Manifest(image.Path, image.Reference())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(manifest, " ", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
|
31
vendor/github.com/genuinetools/reg/manifest_test.go
generated
vendored
Normal file
31
vendor/github.com/genuinetools/reg/manifest_test.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestManifestV2(t *testing.T) {
|
||||
out, err := run("manifest", fmt.Sprintf("%s/busybox", domain))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestV1(t *testing.T) {
|
||||
out, err := run("manifest", "--v1", fmt.Sprintf("%s/busybox", domain))
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
|
||||
expected := `"schemaVersion": 1,`
|
||||
if !strings.Contains(out, expected) {
|
||||
t.Fatalf("expected: %s\ngot: %s", expected, out)
|
||||
}
|
||||
}
|
8
vendor/github.com/genuinetools/reg/registry/authchallenge.go
generated
vendored
8
vendor/github.com/genuinetools/reg/registry/authchallenge.go
generated
vendored
|
@ -1,6 +1,7 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -12,6 +13,9 @@ var (
|
|||
bearerRegex = regexp.MustCompile(
|
||||
`^\s*Bearer\s+(.*)$`)
|
||||
basicRegex = regexp.MustCompile(`^\s*Basic\s+.*$`)
|
||||
|
||||
// ErrBasicAuth indicates that the repository requires basic rather than token authentication.
|
||||
ErrBasicAuth = errors.New("basic auth required")
|
||||
)
|
||||
|
||||
func parseAuthHeader(header http.Header) (*authService, error) {
|
||||
|
@ -25,14 +29,14 @@ func parseAuthHeader(header http.Header) (*authService, error) {
|
|||
|
||||
func parseChallenge(challengeHeader string) (*authService, error) {
|
||||
if basicRegex.MatchString(challengeHeader) {
|
||||
return nil, nil
|
||||
return nil, ErrBasicAuth
|
||||
}
|
||||
|
||||
match := bearerRegex.FindAllStringSubmatch(challengeHeader, -1)
|
||||
if d := len(match); d != 1 {
|
||||
return nil, fmt.Errorf("malformed auth challenge header: '%s', %d", challengeHeader, d)
|
||||
}
|
||||
parts := strings.Split(strings.TrimSpace(match[0][1]), ",")
|
||||
parts := strings.SplitN(strings.TrimSpace(match[0][1]), ",", 3)
|
||||
|
||||
var realm, service string
|
||||
var scope []string
|
||||
|
|
52
vendor/github.com/genuinetools/reg/registry/authchallenge_test.go
generated
vendored
52
vendor/github.com/genuinetools/reg/registry/authchallenge_test.go
generated
vendored
|
@ -1,7 +1,6 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -22,13 +21,13 @@ func (asm authServiceMock) equalTo(v *authService) bool {
|
|||
if asm.service != v.Service {
|
||||
return false
|
||||
}
|
||||
if reflect.DeepEqual(asm.scope, v.Scope) {
|
||||
return false
|
||||
for i, v := range v.Scope {
|
||||
if v != asm.scope[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if asm.realm != v.Realm.String() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
return asm.realm == v.Realm.String()
|
||||
}
|
||||
|
||||
func TestParseChallenge(t *testing.T) {
|
||||
|
@ -40,8 +39,47 @@ func TestParseChallenge(t *testing.T) {
|
|||
realm: "https://foobar.com/api/v1/token",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: `Bearer realm="https://r.j3ss.co/auth",service="Docker registry",scope="repository:chrome:pull"`,
|
||||
value: authServiceMock{
|
||||
service: "Docker registry",
|
||||
realm: "https://r.j3ss.co/auth",
|
||||
scope: []string{"repository:chrome:pull"},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: `Basic realm="https://r.j3ss.co/auth",service="Docker registry"`,
|
||||
errorString: "basic auth required",
|
||||
},
|
||||
{
|
||||
header: `Basic realm="Registry Realm",service="Docker registry"`,
|
||||
errorString: "basic auth required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range challengeHeaderCases {
|
||||
val, err := parseChallenge(tc.header)
|
||||
if err != nil && !strings.Contains(err.Error(), tc.errorString) {
|
||||
t.Fatalf("expected error to contain %v, got %s", tc.errorString, err)
|
||||
}
|
||||
if err == nil && !tc.value.equalTo(val) {
|
||||
t.Fatalf("got %v, expected %v", val, tc.value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChallengePush(t *testing.T) {
|
||||
challengeHeaderCases := []challengeTestCase{
|
||||
{
|
||||
header: `Bearer realm="https://foo.com/v2/token",service="foo.com",scope="repository:pdr/tls:pull,push"`,
|
||||
value: authServiceMock{
|
||||
realm: "https://foo.com/v2/token",
|
||||
service: "foo.com",
|
||||
scope: []string{"repository:pdr/tls:pull,push"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range challengeHeaderCases {
|
||||
val, err := parseChallenge(tc.header)
|
||||
if err != nil && !strings.Contains(err.Error(), tc.errorString) {
|
||||
|
|
2
vendor/github.com/genuinetools/reg/registry/basictransport.go
generated
vendored
2
vendor/github.com/genuinetools/reg/registry/basictransport.go
generated
vendored
|
@ -15,7 +15,7 @@ type BasicTransport struct {
|
|||
|
||||
// RoundTrip defines the round tripper for basic auth transport.
|
||||
func (t *BasicTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if strings.HasPrefix(req.URL.String(), t.URL) {
|
||||
if strings.HasPrefix(req.URL.String(), t.URL) && req.Header.Get("Authorization") == "" {
|
||||
if t.Username != "" || t.Password != "" {
|
||||
req.SetBasicAuth(t.Username, t.Password)
|
||||
}
|
||||
|
|
24
vendor/github.com/genuinetools/reg/registry/customtransport.go
generated
vendored
Normal file
24
vendor/github.com/genuinetools/reg/registry/customtransport.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CustomTransport defines the data structure for custom http.Request options.
|
||||
type CustomTransport struct {
|
||||
Transport http.RoundTripper
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for the error transport.
|
||||
func (t *CustomTransport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
if len(t.Headers) != 0 {
|
||||
for header, value := range t.Headers {
|
||||
request.Header.Add(header, value)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := t.Transport.RoundTrip(request)
|
||||
|
||||
return resp, err
|
||||
}
|
20
vendor/github.com/genuinetools/reg/registry/delete.go
generated
vendored
20
vendor/github.com/genuinetools/reg/registry/delete.go
generated
vendored
|
@ -5,24 +5,12 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
ocd "github.com/opencontainers/go-digest"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Delete removes a repository digest or reference from the registry.
|
||||
// Delete removes a repository digest from the registry.
|
||||
// https://docs.docker.com/registry/spec/api/#deleting-an-image
|
||||
func (r *Registry) Delete(repository, digest string) error {
|
||||
// If digest is not valid try resolving it as a reference
|
||||
if _, err := ocd.Parse(digest); err != nil {
|
||||
digest, err = r.Digest(repository, digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if digest == "" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the image.
|
||||
func (r *Registry) Delete(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)
|
||||
|
@ -32,7 +20,7 @@ func (r *Registry) Delete(repository, digest string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", fmt.Sprintf("%s;q=0.9", schema2.MediaTypeManifest))
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
19
vendor/github.com/genuinetools/reg/registry/digest.go
generated
vendored
19
vendor/github.com/genuinetools/reg/registry/digest.go
generated
vendored
|
@ -5,20 +5,26 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Digest returns the digest for a repository and reference.
|
||||
func (r *Registry) Digest(repository, ref string) (string, error) {
|
||||
url := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
// Digest returns the digest for an image.
|
||||
func (r *Registry) Digest(image Image) (digest.Digest, error) {
|
||||
if len(image.Digest) > 1 {
|
||||
// return early if we already have an image digest.
|
||||
return image.Digest, nil
|
||||
}
|
||||
|
||||
url := r.url("/v2/%s/manifests/%s", image.Path, image.Tag)
|
||||
r.Logf("registry.manifests.get url=%s repository=%s ref=%s",
|
||||
url, repository, ref)
|
||||
url, image.Path, image.Tag)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -29,6 +35,5 @@ func (r *Registry) Digest(repository, ref string) (string, error) {
|
|||
return "", fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
digest := resp.Header.Get("Docker-Content-Digest")
|
||||
return digest, nil
|
||||
return digest.Parse(resp.Header.Get("Docker-Content-Digest"))
|
||||
}
|
||||
|
|
49
vendor/github.com/genuinetools/reg/registry/digest_test.go
generated
vendored
Normal file
49
vendor/github.com/genuinetools/reg/registry/digest_test.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
)
|
||||
|
||||
func TestDigestFromDockerHub(t *testing.T) {
|
||||
auth, err := repoutils.GetAuthConfig("", "", "docker.io")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get auth config: %s", err)
|
||||
}
|
||||
|
||||
r, err := New(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"})
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get digest: %s", err)
|
||||
}
|
||||
|
||||
if d == "" {
|
||||
t.Error("Empty digest received")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDigestFromGCR(t *testing.T) {
|
||||
auth, err := repoutils.GetAuthConfig("", "", "gcr.io")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get auth config: %s", err)
|
||||
}
|
||||
|
||||
r, err := New(auth, Opt{})
|
||||
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"})
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get digest: %s", err)
|
||||
}
|
||||
|
||||
if d == "" {
|
||||
t.Error("Empty digest received")
|
||||
}
|
||||
}
|
67
vendor/github.com/genuinetools/reg/registry/image.go
generated
vendored
Normal file
67
vendor/github.com/genuinetools/reg/registry/image.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Image holds information about an image.
|
||||
type Image struct {
|
||||
Domain string
|
||||
Path string
|
||||
Tag string
|
||||
Digest digest.Digest
|
||||
named reference.Named
|
||||
}
|
||||
|
||||
// String returns the string representation of an image.
|
||||
func (i Image) String() string {
|
||||
return i.named.String()
|
||||
}
|
||||
|
||||
// Reference returns either the digest if it is non-empty or the tag for the image.
|
||||
func (i Image) Reference() string {
|
||||
if len(i.Digest.String()) > 1 {
|
||||
return i.Digest.String()
|
||||
}
|
||||
|
||||
return i.Tag
|
||||
}
|
||||
|
||||
// WithDigest sets the digest for an image.
|
||||
func (i *Image) WithDigest(digest digest.Digest) (err error) {
|
||||
i.Digest = digest
|
||||
i.named, err = reference.WithDigest(i.named, digest)
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseImage returns an Image struct with all the values filled in for a given image.
|
||||
func ParseImage(image string) (Image, error) {
|
||||
// Parse the image name and tag.
|
||||
named, err := reference.ParseNormalizedNamed(image)
|
||||
if err != nil {
|
||||
return Image{}, fmt.Errorf("parsing image %q failed: %v", image, err)
|
||||
}
|
||||
// Add the latest lag if they did not provide one.
|
||||
named = reference.TagNameOnly(named)
|
||||
|
||||
i := Image{
|
||||
named: named,
|
||||
Domain: reference.Domain(named),
|
||||
Path: reference.Path(named),
|
||||
}
|
||||
|
||||
// Add the tag if there was one.
|
||||
if tagged, ok := named.(reference.Tagged); ok {
|
||||
i.Tag = tagged.Tag()
|
||||
}
|
||||
|
||||
// Add the digest if there was one.
|
||||
if canonical, ok := named.(reference.Canonical); ok {
|
||||
i.Digest = canonical.Digest()
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
13
vendor/github.com/genuinetools/reg/registry/layer.go
generated
vendored
13
vendor/github.com/genuinetools/reg/registry/layer.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"fmt"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
@ -24,7 +25,7 @@ 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, err := r.initiateUpload(repository)
|
||||
uploadURL, token, err := r.initiateUpload(repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -39,6 +40,7 @@ func (r *Registry) UploadLayer(repository string, digest reference.Reference, co
|
|||
return err
|
||||
}
|
||||
upload.Header.Set("Content-Type", "application/octet-stream")
|
||||
upload.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
_, err = r.Client.Do(upload)
|
||||
return err
|
||||
|
@ -70,20 +72,21 @@ func (r *Registry) HasLayer(repository string, digest digest.Digest) (bool, erro
|
|||
return false, err
|
||||
}
|
||||
|
||||
func (r *Registry) initiateUpload(repository string) (*url.URL, error) {
|
||||
func (r *Registry) initiateUpload(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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
token := resp.Header.Get("Request-Token")
|
||||
defer resp.Body.Close()
|
||||
|
||||
location := resp.Header.Get("Location")
|
||||
locationURL, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, token, err
|
||||
}
|
||||
return locationURL, nil
|
||||
return locationURL, token, nil
|
||||
}
|
||||
|
|
29
vendor/github.com/genuinetools/reg/registry/manifest.go
generated
vendored
29
vendor/github.com/genuinetools/reg/registry/manifest.go
generated
vendored
|
@ -1,6 +1,9 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
|
@ -20,8 +23,7 @@ func (r *Registry) Manifest(repository, ref string) (distribution.Manifest, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
||||
req.Header.Add("Accept", fmt.Sprintf("%s;q=0.9", schema2.MediaTypeManifest))
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
|
@ -84,3 +86,26 @@ func (r *Registry) ManifestV1(repository, ref string) (schema1.SignedManifest, e
|
|||
|
||||
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 {
|
||||
url := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifest.put url=%s repository=%s reference=%s", url, repository, ref)
|
||||
|
||||
b, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("PUT", url, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", schema2.MediaTypeManifest)
|
||||
resp, err := r.Client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
52
vendor/github.com/genuinetools/reg/registry/registry.go
generated
vendored
52
vendor/github.com/genuinetools/reg/registry/registry.go
generated
vendored
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
|
@ -22,6 +23,7 @@ type Registry struct {
|
|||
Password string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
Opt Opt
|
||||
}
|
||||
|
||||
var reProtocol = regexp.MustCompile("^https?://")
|
||||
|
@ -37,26 +39,31 @@ func Log(format string, args ...interface{}) {
|
|||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Opt holds the options for a new registry.
|
||||
type Opt struct {
|
||||
Insecure bool
|
||||
Debug bool
|
||||
SkipPing 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, debug bool) (*Registry, error) {
|
||||
func New(auth types.AuthConfig, opt Opt) (*Registry, error) {
|
||||
transport := http.DefaultTransport
|
||||
|
||||
return newFromTransport(auth, transport, debug)
|
||||
}
|
||||
|
||||
// NewInsecure creates a new Registry struct with the given URL and credentials,
|
||||
// using a http.Transport that will not verify an SSL certificate.
|
||||
func NewInsecure(auth types.AuthConfig, debug bool) (*Registry, error) {
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
if opt.Insecure {
|
||||
transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return newFromTransport(auth, transport, debug)
|
||||
return newFromTransport(auth, transport, opt)
|
||||
}
|
||||
|
||||
func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, debug bool) (*Registry, error) {
|
||||
func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, opt Opt) (*Registry, error) {
|
||||
url := strings.TrimSuffix(auth.ServerAddress, "/")
|
||||
|
||||
if !reProtocol.MatchString(url) {
|
||||
|
@ -77,10 +84,14 @@ func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, debug
|
|||
errorTransport := &ErrorTransport{
|
||||
Transport: basicAuthTransport,
|
||||
}
|
||||
customTransport := &CustomTransport{
|
||||
Transport: errorTransport,
|
||||
Headers: opt.Headers,
|
||||
}
|
||||
|
||||
// set the logging
|
||||
logf := Quiet
|
||||
if debug {
|
||||
if opt.Debug {
|
||||
logf = Log
|
||||
}
|
||||
|
||||
|
@ -88,15 +99,19 @@ func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, debug
|
|||
URL: url,
|
||||
Domain: reProtocol.ReplaceAllString(url, ""),
|
||||
Client: &http.Client{
|
||||
Transport: errorTransport,
|
||||
Timeout: opt.Timeout,
|
||||
Transport: customTransport,
|
||||
},
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
Logf: logf,
|
||||
Opt: opt,
|
||||
}
|
||||
|
||||
if err := registry.Ping(); err != nil {
|
||||
return nil, err
|
||||
if !opt.SkipPing {
|
||||
if err := registry.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return registry, nil
|
||||
|
@ -115,8 +130,7 @@ func (r *Registry) getJSON(url string, response interface{}, addV2Header bool) (
|
|||
return nil, err
|
||||
}
|
||||
if addV2Header {
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
||||
req.Header.Add("Accept", fmt.Sprintf("%s,%s;q=0.9", schema2.MediaTypeManifest, manifestlist.MediaTypeManifestList))
|
||||
}
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
|
|
50
vendor/github.com/genuinetools/reg/registry/tokentransport.go
generated
vendored
50
vendor/github.com/genuinetools/reg/registry/tokentransport.go
generated
vendored
|
@ -1,6 +1,8 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -44,7 +46,11 @@ func (t *TokenTransport) authAndRetry(authService *authService, req *http.Reques
|
|||
return authResp, err
|
||||
}
|
||||
|
||||
return t.retry(req, token)
|
||||
response, err := t.retry(req, token)
|
||||
if response != nil {
|
||||
response.Header.Set("request-token", token)
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (t *TokenTransport) auth(authService *authService) (string, *http.Response, error) {
|
||||
|
@ -114,7 +120,8 @@ func isTokenDemand(resp *http.Response) (*authService, error) {
|
|||
return parseAuthHeader(resp.Header)
|
||||
}
|
||||
|
||||
// Token returns the required token for the specific resource url.
|
||||
// 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) {
|
||||
r.Logf("registry.token url=%s", url)
|
||||
|
||||
|
@ -123,7 +130,19 @@ func (r *Registry) Token(url string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
client := http.DefaultClient
|
||||
if r.Opt.Insecure {
|
||||
client = &http.Client{
|
||||
Timeout: r.Opt.Timeout,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -143,7 +162,7 @@ func (r *Registry) Token(url string) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp, err = r.Client.Do(authReq)
|
||||
resp, err = http.DefaultClient.Do(authReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -164,3 +183,26 @@ func (r *Registry) Token(url string) (string, error) {
|
|||
|
||||
return authToken.Token, nil
|
||||
}
|
||||
|
||||
// Headers returns the authorization headers for a specific uri.
|
||||
func (r *Registry) Headers(uri string) (map[string]string, error) {
|
||||
// Get the token.
|
||||
token, err := r.Token(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.
|
||||
return map[string]string{
|
||||
"Authorization": fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(r.Username+":"+r.Password))),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(token) < 1 {
|
||||
r.Logf("got empty token for %s", uri)
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"Authorization": fmt.Sprintf("Bearer %s", token),
|
||||
}, nil
|
||||
}
|
||||
|
|
38
vendor/github.com/genuinetools/reg/registry/tokentransport_test.go
generated
vendored
Normal file
38
vendor/github.com/genuinetools/reg/registry/tokentransport_test.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
func TestErrBasicAuth(t *testing.T) {
|
||||
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"`)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
authConfig := types.AuthConfig{
|
||||
Username: "j3ss",
|
||||
Password: "ss3j",
|
||||
ServerAddress: ts.URL,
|
||||
}
|
||||
r, err := New(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)
|
||||
if err != ErrBasicAuth {
|
||||
t.Fatalf("expected ErrBasicAuth getting token, got %v", err)
|
||||
}
|
||||
if token != "" {
|
||||
t.Fatalf("expected empty token, got %v", err)
|
||||
}
|
||||
}
|
56
vendor/github.com/genuinetools/reg/remove.go
generated
vendored
Normal file
56
vendor/github.com/genuinetools/reg/remove.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
const removeHelp = `Delete a specific reference of a repository.`
|
||||
|
||||
func (cmd *removeCommand) Name() string { return "rm" }
|
||||
func (cmd *removeCommand) Args() string { return "[OPTIONS] NAME[:TAG|@DIGEST]" }
|
||||
func (cmd *removeCommand) ShortHelp() string { return removeHelp }
|
||||
func (cmd *removeCommand) LongHelp() string { return removeHelp }
|
||||
func (cmd *removeCommand) Hidden() bool { return false }
|
||||
|
||||
func (cmd *removeCommand) Register(fs *flag.FlagSet) {}
|
||||
|
||||
type removeCommand struct{}
|
||||
|
||||
func (cmd *removeCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(image.Domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the digest.
|
||||
digest, err := r.Digest(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := image.WithDigest(digest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the reference.
|
||||
if err := r.Delete(image.Path, digest); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleted %s\n", image.String())
|
||||
|
||||
return nil
|
||||
}
|
46
vendor/github.com/genuinetools/reg/remove_test.go
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/remove_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func teardownTest(t *testing.T) {
|
||||
if err := registryHelper.RefillRegistry("busybox:glibc"); err != nil {
|
||||
t.Fatalf("adding image after remove failed: +%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
defer teardownTest(t)
|
||||
// Make sure we have busybox in list.
|
||||
out, err := run("ls", domain)
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
expected := `REPO TAGS
|
||||
alpine 3.5, latest
|
||||
busybox glibc, latest, musl`
|
||||
if !strings.HasSuffix(strings.TrimSpace(out), expected) {
|
||||
t.Fatalf("expected to contain: %s\ngot: %s", expected, out)
|
||||
}
|
||||
|
||||
// Remove busybox image.
|
||||
if out, err := run("rm", fmt.Sprintf("%s/busybox:glibc", domain)); err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
|
||||
// Make sure we have removed busybox:glibc.
|
||||
out, err = run("ls", domain)
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
expected = `REPO TAGS
|
||||
alpine 3.5, latest
|
||||
busybox latest, musl`
|
||||
if !strings.HasSuffix(strings.TrimSpace(out), expected) {
|
||||
t.Fatalf("expected to contain: %s\ngot: %s", expected, out)
|
||||
}
|
||||
|
||||
}
|
3
vendor/github.com/genuinetools/reg/repoutils/repoutils.go
generated
vendored
3
vendor/github.com/genuinetools/reg/repoutils/repoutils.go
generated
vendored
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker-ce/components/cli/cli/config"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -80,7 +81,7 @@ func GetAuthConfig(username, password, registry string) (types.AuthConfig, error
|
|||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Using registry %q with no authentication\n", registry)
|
||||
logrus.Debugf("Using registry %q with no authentication", registry)
|
||||
|
||||
// Otherwise just use the registry with no auth.
|
||||
return setDefaultRegistry(types.AuthConfig{
|
||||
|
|
185
vendor/github.com/genuinetools/reg/server.go
generated
vendored
Normal file
185
vendor/github.com/genuinetools/reg/server.go
generated
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/internal/binutils/static"
|
||||
"github.com/genuinetools/reg/internal/binutils/templates"
|
||||
"github.com/gorilla/mux"
|
||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||
"github.com/shurcooL/httpfs/html/vfstemplate"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const serverHelp = `Run a static UI server for a registry.`
|
||||
|
||||
func (cmd *serverCommand) Name() string { return "server" }
|
||||
func (cmd *serverCommand) Args() string { return "[OPTIONS]" }
|
||||
func (cmd *serverCommand) ShortHelp() string { return serverHelp }
|
||||
func (cmd *serverCommand) LongHelp() string { return serverHelp }
|
||||
func (cmd *serverCommand) Hidden() bool { return false }
|
||||
|
||||
func (cmd *serverCommand) Register(fs *flag.FlagSet) {
|
||||
fs.DurationVar(&cmd.interval, "interval", time.Hour, "interval to generate new index.html's at")
|
||||
|
||||
fs.StringVar(&cmd.registryServer, "registry", "", "URL to the private registry (ex. r.j3ss.co)")
|
||||
fs.StringVar(&cmd.registryServer, "r", "", "URL to the private registry (ex. r.j3ss.co)")
|
||||
|
||||
fs.StringVar(&cmd.clairServer, "clair", "", "url to clair instance")
|
||||
|
||||
fs.StringVar(&cmd.cert, "cert", "", "path to ssl cert")
|
||||
fs.StringVar(&cmd.key, "key", "", "path to ssl key")
|
||||
fs.StringVar(&cmd.listenAddress, "listen-address", "", "address to listen on")
|
||||
fs.StringVar(&cmd.port, "port", "8080", "port for server to run on")
|
||||
fs.StringVar(&cmd.assetPath, "asset-path", "", "Path to assets and templates")
|
||||
|
||||
fs.BoolVar(&cmd.generateAndExit, "once", false, "generate the templates once and then exit")
|
||||
}
|
||||
|
||||
type serverCommand struct {
|
||||
interval time.Duration
|
||||
registryServer string
|
||||
clairServer string
|
||||
|
||||
generateAndExit bool
|
||||
|
||||
cert string
|
||||
key string
|
||||
listenAddress string
|
||||
port string
|
||||
assetPath string
|
||||
}
|
||||
|
||||
func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(cmd.registryServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry controller for the handlers.
|
||||
rc := registryController{
|
||||
reg: r,
|
||||
generateOnly: cmd.generateAndExit,
|
||||
}
|
||||
|
||||
// Create a clair client if the user passed in a server address.
|
||||
if len(cmd.clairServer) > 0 {
|
||||
rc.cl, err = clair.New(cmd.clairServer, clair.Opt{
|
||||
Insecure: insecure,
|
||||
Debug: debug,
|
||||
Timeout: timeout,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("creation of clair client at %s failed: %v", cmd.clairServer, err)
|
||||
}
|
||||
} else {
|
||||
rc.cl = nil
|
||||
}
|
||||
// Get the path to the asset directory.
|
||||
assetDir := cmd.assetPath
|
||||
if len(cmd.assetPath) <= 0 {
|
||||
assetDir, err = os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
staticDir := filepath.Join(assetDir, "static")
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"trim": func(s string) string {
|
||||
return wordwrap.WrapString(s, 80)
|
||||
},
|
||||
"color": func(s string) string {
|
||||
switch s = strings.ToLower(s); s {
|
||||
case "high":
|
||||
return "danger"
|
||||
case "critical":
|
||||
return "danger"
|
||||
case "defcon1":
|
||||
return "danger"
|
||||
case "medium":
|
||||
return "warning"
|
||||
case "low":
|
||||
return "info"
|
||||
case "negligible":
|
||||
return "info"
|
||||
case "unknown":
|
||||
return "default"
|
||||
default:
|
||||
return "default"
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
rc.tmpl = template.New("").Funcs(funcMap)
|
||||
rc.tmpl = template.Must(vfstemplate.ParseGlob(templates.Assets, rc.tmpl, "*.html"))
|
||||
|
||||
// Create the initial index.
|
||||
logrus.Info("creating initial static index")
|
||||
if err := rc.repositories(staticDir); err != nil {
|
||||
return fmt.Errorf("creating index failed: %v", err)
|
||||
}
|
||||
|
||||
if cmd.generateAndExit {
|
||||
logrus.Info("output generated, exiting...")
|
||||
return nil
|
||||
}
|
||||
|
||||
rc.interval = cmd.interval
|
||||
ticker := time.NewTicker(rc.interval)
|
||||
go func() {
|
||||
// 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 {
|
||||
logrus.Warnf("creating static index failed: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Create mux server.
|
||||
mux := mux.NewRouter()
|
||||
mux.UseEncodedPath()
|
||||
|
||||
// Static files handler.
|
||||
mux.HandleFunc("/repo/{repo}/tags", rc.tagsHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tags/", rc.tagsHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/", rc.vulnerabilitiesHandler)
|
||||
|
||||
// Add the vulns endpoints if we have a client for a clair server.
|
||||
if rc.cl != nil {
|
||||
logrus.Infof("adding clair handlers...")
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns/", rc.vulnerabilitiesHandler)
|
||||
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns.json", rc.vulnerabilitiesHandler)
|
||||
}
|
||||
|
||||
// Serve the static assets.
|
||||
staticAssetsHandler := http.FileServer(static.Assets)
|
||||
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticAssetsHandler))
|
||||
staticHandler := http.FileServer(http.Dir(staticDir))
|
||||
mux.Handle("/", staticHandler)
|
||||
|
||||
// Set up the server.
|
||||
server := &http.Server{
|
||||
Addr: cmd.listenAddress + ":" + cmd.port,
|
||||
Handler: mux,
|
||||
}
|
||||
logrus.Infof("Starting server on port %q", cmd.port)
|
||||
if len(cmd.cert) > 0 && len(cmd.key) > 0 {
|
||||
return server.ListenAndServeTLS(cmd.cert, cmd.key)
|
||||
}
|
||||
return server.ListenAndServe()
|
||||
}
|
BIN
vendor/github.com/genuinetools/reg/server/home.png
generated
vendored
Normal file
BIN
vendor/github.com/genuinetools/reg/server/home.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
596
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.css
generated
vendored
Normal file
596
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.css
generated
vendored
Normal file
|
@ -0,0 +1,596 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2017 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Generated using the Bootstrap Customizer (https://getbootstrap.com/customize/?id=5343e9cbe6bb765ae40092b46ea944d5)
|
||||
* Config saved to config.json and https://gist.github.com/5343e9cbe6bb765ae40092b46ea944d5
|
||||
*/
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-primary.disabled,
|
||||
.btn-success.disabled,
|
||||
.btn-info.disabled,
|
||||
.btn-warning.disabled,
|
||||
.btn-danger.disabled,
|
||||
.btn-default[disabled],
|
||||
.btn-primary[disabled],
|
||||
.btn-success[disabled],
|
||||
.btn-info[disabled],
|
||||
.btn-warning[disabled],
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
fieldset[disabled] .btn-primary,
|
||||
fieldset[disabled] .btn-success,
|
||||
fieldset[disabled] .btn-info,
|
||||
fieldset[disabled] .btn-warning,
|
||||
fieldset[disabled] .btn-danger {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-default .badge,
|
||||
.btn-primary .badge,
|
||||
.btn-success .badge,
|
||||
.btn-info .badge,
|
||||
.btn-warning .badge,
|
||||
.btn-danger .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
|
||||
background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
|
||||
background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-default[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
.btn-default.disabled:hover,
|
||||
.btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default:hover,
|
||||
.btn-default.disabled:focus,
|
||||
.btn-default[disabled]:focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
.btn-default.disabled.focus,
|
||||
.btn-default[disabled].focus,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
.btn-default.disabled:active,
|
||||
.btn-default[disabled]:active,
|
||||
fieldset[disabled] .btn-default:active,
|
||||
.btn-default.disabled.active,
|
||||
.btn-default[disabled].active,
|
||||
fieldset[disabled] .btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #265a88;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #265a88;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled],
|
||||
fieldset[disabled] .btn-primary,
|
||||
.btn-primary.disabled:hover,
|
||||
.btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary:hover,
|
||||
.btn-primary.disabled:focus,
|
||||
.btn-primary[disabled]:focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
.btn-primary.disabled.focus,
|
||||
.btn-primary[disabled].focus,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
.btn-primary.disabled:active,
|
||||
.btn-primary[disabled]:active,
|
||||
fieldset[disabled] .btn-primary:active,
|
||||
.btn-primary.disabled.active,
|
||||
.btn-primary[disabled].active,
|
||||
fieldset[disabled] .btn-primary.active {
|
||||
background-color: #265a88;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success.disabled,
|
||||
.btn-success[disabled],
|
||||
fieldset[disabled] .btn-success,
|
||||
.btn-success.disabled:hover,
|
||||
.btn-success[disabled]:hover,
|
||||
fieldset[disabled] .btn-success:hover,
|
||||
.btn-success.disabled:focus,
|
||||
.btn-success[disabled]:focus,
|
||||
fieldset[disabled] .btn-success:focus,
|
||||
.btn-success.disabled.focus,
|
||||
.btn-success[disabled].focus,
|
||||
fieldset[disabled] .btn-success.focus,
|
||||
.btn-success.disabled:active,
|
||||
.btn-success[disabled]:active,
|
||||
fieldset[disabled] .btn-success:active,
|
||||
.btn-success.disabled.active,
|
||||
.btn-success[disabled].active,
|
||||
fieldset[disabled] .btn-success.active {
|
||||
background-color: #419641;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info.disabled,
|
||||
.btn-info[disabled],
|
||||
fieldset[disabled] .btn-info,
|
||||
.btn-info.disabled:hover,
|
||||
.btn-info[disabled]:hover,
|
||||
fieldset[disabled] .btn-info:hover,
|
||||
.btn-info.disabled:focus,
|
||||
.btn-info[disabled]:focus,
|
||||
fieldset[disabled] .btn-info:focus,
|
||||
.btn-info.disabled.focus,
|
||||
.btn-info[disabled].focus,
|
||||
fieldset[disabled] .btn-info.focus,
|
||||
.btn-info.disabled:active,
|
||||
.btn-info[disabled]:active,
|
||||
fieldset[disabled] .btn-info:active,
|
||||
.btn-info.disabled.active,
|
||||
.btn-info[disabled].active,
|
||||
fieldset[disabled] .btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning.disabled,
|
||||
.btn-warning[disabled],
|
||||
fieldset[disabled] .btn-warning,
|
||||
.btn-warning.disabled:hover,
|
||||
.btn-warning[disabled]:hover,
|
||||
fieldset[disabled] .btn-warning:hover,
|
||||
.btn-warning.disabled:focus,
|
||||
.btn-warning[disabled]:focus,
|
||||
fieldset[disabled] .btn-warning:focus,
|
||||
.btn-warning.disabled.focus,
|
||||
.btn-warning[disabled].focus,
|
||||
fieldset[disabled] .btn-warning.focus,
|
||||
.btn-warning.disabled:active,
|
||||
.btn-warning[disabled]:active,
|
||||
fieldset[disabled] .btn-warning:active,
|
||||
.btn-warning.disabled.active,
|
||||
.btn-warning[disabled].active,
|
||||
fieldset[disabled] .btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger.disabled,
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-danger,
|
||||
.btn-danger.disabled:hover,
|
||||
.btn-danger[disabled]:hover,
|
||||
fieldset[disabled] .btn-danger:hover,
|
||||
.btn-danger.disabled:focus,
|
||||
.btn-danger[disabled]:focus,
|
||||
fieldset[disabled] .btn-danger:focus,
|
||||
.btn-danger.disabled.focus,
|
||||
.btn-danger[disabled].focus,
|
||||
fieldset[disabled] .btn-danger.focus,
|
||||
.btn-danger.disabled:active,
|
||||
.btn-danger[disabled]:active,
|
||||
fieldset[disabled] .btn-danger:active,
|
||||
.btn-danger.disabled.active,
|
||||
.btn-danger[disabled].active,
|
||||
fieldset[disabled] .btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
background-image: none;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-color: #2e6da4;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
|
||||
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
|
||||
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .open > a,
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
|
||||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222222));
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .open > a,
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
}
|
||||
.progress-bar-striped {
|
||||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #286090;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.list-group-item.active .badge,
|
||||
.list-group-item.active:hover .badge,
|
||||
.list-group-item.active:focus .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.min.css
generated
vendored
Normal file
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap-theme.min.css
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
3853
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.css
generated
vendored
Normal file
3853
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.css
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.min.css
generated
vendored
Normal file
14
vendor/github.com/genuinetools/reg/server/static/css/bootstrap.min.css
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
16
vendor/github.com/genuinetools/reg/server/static/css/search.svg
generated
vendored
Normal file
16
vendor/github.com/genuinetools/reg/server/static/css/search.svg
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
|
||||
<g id="search_1_">
|
||||
<path fill="#CCCCCC" d="M20,0.005c-6.627,0-12,5.373-12,12c0,2.026,0.507,3.933,1.395,5.608l-8.344,8.342l0.007,0.006
|
||||
C0.406,26.602,0,27.49,0,28.477c0,1.949,1.58,3.529,3.529,3.529c0.985,0,1.874-0.406,2.515-1.059l-0.002-0.002l8.341-8.34
|
||||
c1.676,0.891,3.586,1.4,5.617,1.4c6.627,0,12-5.373,12-12S26.627,0.005,20,0.005z M4.795,29.697
|
||||
c-0.322,0.334-0.768,0.543-1.266,0.543c-0.975,0-1.765-0.789-1.765-1.764c0-0.498,0.21-0.943,0.543-1.266l-0.009-0.008l8.066-8.066
|
||||
c0.705,0.951,1.545,1.791,2.494,2.498L4.795,29.697z M20,22.006c-5.522,0-10-4.479-10-10c0-5.522,4.478-10,10-10
|
||||
c5.521,0,10,4.478,10,10C30,17.527,25.521,22.006,20,22.006z"/>
|
||||
<path fill="#CCCCCC" d="M20,5.005c-3.867,0-7,3.134-7,7c0,0.276,0.224,0.5,0.5,0.5s0.5-0.224,0.5-0.5c0-3.313,2.686-6,6-6
|
||||
c0.275,0,0.5-0.224,0.5-0.5S20.275,5.005,20,5.005z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
252
vendor/github.com/genuinetools/reg/server/static/css/styles.css
generated
vendored
Normal file
252
vendor/github.com/genuinetools/reg/server/static/css/styles.css
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,300');
|
||||
/* Have to use @import for the font, as you can only specify a single stylesheet */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html {
|
||||
min-height: 100%;
|
||||
border-top: 10px solid #ECEEF1;
|
||||
border-bottom: 10px solid #ECEEF1;
|
||||
color: #61666c;
|
||||
font-weight: 300;
|
||||
font-size: 1em;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
line-height: 2em;
|
||||
}
|
||||
body {
|
||||
padding: 20px;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
code {
|
||||
font-family: Inconsolata,monospace;
|
||||
}
|
||||
a {
|
||||
color: #61666c;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #2a2a2a;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Wrapper
|
||||
\*------------------------------------*/
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
max-width: 800px;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Demo block
|
||||
\*------------------------------------*/
|
||||
.block {
|
||||
font-size: .875em;
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
color: #9099A3;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
line-height: 3em;
|
||||
font-family: 'Museo Slab', 'Open Sans', monospace;
|
||||
}
|
||||
form {
|
||||
text-align: center;
|
||||
}
|
||||
input {
|
||||
margin: 0 auto;
|
||||
font-size: 100%;
|
||||
vertical-align: middle;
|
||||
*overflow: visible;
|
||||
line-height: normal;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
padding: 4px 32px 4px 6px;
|
||||
margin-bottom: 9px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #555555;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
width: 196px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
background: url('search.svg') no-repeat 211px center;
|
||||
background-size: auto 20px;
|
||||
}
|
||||
input:focus {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
/* IE6-9 */
|
||||
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
input[type="search"] {
|
||||
margin-top: 20px;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-transition: all 300ms ease-in;
|
||||
-moz-transition: all 300ms ease-in;
|
||||
-ms-transition: all 300ms ease-in;
|
||||
-o-transition: all 300ms ease-in;
|
||||
transition: all 300ms ease-in;
|
||||
}
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
a.clear,
|
||||
a.clear:link,
|
||||
a.clear:visited {
|
||||
color: #666;
|
||||
padding: 2px 0 2px 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
margin: 0px 0 0 20px;
|
||||
line-height: 14px;
|
||||
display: inline-block;
|
||||
border-bottom: transparent 1px solid;
|
||||
vertical-align: -10px;
|
||||
-webkit-transition: all 300ms ease-in;
|
||||
-moz-transition: all 300ms ease-in;
|
||||
-ms-transition: all 300ms ease-in;
|
||||
-o-transition: all 300ms ease-in;
|
||||
transition: all 300ms ease-in;
|
||||
}
|
||||
a.clear:hover {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Table (directory listing)
|
||||
\*------------------------------------*/
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
font-size: .875em;
|
||||
max-width: 100%;
|
||||
margin: 20px auto 0px auto;
|
||||
}
|
||||
tr {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
tr:hover td {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
th {
|
||||
text-align: left;
|
||||
font-size: .75em;
|
||||
padding-right: 20px;
|
||||
}
|
||||
/* 2nd Column: Filename */
|
||||
th + th {
|
||||
width: 65%;
|
||||
}
|
||||
/* 3rd Column: Last Modified */
|
||||
/* 4th Column: Size */
|
||||
th + th + th + th {
|
||||
width: 5%;
|
||||
}
|
||||
tr td:first-of-type {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
td {
|
||||
padding: 5px 0;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
-webkit-transition: background 300ms ease-in;
|
||||
-moz-transition: background 300ms ease-in;
|
||||
-ms-transition: background 300ms ease-in;
|
||||
-o-transition: background 300ms ease-in;
|
||||
transition: background 300ms ease-in;
|
||||
}
|
||||
td:last-child,
|
||||
th:last-child {
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
}
|
||||
td a {
|
||||
display: block;
|
||||
}
|
||||
tr.parent a {
|
||||
color: #9099A3;
|
||||
}
|
||||
.parent a:hover {
|
||||
color: #2a2a2a;
|
||||
}
|
||||
|
||||
/*------------------------------------*\
|
||||
Loading Indicator
|
||||
\*------------------------------------*/
|
||||
.signal {
|
||||
border: 2px solid #333;
|
||||
border-radius: 15px;
|
||||
height: 15px;
|
||||
left: 50%;
|
||||
margin: -8px 0 0 -8px;
|
||||
opacity: 0;
|
||||
top: 50%;
|
||||
width: 15px;
|
||||
float: right;
|
||||
animation: pulsate 1s ease-out;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
@keyframes pulsate {
|
||||
0% {
|
||||
transform: scale(.1);
|
||||
opacity: 0.0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------*\
|
||||
Footer
|
||||
\*------------------------------------*/
|
||||
.footer {
|
||||
text-align: center;
|
||||
font-size: .75em;
|
||||
margin-top: 50px;
|
||||
}
|
||||
img {
|
||||
outline: none;
|
||||
border: none;
|
||||
max-height: 16px;
|
||||
}
|
226
vendor/github.com/genuinetools/reg/server/static/css/styles.less
generated
vendored
Normal file
226
vendor/github.com/genuinetools/reg/server/static/css/styles.less
generated
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
|
||||
@import url('http://fonts.googleapis.com/css?family=Open+Sans:400,300');
|
||||
/* Have to use @import for the font, as you can only specify a single stylesheet */
|
||||
* {
|
||||
margin:0;
|
||||
padding:0;
|
||||
-webkit-box-sizing:border-box;
|
||||
-moz-box-sizing:border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height:100%;
|
||||
border-top:10px solid #ECEEF1;
|
||||
border-bottom:10px solid #ECEEF1;
|
||||
color:#61666c;
|
||||
font-weight:300;
|
||||
font-size:1em;
|
||||
font-family:'Open Sans', sans-serif;
|
||||
line-height:2em;
|
||||
}
|
||||
body {
|
||||
padding:20px;
|
||||
-webkit-backface-visibility:hidden;
|
||||
}
|
||||
code {
|
||||
font-family:Inconsolata,monospace;
|
||||
}
|
||||
a {
|
||||
color:#61666c;
|
||||
text-decoration:none;
|
||||
}
|
||||
a:hover {
|
||||
color:#2a2a2a;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Wrapper
|
||||
\*------------------------------------*/
|
||||
.wrapper {
|
||||
margin:0 auto;
|
||||
padding-top:20px;
|
||||
max-width:800px;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Demo block
|
||||
\*------------------------------------*/
|
||||
.block {
|
||||
font-size:.875em;
|
||||
margin:20px 0;
|
||||
padding:20px;
|
||||
color:#9099A3;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight:200;
|
||||
text-align:center;
|
||||
font-size:1.4em;
|
||||
line-height:3em;
|
||||
font-family:'Museo Slab','Open Sans',monospace;
|
||||
}
|
||||
form {
|
||||
text-align:center;
|
||||
}
|
||||
input {
|
||||
margin: 0 auto;
|
||||
font-size: 100%;
|
||||
vertical-align: middle;
|
||||
*overflow: visible;
|
||||
line-height: normal;
|
||||
font-family:'Open Sans', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
color:#555555;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
padding: 4px 32px 4px 6px;
|
||||
margin-bottom: 9px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #555555;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
width: 196px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-webkit-transition: border linear .2s, box-shadow linear .2s;
|
||||
-moz-transition: border linear .2s, box-shadow linear .2s;
|
||||
-o-transition: border linear .2s, box-shadow linear .2s;
|
||||
transition: border linear .2s, box-shadow linear .2s;
|
||||
background: url('search.svg') no-repeat 211px center;
|
||||
background-size:auto 20px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
border-color: rgba(82, 168, 236, 0.8);
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
/* IE6-9 */
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
margin-top: 20px;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
-webkit-appearance: textfield;
|
||||
-webkit-transition:all 300ms ease-in;
|
||||
-moz-transition:all 300ms ease-in;
|
||||
-ms-transition:all 300ms ease-in;
|
||||
-o-transition:all 300ms ease-in;
|
||||
transition:all 300ms ease-in;
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
input[type="search"]::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
a.clear, a.clear:link, a.clear:visited {
|
||||
color:#666;
|
||||
padding:2px 0 2px 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
margin:0px 0 0 20px;
|
||||
line-height: 14px;
|
||||
display: inline-block;
|
||||
border-bottom: transparent 1px solid;
|
||||
vertical-align: -10px;
|
||||
-webkit-transition:all 300ms ease-in;
|
||||
-moz-transition:all 300ms ease-in;
|
||||
-ms-transition:all 300ms ease-in;
|
||||
-o-transition:all 300ms ease-in;
|
||||
transition:all 300ms ease-in;
|
||||
}
|
||||
a.clear:hover {
|
||||
text-decoration: none;
|
||||
color:#333;
|
||||
cursor: pointer;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Table (directory listing)
|
||||
\*------------------------------------*/
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
font-size:.875em;
|
||||
max-width:100%;
|
||||
margin:20px auto 0px auto;
|
||||
}
|
||||
tr {
|
||||
outline:0;
|
||||
border:0;
|
||||
}
|
||||
tr:hover td {
|
||||
background:#f6f6f6;
|
||||
}
|
||||
th {
|
||||
text-align:left;
|
||||
font-size:.75em;
|
||||
padding-right:20px;
|
||||
}
|
||||
/* 2nd Column: Filename */
|
||||
th + th {
|
||||
width:65%;
|
||||
}
|
||||
/* 3rd Column: Last Modified */
|
||||
th + th + th {
|
||||
}
|
||||
/* 4th Column: Size */
|
||||
th + th + th + th {
|
||||
width:5%;
|
||||
}
|
||||
tr td:first-of-type {
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
}
|
||||
td {
|
||||
padding:5px 0;
|
||||
outline:0;
|
||||
border:0;
|
||||
border-bottom:1px solid #edf1f5;
|
||||
vertical-align:middle;
|
||||
text-align:left;
|
||||
-webkit-transition:background 300ms ease-in;
|
||||
-moz-transition:background 300ms ease-in;
|
||||
-ms-transition:background 300ms ease-in;
|
||||
-o-transition:background 300ms ease-in;
|
||||
transition:background 300ms ease-in;
|
||||
}
|
||||
td:last-child, th:last-child {
|
||||
text-align:right;
|
||||
padding-right:0px;
|
||||
}
|
||||
td a{
|
||||
display: block;
|
||||
}
|
||||
tr.parent a {
|
||||
color:#9099A3;
|
||||
}
|
||||
.parent a:hover {
|
||||
color:#2a2a2a;
|
||||
}
|
||||
/*------------------------------------*\
|
||||
Footer
|
||||
\*------------------------------------*/
|
||||
.footer {
|
||||
text-align:center;
|
||||
font-size:.75em;
|
||||
margin-top:50px;
|
||||
}
|
||||
img {
|
||||
outline:none;
|
||||
border:none;
|
||||
max-height:16px;
|
||||
}
|
BIN
vendor/github.com/genuinetools/reg/server/static/favicon.ico
generated
vendored
Normal file
BIN
vendor/github.com/genuinetools/reg/server/static/favicon.ico
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
5
vendor/github.com/genuinetools/reg/server/static/js/lib/jquery-1.9.1.min.js
generated
vendored
Normal file
5
vendor/github.com/genuinetools/reg/server/static/js/lib/jquery-1.9.1.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
103
vendor/github.com/genuinetools/reg/server/static/js/scripts.js
generated
vendored
Normal file
103
vendor/github.com/genuinetools/reg/server/static/js/scripts.js
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
// pretty date function
|
||||
function prettyDate(time){
|
||||
var date = new Date((time || "").replace(/-/g,"/").replace(/[TZ]/g," ")),
|
||||
diff = (((new Date()).getTime() - date.getTime()) / 1000),
|
||||
day_diff = Math.floor(diff / 86400);
|
||||
|
||||
if (isNaN(day_diff) || day_diff < 0)
|
||||
return;
|
||||
|
||||
return day_diff == 0 && (
|
||||
diff < 60 && "just now" ||
|
||||
diff < 120 && "1 minute ago" ||
|
||||
diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
|
||||
diff < 7200 && "1 hour ago" ||
|
||||
diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
|
||||
day_diff == 1 && "Yesterday" ||
|
||||
day_diff < 7 && day_diff + " days ago" ||
|
||||
day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" ||
|
||||
day_diff > 31 && Math.round(day_diff / 31) + " months ago";
|
||||
}
|
||||
|
||||
// search function
|
||||
function search(search_val){
|
||||
var suche = search_val.toLowerCase();
|
||||
var table = document.getElementById("directory");
|
||||
var cellNr = 1;
|
||||
var ele;
|
||||
for (var r = 1; r < table.rows.length; r++){
|
||||
ele = table.rows[r].cells[cellNr].innerHTML.replace(/<[^>]+>/g,"");
|
||||
if (ele.toLowerCase().indexOf(suche)>=0 ) {
|
||||
table.rows[r].style.display = '';
|
||||
} else {
|
||||
table.rows[r].style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadVulnerabilityCount(url){
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
var report = JSON.parse(xhr.responseText);
|
||||
var id = report.Repo + ':' + report.Tag;
|
||||
var element = document.getElementById(id);
|
||||
|
||||
if (element) {
|
||||
element.innerHTML = report.BadVulns;
|
||||
} else {
|
||||
console.log("element not found for given id ", id);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
var el = document.querySelectorAll('tr:nth-child(2)')[0].querySelectorAll('td:nth-child(2)')[0];
|
||||
if (el.textContent == 'Parent Directory'){
|
||||
var parent_row = document.querySelectorAll('tr:nth-child(2)')[0];
|
||||
if (parent_row.classList){
|
||||
parent_row.classList.add('parent');
|
||||
} else {
|
||||
parent_row.className += ' ' + 'parent';
|
||||
}
|
||||
}
|
||||
|
||||
var cells = document.querySelectorAll('td a');
|
||||
Array.prototype.forEach.call(cells, function(item, index){
|
||||
var link = item.getAttribute('href');
|
||||
link = link.replace('.html', '');
|
||||
item.setAttribute('href', link);
|
||||
});
|
||||
|
||||
var our_table = document.querySelectorAll('table')[0];
|
||||
our_table.setAttribute('id', 'directory');
|
||||
|
||||
// search script
|
||||
var search_input = document.querySelectorAll('input[name="filter"]')[0];
|
||||
var clear_button = document.querySelectorAll('a.clear')[0];
|
||||
|
||||
if (search_input) {
|
||||
if (search_input.value !== ''){
|
||||
search(search_input.value);
|
||||
}
|
||||
|
||||
search_input.addEventListener('keyup', function(e){
|
||||
e.preventDefault();
|
||||
search(search_input.value);
|
||||
});
|
||||
|
||||
search_input.addEventListener('keypress', function(e){
|
||||
if ( e.which == 13 ) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (clear_button) {
|
||||
clear_button.addEventListener('click', function(e){
|
||||
search_input.value = '';
|
||||
search('');
|
||||
});
|
||||
}
|
54
vendor/github.com/genuinetools/reg/server/templates/repositories.html
generated
vendored
Normal file
54
vendor/github.com/genuinetools/reg/server/templates/repositories.html
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
{{define "repositories"}}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryDomain }}</title>
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .RegistryDomain }}</h1>
|
||||
<form>
|
||||
<input name="filter" type="search"><a class="clear">clear</a>
|
||||
</form>
|
||||
|
||||
<div class="wrapper">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Repository Name</th>
|
||||
<th>Pull Command</th>
|
||||
</tr>
|
||||
{{ range $key, $value := .Repositories }}
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tags">
|
||||
{{ $value.Name }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td align="right" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tags">
|
||||
<code>docker pull {{ $value.URI }}</code>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>Made with <code><3</code> by <a href="https://github.com/jessfraz">@jessfraz</a></p>
|
||||
<p>Checkout the source code at: <a href="https://github.com/genuinetools/reg">github.com/genuinetools/reg</a></p>
|
||||
<p>Last Updated: {{ .LastUpdated }}</p>
|
||||
<p>Update Interval: {{ .UpdateInterval }}</p>
|
||||
</div><!--/.footer-->
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
74
vendor/github.com/genuinetools/reg/server/templates/tags.html
generated
vendored
Normal file
74
vendor/github.com/genuinetools/reg/server/templates/tags.html
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
{{define "tags"}}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryDomain }}/{{ .Name }}</title>
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .RegistryDomain }}/{{ .Name }}</h1>
|
||||
<div class="wrapper">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Tag</th>
|
||||
<th>Created</th>
|
||||
{{if .HasVulns}}<th>Vulnerabilities</th>{{end}}
|
||||
</tr>
|
||||
{{ range $key, $value := .Repositories }}
|
||||
<tr>
|
||||
<td valign="left" nowrap>
|
||||
{{if $.HasVulns}}<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns">{{end}}
|
||||
{{ $value.Name }}
|
||||
{{if $.HasVulns}}</a>{{end}}
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
{{if $.HasVulns}}<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns">{{end}}
|
||||
{{ $value.Tag }}
|
||||
{{if $.HasVulns}}</a>{{end}}
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
{{ $value.Created.Format "02 Jan, 2006 15:04:05 UTC" }}
|
||||
</td>
|
||||
{{if $.HasVulns}}
|
||||
<td align="right" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns" id="{{ $value.Name }}:{{ $value.Tag }}">
|
||||
<div class="signal"></div>
|
||||
</a>
|
||||
</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>Made with <code><3</code> by <a href="https://github.com/jessfraz">@jessfraz</a></p>
|
||||
<p>Checkout the source code at: <a href="https://github.com/genuinetools/reg">github.com/genuinetools/reg</a></p>
|
||||
<p>Last Updated: {{ .LastUpdated }}</p>
|
||||
</div><!--/.footer-->
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
{{if .HasVulns}}
|
||||
<script type="text/javascript">
|
||||
var ajaxCalls = [
|
||||
{{ range $key, $value := .Repositories }}
|
||||
'/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns.json',
|
||||
{{ end }}
|
||||
];
|
||||
window.onload = function() {
|
||||
Array.prototype.forEach.call(ajaxCalls, function(url, index){
|
||||
loadVulnerabilityCount(url);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
73
vendor/github.com/genuinetools/reg/server/templates/vulns.html
generated
vendored
Normal file
73
vendor/github.com/genuinetools/reg/server/templates/vulns.html
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
{{define "vulns"}}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<base href="/" >
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>{{ .RegistryURL }}/{{ .Repo }}:{{ .Tag }} Vulnerability Report</title>
|
||||
<link rel="icon" type="image/ico" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/">{{ .RegistryURL }}</a></li>
|
||||
<li class="active">{{ .Repo }}:{{ .Tag }}</li>
|
||||
</ol>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>{{ .RegistryURL }}/{{ .Repo }}:{{ .Tag }} <small>Vulnerability Report</small></h1>
|
||||
</div>
|
||||
<p class="text-right">Generated on: {{.Date}}</p>
|
||||
|
||||
{{if gt .BadVulns 5}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{.BadVulns}} High, Critical, and/or Defcon1 vulnerabilities found
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h2>Summary</h2>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<span class="badge">{{ len .Vulns }}</span>
|
||||
<b>Total</b>
|
||||
</li>
|
||||
{{range $key, $value := .VulnsBySeverity}}
|
||||
<li class="list-group-item">
|
||||
<span class="label label-{{color $key}} pull-right">{{ len $value }}</span>
|
||||
{{ $key }}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
<h2>Details</h2>
|
||||
{{range $vulns := .VulnsBySeverity}}
|
||||
{{range $value := $vulns}}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{{$value.Name}}
|
||||
<span class="label label-{{color $value.Severity}} pull-right">{{$value.Severity}}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{$value.Description}}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<a href="{{$value.Link}}" target="_blank">{{$value.Link}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<footer class="text-center">
|
||||
<p>Made with <code><3</code> by <a href="https://github.com/jessfraz">@jessfraz</a></p>
|
||||
<p>Checkout the source code at: <a href="https://github.com/genuinetools/reg">github.com/genuinetools/reg</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
BIN
vendor/github.com/genuinetools/reg/server/vuln.png
generated
vendored
Normal file
BIN
vendor/github.com/genuinetools/reg/server/vuln.png
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
55
vendor/github.com/genuinetools/reg/tags.go
generated
vendored
55
vendor/github.com/genuinetools/reg/tags.go
generated
vendored
|
@ -1,28 +1,51 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
)
|
||||
|
||||
var tagsCommand = cli.Command{
|
||||
Name: "tags",
|
||||
Usage: "get the tags for a repository",
|
||||
Action: func(c *cli.Context) error {
|
||||
if len(c.Args()) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
const tagsHelp = `Get the tags for a repository.`
|
||||
|
||||
tags, err := r.Tags(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (cmd *tagsCommand) Name() string { return "tags" }
|
||||
func (cmd *tagsCommand) Args() string { return "[OPTIONS] NAME[:TAG|@DIGEST]" }
|
||||
func (cmd *tagsCommand) ShortHelp() string { return tagsHelp }
|
||||
func (cmd *tagsCommand) LongHelp() string { return tagsHelp }
|
||||
func (cmd *tagsCommand) Hidden() bool { return false }
|
||||
|
||||
// Print the tags.
|
||||
fmt.Println(strings.Join(tags, "\n"))
|
||||
func (cmd *tagsCommand) Register(fs *flag.FlagSet) {}
|
||||
|
||||
return nil
|
||||
},
|
||||
type tagsCommand struct{}
|
||||
|
||||
func (cmd *tagsCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(image.Domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tags, err := r.Tags(image.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(tags)
|
||||
|
||||
// Print the tags.
|
||||
fmt.Println(strings.Join(tags, "\n"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
8
vendor/github.com/genuinetools/reg/tags_test.go
generated
vendored
8
vendor/github.com/genuinetools/reg/tags_test.go
generated
vendored
|
@ -1,19 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTags(t *testing.T) {
|
||||
out, err := run("tags", "busybox")
|
||||
out, err := run("tags", fmt.Sprintf("%s/busybox", domain))
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(out), err)
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
expected := `glibc
|
||||
latest
|
||||
musl
|
||||
`
|
||||
if !strings.HasSuffix(out, expected) {
|
||||
t.Logf("expected: %s\ngot: %s", expected, out)
|
||||
t.Fatalf("expected: %s\ngot: %s", expected, out)
|
||||
}
|
||||
}
|
||||
|
|
23
vendor/github.com/genuinetools/reg/testutils/configs/basicauth.yml
generated
vendored
Normal file
23
vendor/github.com/genuinetools/reg/testutils/configs/basicauth.yml
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
formatter: text
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
delete:
|
||||
enabled: true
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
host: https://localhost:5000
|
||||
tls:
|
||||
certificate: /etc/docker/registry/ssl/cert.pem
|
||||
key: /etc/docker/registry/ssl/key.pem
|
||||
auth:
|
||||
htpasswd:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
75
vendor/github.com/genuinetools/reg/testutils/configs/clair.yml
generated
vendored
Normal file
75
vendor/github.com/genuinetools/reg/testutils/configs/clair.yml
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Copyright 2015 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.
|
||||
|
||||
# The values specified here are the default values that Clair uses if no configuration file is specified or if the keys are not defined.
|
||||
clair:
|
||||
database:
|
||||
# Database driver
|
||||
type: pgsql
|
||||
options:
|
||||
# PostgreSQL Connection string
|
||||
# https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
source: postgresql://hacker:password@127.0.0.1:5432/clair?sslmode=disable&statement_timeout=60000
|
||||
|
||||
# Number of elements kept in the cache
|
||||
# Values unlikely to change (e.g. namespaces) are cached in order to save prevent needless roundtrips to the database.
|
||||
cachesize: 16384
|
||||
|
||||
api:
|
||||
# API server port
|
||||
addr: "0.0.0.0:6060"
|
||||
|
||||
# Health server port
|
||||
# This is an unencrypted endpoint useful for load balancers to check to healthiness of the clair server.
|
||||
healthaddr: "0.0.0.0:6061"
|
||||
|
||||
# Deadline before an API request will respond with a 503
|
||||
timeout: 900s
|
||||
|
||||
# Optional PKI configuration
|
||||
# If you want to easily generate client certificates and CAs, try the following projects:
|
||||
# https://github.com/coreos/etcd-ca
|
||||
# https://github.com/cloudflare/cfssl
|
||||
servername:
|
||||
cafile:
|
||||
keyfile:
|
||||
certfile:
|
||||
|
||||
updater:
|
||||
# Frequency the database will be updated with vulnerabilities from the default data sources
|
||||
# The value 0 disables the updater entirely.
|
||||
interval: 2h
|
||||
|
||||
notifier:
|
||||
# Number of attempts before the notification is marked as failed to be sent
|
||||
attempts: 3
|
||||
|
||||
# Duration before a failed notification is retried
|
||||
renotifyinterval: 2h
|
||||
|
||||
http:
|
||||
# Optional endpoint that will receive notifications via POST requests
|
||||
endpoint:
|
||||
|
||||
# Optional PKI configuration
|
||||
# If you want to easily generate client certificates and CAs, try the following projects:
|
||||
# https://github.com/cloudflare/cfssl
|
||||
# https://github.com/coreos/etcd-ca
|
||||
servername:
|
||||
cafile:
|
||||
keyfile:
|
||||
certfile:
|
||||
|
||||
# Optional HTTP Proxy: must be a valid URL (including the scheme).
|
||||
proxy:
|
1
vendor/github.com/genuinetools/reg/testutils/configs/htpasswd
generated
vendored
Normal file
1
vendor/github.com/genuinetools/reg/testutils/configs/htpasswd
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
admin:$2y$05$TpcLzA8b5hMLptNGRIRWXuOI7KmAOqIuRhHAv15qHNrJaxuyIhCg6
|
19
vendor/github.com/genuinetools/reg/testutils/configs/noauth.yml
generated
vendored
Normal file
19
vendor/github.com/genuinetools/reg/testutils/configs/noauth.yml
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
formatter: text
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
delete:
|
||||
enabled: true
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
host: https://localhost:5000
|
||||
tls:
|
||||
certificate: /etc/docker/registry/ssl/cert.pem
|
||||
key: /etc/docker/registry/ssl/key.pem
|
50
vendor/github.com/genuinetools/reg/testutils/registry.go
generated
vendored
Normal file
50
vendor/github.com/genuinetools/reg/testutils/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
)
|
||||
|
||||
// RegistryHelper implements methods to manipulate docker registry from test cases
|
||||
type RegistryHelper struct {
|
||||
dcli *client.Client
|
||||
auth string
|
||||
addr string
|
||||
}
|
||||
|
||||
// NewRegistryHelper returns RegistryHelper
|
||||
func NewRegistryHelper(dcli *client.Client, username, password, addr string) (*RegistryHelper, error) {
|
||||
auth, err := constructRegistryAuth(username, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RegistryHelper{dcli: dcli, auth: auth, addr: addr}, nil
|
||||
}
|
||||
|
||||
// RefillRegistry adds images to a registry.
|
||||
func (r *RegistryHelper) RefillRegistry(image string) error {
|
||||
if err := pullDockerImage(r.dcli, image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.dcli.ImageTag(context.Background(), image, r.addr+"/"+image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := r.dcli.ImagePush(context.Background(), r.addr+"/"+image, types.ImagePushOptions{
|
||||
RegistryAuth: r.auth,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
fd, isTerm := term.GetFdInfo(os.Stdout)
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
||||
}
|
19
vendor/github.com/genuinetools/reg/testutils/snakeoil/cert.pem
generated
vendored
Normal file
19
vendor/github.com/genuinetools/reg/testutils/snakeoil/cert.pem
generated
vendored
Normal file
|
@ -0,0 +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==
|
||||
-----END CERTIFICATE-----
|
27
vendor/github.com/genuinetools/reg/testutils/snakeoil/key.pem
generated
vendored
Normal file
27
vendor/github.com/genuinetools/reg/testutils/snakeoil/key.pem
generated
vendored
Normal file
|
@ -0,0 +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==
|
||||
-----END RSA PRIVATE KEY-----
|
414
vendor/github.com/genuinetools/reg/testutils/testutils.go
generated
vendored
Normal file
414
vendor/github.com/genuinetools/reg/testutils/testutils.go
generated
vendored
Normal file
|
@ -0,0 +1,414 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// StartRegistry starts a new registry container.
|
||||
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")
|
||||
}
|
||||
|
||||
image := "registry:2"
|
||||
|
||||
if err := pullDockerImage(dcli, image); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
r, err := dcli.ContainerCreate(
|
||||
context.Background(),
|
||||
&container.Config{
|
||||
Image: image,
|
||||
},
|
||||
&container.HostConfig{
|
||||
NetworkMode: "host",
|
||||
Binds: []string{
|
||||
filepath.Join(filepath.Dir(filename), "configs", config) + ":" + "/etc/docker/registry/config.yml" + ":ro",
|
||||
filepath.Join(filepath.Dir(filename), "configs", "htpasswd") + ":" + "/etc/docker/registry/htpasswd" + ":ro",
|
||||
filepath.Join(filepath.Dir(filename), "snakeoil") + ":" + "/etc/docker/registry/ssl" + ":ro",
|
||||
},
|
||||
RestartPolicy: container.RestartPolicy{
|
||||
Name: "always",
|
||||
},
|
||||
},
|
||||
nil, "")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// start the container
|
||||
if err := dcli.ContainerStart(context.Background(), r.ID, types.ContainerStartOptions{}); err != nil {
|
||||
return r.ID, "", err
|
||||
}
|
||||
|
||||
port := ":5000"
|
||||
addr := "https://localhost" + port
|
||||
|
||||
if err := waitForConn(addr, filepath.Join(filepath.Dir(filename), "snakeoil", "cert.pem"), filepath.Join(filepath.Dir(filename), "snakeoil", "key.pem")); err != nil {
|
||||
return r.ID, addr, err
|
||||
}
|
||||
|
||||
if err := dockerLogin("localhost"+port, username, password); err != nil {
|
||||
return r.ID, addr, err
|
||||
}
|
||||
|
||||
// Prefill the images.
|
||||
images := []string{"alpine:3.5", "alpine:latest", "busybox:latest", "busybox:musl", "busybox:glibc"}
|
||||
for _, image := range images {
|
||||
if err := prefillRegistry(image, dcli, "localhost"+port, username, password); err != nil {
|
||||
return r.ID, addr, err
|
||||
}
|
||||
}
|
||||
|
||||
return r.ID, addr, nil
|
||||
}
|
||||
|
||||
func startClairDB(dcli *client.Client) (string, error) {
|
||||
image := "postgres:latest"
|
||||
|
||||
if err := pullDockerImage(dcli, image); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
c, err := dcli.ContainerCreate(
|
||||
context.Background(),
|
||||
&container.Config{
|
||||
Image: image,
|
||||
Env: []string{
|
||||
"POSTGRES_PASSWORD=password",
|
||||
"POSTGRES_DB=clair",
|
||||
"POSTGRES_USER=hacker",
|
||||
},
|
||||
},
|
||||
&container.HostConfig{
|
||||
NetworkMode: "host",
|
||||
RestartPolicy: container.RestartPolicy{
|
||||
Name: "always",
|
||||
},
|
||||
},
|
||||
nil, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// start the container
|
||||
return c.ID, dcli.ContainerStart(context.Background(), c.ID, types.ContainerStartOptions{})
|
||||
}
|
||||
|
||||
// StartClair starts a new clair container and accompanying database.
|
||||
func StartClair(dcli *client.Client) (string, string, error) {
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
return "", "", errors.New("No caller information")
|
||||
}
|
||||
|
||||
// start the database container.
|
||||
dbID, err := startClairDB(dcli)
|
||||
if err != nil {
|
||||
return dbID, "", err
|
||||
}
|
||||
|
||||
image := "clair:dev"
|
||||
|
||||
// build the docker image
|
||||
// create the tar ball
|
||||
ctx := filepath.Dir(filepath.Dir(filename))
|
||||
tw, err := tarit(ctx)
|
||||
if err != nil {
|
||||
return dbID, "", fmt.Errorf("tarit: %v", err)
|
||||
}
|
||||
|
||||
// build the image
|
||||
resp, err := dcli.ImageBuild(context.Background(), tw, types.ImageBuildOptions{
|
||||
Tags: []string{image},
|
||||
Dockerfile: "Dockerfile.clair",
|
||||
ForceRemove: true,
|
||||
Remove: true,
|
||||
SuppressOutput: false,
|
||||
PullParent: true,
|
||||
})
|
||||
if err != nil {
|
||||
return dbID, "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return dbID, "", err
|
||||
}
|
||||
|
||||
fmt.Printf("body: %s\n", string(b))
|
||||
|
||||
c, err := dcli.ContainerCreate(
|
||||
context.Background(),
|
||||
&container.Config{
|
||||
Image: image,
|
||||
},
|
||||
&container.HostConfig{
|
||||
NetworkMode: "host",
|
||||
Binds: []string{
|
||||
filepath.Join(filepath.Dir(filename), "configs", "clair.yml") + ":" + "/etc/clair/config.yaml" + ":ro",
|
||||
},
|
||||
RestartPolicy: container.RestartPolicy{
|
||||
Name: "always",
|
||||
},
|
||||
},
|
||||
nil, "")
|
||||
if err != nil {
|
||||
return dbID, c.ID, err
|
||||
}
|
||||
|
||||
// start the container
|
||||
err = dcli.ContainerStart(context.Background(), c.ID, types.ContainerStartOptions{})
|
||||
|
||||
// wait for clair to start
|
||||
// TODO: make this not a sleep
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
return dbID, c.ID, err
|
||||
}
|
||||
|
||||
// RemoveContainer removes with force a container by it's container ID.
|
||||
func RemoveContainer(dcli *client.Client, ctrs ...string) (err error) {
|
||||
for _, c := range ctrs {
|
||||
err = dcli.ContainerRemove(context.Background(), c,
|
||||
types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// dockerLogin logins via the command line to a docker registry
|
||||
func dockerLogin(addr, username, password string) error {
|
||||
cmd := exec.Command("docker", "login", "--username", username, "--password", password, addr)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("docker login [%s] failed with output %q and error: %v", strings.Join(cmd.Args, " "), string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// prefillRegistry adds images to a registry.
|
||||
func prefillRegistry(image string, dcli *client.Client, addr, username, password string) error {
|
||||
if err := pullDockerImage(dcli, image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := dcli.ImageTag(context.Background(), image, addr+"/"+image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auth, err := constructRegistryAuth(username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := dcli.ImagePush(context.Background(), addr+"/"+image, types.ImagePushOptions{
|
||||
RegistryAuth: auth,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
fd, isTerm := term.GetFdInfo(os.Stdout)
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
||||
}
|
||||
|
||||
func pullDockerImage(dcli *client.Client, image string) error {
|
||||
exists, err := imageExists(dcli, image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := dcli.ImagePull(context.Background(), image, types.ImagePullOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
fd, isTerm := term.GetFdInfo(os.Stdout)
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerm, nil)
|
||||
}
|
||||
|
||||
func imageExists(dcli *client.Client, image string) (bool, error) {
|
||||
_, _, err := dcli.ImageInspectWithRaw(context.Background(), image)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if client.IsErrNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// waitForConn takes a tcp addr and waits until it is reachable
|
||||
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)
|
||||
}
|
||||
|
||||
certPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read system certificates: %v", err)
|
||||
}
|
||||
pem, err := ioutil.ReadFile(cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read CA certificate %s: %v", cert, err)
|
||||
}
|
||||
if !certPool.AppendCertsFromPEM(pem) {
|
||||
return fmt.Errorf("failed to append certificates from PEM file: %s", cert)
|
||||
}
|
||||
|
||||
c := http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
},
|
||||
RootCAs: certPool,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
n := 0
|
||||
max := 10
|
||||
for n < max {
|
||||
if _, err := c.Get(addr + "/v2/"); err != nil {
|
||||
fmt.Printf("try number %d to %s: %v\n", n, addr, err)
|
||||
n++
|
||||
if n != max {
|
||||
fmt.Println("sleeping for 1 second then will try again...")
|
||||
time.Sleep(time.Second)
|
||||
} else {
|
||||
return fmt.Errorf("[WHOOPS]: maximum retries for %s exceeded", addr)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// constructRegistryAuth serializes the auth configuration as JSON base64 payload.
|
||||
func constructRegistryAuth(identity, secret string) (string, error) {
|
||||
buf, err := json.Marshal(types.AuthConfig{Username: identity, Password: secret})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
func tarit(src string) (io.Reader, error) {
|
||||
s := bytes.NewBuffer(nil)
|
||||
t := bytes.NewBuffer(nil)
|
||||
buf := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(t))
|
||||
tarball := tar.NewWriter(s)
|
||||
|
||||
err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var link string
|
||||
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
if link, err = os.Readlink(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
header, err := tar.FileInfoHeader(info, link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Name = strings.TrimPrefix(path, src)
|
||||
if err := tarball.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !info.Mode().IsRegular() { //nothing more to do for non-regular
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(tarball, file)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.WriteTo(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = buf.Writer.Flush()
|
||||
return t, err
|
||||
}
|
7
vendor/github.com/genuinetools/reg/version/version.go
generated
vendored
Normal file
7
vendor/github.com/genuinetools/reg/version/version.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package version
|
||||
|
||||
// VERSION indicates which version of the binary is running.
|
||||
var VERSION string
|
||||
|
||||
// GITCOMMIT indicates which git hash the binary was built off of
|
||||
var GITCOMMIT string
|
204
vendor/github.com/genuinetools/reg/vulns.go
generated
vendored
204
vendor/github.com/genuinetools/reg/vulns.go
generated
vendored
|
@ -1,103 +1,127 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var vulnsCommand = cli.Command{
|
||||
Name: "vulns",
|
||||
Usage: "get a vulnerability report for the image from CoreOS Clair",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "clair",
|
||||
Usage: "url to clair instance",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "fixable-threshold",
|
||||
Usage: "number of fixable issues permitted",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.String("clair") == "" {
|
||||
return errors.New("clair url cannot be empty, pass --clair")
|
||||
}
|
||||
const vulnsHelp = `Get a vulnerability report for a repository from a CoreOS Clair server.`
|
||||
|
||||
if c.Int("fixable-threshold") < 0 {
|
||||
return errors.New("fixable threshold must be a positive integer")
|
||||
}
|
||||
func (cmd *vulnsCommand) Name() string { return "vulns" }
|
||||
func (cmd *vulnsCommand) Args() string { return "[OPTIONS] NAME[:TAG|@DIGEST]" }
|
||||
func (cmd *vulnsCommand) ShortHelp() string { return vulnsHelp }
|
||||
func (cmd *vulnsCommand) LongHelp() string { return vulnsHelp }
|
||||
func (cmd *vulnsCommand) Hidden() bool { return false }
|
||||
|
||||
if len(c.Args()) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize clair client.
|
||||
cr, err := clair.New(c.String("clair"), c.GlobalBool("debug"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the vulnerability report.
|
||||
report, err := cr.Vulnerabilities(r, repo, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate over the vulnerabilities by severity list.
|
||||
for sev, vulns := range report.VulnsBySeverity {
|
||||
for _, v := range vulns {
|
||||
if sev == "Fixable" {
|
||||
fmt.Printf("%s: [%s] \n%s\n%s\n", v.Name, v.Severity+" - Fixable", v.Description, v.Link)
|
||||
fmt.Printf("Fixed by: %s\n", v.FixedBy)
|
||||
} else {
|
||||
fmt.Printf("%s: [%s] \n%s\n%s\n", v.Name, v.Severity, v.Description, v.Link)
|
||||
}
|
||||
fmt.Println("-----------------------------------------")
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary and count.
|
||||
for sev, vulns := range report.VulnsBySeverity {
|
||||
fmt.Printf("%s: %d\n", sev, len(vulns))
|
||||
}
|
||||
|
||||
// Return an error if there are more than 1 fixable vulns.
|
||||
fixable, ok := report.VulnsBySeverity["Fixable"]
|
||||
if ok {
|
||||
if len(fixable) > c.Int("fixable-threshold") {
|
||||
logrus.Fatalf("%d fixable vulnerabilities found", len(fixable))
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if there are more than 10 bad vulns.
|
||||
badVulns := 0
|
||||
// Include any high vulns.
|
||||
if highVulns, ok := report.VulnsBySeverity["High"]; ok {
|
||||
badVulns += len(highVulns)
|
||||
}
|
||||
// Include any critical vulns.
|
||||
if criticalVulns, ok := report.VulnsBySeverity["Critical"]; ok {
|
||||
badVulns += len(criticalVulns)
|
||||
}
|
||||
// Include any defcon1 vulns.
|
||||
if defcon1Vulns, ok := report.VulnsBySeverity["Defcon1"]; ok {
|
||||
badVulns += len(defcon1Vulns)
|
||||
}
|
||||
if badVulns > 10 {
|
||||
logrus.Fatalf("%d bad vulnerabilities found", badVulns)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
func (cmd *vulnsCommand) Register(fs *flag.FlagSet) {
|
||||
fs.StringVar(&cmd.clairServer, "clair", os.Getenv("CLAIR_URL"), "url to clair instance (or env var CLAIR_URL)")
|
||||
fs.IntVar(&cmd.fixableThreshold, "fixable-threshhold", 0, "number of fixable issues permitted")
|
||||
}
|
||||
|
||||
type vulnsCommand struct {
|
||||
clairServer string
|
||||
fixableThreshold int
|
||||
}
|
||||
|
||||
func (cmd *vulnsCommand) Run(ctx context.Context, args []string) error {
|
||||
if len(cmd.clairServer) < 1 {
|
||||
return errors.New("clair url cannot be empty, pass --clair")
|
||||
}
|
||||
|
||||
if cmd.fixableThreshold < 0 {
|
||||
return errors.New("fixable threshold must be a positive integer")
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("pass the name of the repository")
|
||||
}
|
||||
|
||||
image, err := registry.ParseImage(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the registry client.
|
||||
r, err := createRegistryClient(image.Domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize clair client.
|
||||
cr, err := clair.New(cmd.clairServer, clair.Opt{
|
||||
Debug: debug,
|
||||
Timeout: timeout,
|
||||
Insecure: insecure,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("creation of clair client at %s failed: %v", cmd.clairServer, err)
|
||||
}
|
||||
|
||||
// Get the vulnerability report.
|
||||
report, err := cr.VulnerabilitiesV3(r, image.Path, image.Reference())
|
||||
if err != nil {
|
||||
// Fallback to Clair v2 API.
|
||||
report, err = cr.Vulnerabilities(r, image.Path, image.Reference())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the vulnerabilities by severity list.
|
||||
for sev, vulns := range report.VulnsBySeverity {
|
||||
for _, v := range vulns {
|
||||
if sev == "Fixable" {
|
||||
fmt.Printf("%s: [%s] \n%s\n%s\n", v.Name, v.Severity+" - Fixable", v.Description, v.Link)
|
||||
fmt.Printf("Fixed by: %s\n", v.FixedBy)
|
||||
} else {
|
||||
fmt.Printf("%s: [%s] \n%s\n%s\n", v.Name, v.Severity, v.Description, v.Link)
|
||||
}
|
||||
fmt.Println("-----------------------------------------")
|
||||
}
|
||||
}
|
||||
|
||||
if len(report.VulnsBySeverity) < 1 {
|
||||
fmt.Println("No vulnerabilies found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print summary and count.
|
||||
for sev, vulns := range report.VulnsBySeverity {
|
||||
fmt.Printf("%s: %d\n", sev, len(vulns))
|
||||
}
|
||||
|
||||
// Return an error if there are more than 1 fixable vulns.
|
||||
fixable, ok := report.VulnsBySeverity["Fixable"]
|
||||
if ok {
|
||||
if len(fixable) > cmd.fixableThreshold {
|
||||
logrus.Fatalf("%d fixable vulnerabilities found", len(fixable))
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if there are more than 10 bad vulns.
|
||||
badVulns := 0
|
||||
// Include any high vulns.
|
||||
if highVulns, ok := report.VulnsBySeverity["High"]; ok {
|
||||
badVulns += len(highVulns)
|
||||
}
|
||||
// Include any critical vulns.
|
||||
if criticalVulns, ok := report.VulnsBySeverity["Critical"]; ok {
|
||||
badVulns += len(criticalVulns)
|
||||
}
|
||||
// Include any defcon1 vulns.
|
||||
if defcon1Vulns, ok := report.VulnsBySeverity["Defcon1"]; ok {
|
||||
badVulns += len(defcon1Vulns)
|
||||
}
|
||||
if badVulns > 10 {
|
||||
logrus.Fatalf("%d bad vulnerabilities found", badVulns)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
20
vendor/github.com/genuinetools/reg/vulns_test.go
generated
vendored
Normal file
20
vendor/github.com/genuinetools/reg/vulns_test.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVulns(t *testing.T) {
|
||||
out, err := run("vulns", "--clair", "http://localhost:6060", fmt.Sprintf("%s/alpine:3.5", domain))
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", out, err)
|
||||
}
|
||||
|
||||
expected := `clair.clair resp.Status=200 OK
|
||||
No vulnerabilies found.`
|
||||
if !strings.HasSuffix(strings.TrimSpace(out), expected) {
|
||||
t.Fatalf("expected: %s\ngot: %s", expected, out)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue