add better generate
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
3fc6abf56b
commit
cdd93563f5
5655 changed files with 1187011 additions and 392 deletions
53
vendor/github.com/genuinetools/reg/.gitignore
generated
vendored
Normal file
53
vendor/github.com/genuinetools/reg/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
###Go###
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
|
||||
|
||||
###OSX###
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must ends with two \r.
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
reg
|
||||
server/server
|
||||
testreg
|
||||
.certs
|
||||
cross/
|
||||
|
||||
# Go coverage results
|
||||
coverage.txt
|
||||
profile.out
|
62
vendor/github.com/genuinetools/reg/.travis.yml
generated
vendored
Normal file
62
vendor/github.com/genuinetools/reg/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
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
|
32
vendor/github.com/genuinetools/reg/Dockerfile
generated
vendored
Normal file
32
vendor/github.com/genuinetools/reg/Dockerfile
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
FROM golang:alpine as builder
|
||||
MAINTAINER Jessica Frazelle <jess@linux.com>
|
||||
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates
|
||||
|
||||
COPY . /go/src/github.com/genuinetools/reg
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache --virtual .build-deps \
|
||||
git \
|
||||
gcc \
|
||||
libc-dev \
|
||||
libgcc \
|
||||
make \
|
||||
&& cd /go/src/github.com/genuinetools/reg \
|
||||
&& make static \
|
||||
&& mv reg /usr/bin/reg \
|
||||
&& apk del .build-deps \
|
||||
&& rm -rf /go \
|
||||
&& echo "Build complete."
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /usr/bin/reg /usr/bin/reg
|
||||
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs
|
||||
|
||||
ENTRYPOINT [ "reg" ]
|
||||
CMD [ "--help" ]
|
23
vendor/github.com/genuinetools/reg/Dockerfile.dev
generated
vendored
Normal file
23
vendor/github.com/genuinetools/reg/Dockerfile.dev
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
FROM golang:alpine
|
||||
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
ca-certificates \
|
||||
git
|
||||
|
||||
ENV DOCKER_BUCKET get.docker.com
|
||||
ENV DOCKER_VERSION 1.11.1
|
||||
ENV DOCKER_SHA256 893e3c6e89c0cd2c5f1e51ea41bc2dd97f5e791fcfa3cee28445df277836339d
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache --virtual .build-deps \
|
||||
curl \
|
||||
&& curl -fSL "https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-$DOCKER_VERSION.tgz" -o docker.tgz \
|
||||
&& echo "${DOCKER_SHA256} *docker.tgz" | sha256sum -c - \
|
||||
&& tar -xzvf docker.tgz \
|
||||
&& mv docker/docker /usr/local/bin/ \
|
||||
&& rm -rf docker \
|
||||
&& rm docker.tgz \
|
||||
&& docker -v \
|
||||
&& apk del .build-deps
|
||||
|
310
vendor/github.com/genuinetools/reg/Gopkg.lock
generated
vendored
Normal file
310
vendor/github.com/genuinetools/reg/Gopkg.lock
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
|||
# 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
Normal file
65
vendor/github.com/genuinetools/reg/Gopkg.toml
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
# 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"
|
21
vendor/github.com/genuinetools/reg/LICENSE
generated
vendored
Normal file
21
vendor/github.com/genuinetools/reg/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 The Genuinetools Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
192
vendor/github.com/genuinetools/reg/Makefile
generated
vendored
Normal file
192
vendor/github.com/genuinetools/reg/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,192 @@
|
|||
# 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
|
||||
BUILDTAGS :=
|
||||
|
||||
# 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
|
||||
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: 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
|
||||
docker run -d \
|
||||
--name $(DIND_CONTAINER) \
|
||||
--privileged \
|
||||
-v $(CURDIR)/.certs:/etc/docker/ssl \
|
||||
-v $(CURDIR):/go/src/github.com/genuinetools/reg \
|
||||
-v /tmp:/tmp \
|
||||
$(DIND_DOCKER_IMAGE) \
|
||||
dockerd -D --storage-driver $(DOCKER_GRAPHDRIVER) \
|
||||
-H tcp://127.0.0.1:2375 \
|
||||
--host=unix:///var/run/docker.sock \
|
||||
--exec-opt=native.cgroupdriver=cgroupfs \
|
||||
--insecure-registry localhost:5000 \
|
||||
--tlsverify \
|
||||
--tlscacert=/etc/docker/ssl/cacert.pem \
|
||||
--tlskey=/etc/docker/ssl/server.key \
|
||||
--tlscert=/etc/docker/ssl/server.cert
|
||||
|
||||
.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) .
|
||||
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) \
|
||||
-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
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
190
vendor/github.com/genuinetools/reg/README.md
generated
vendored
Normal file
190
vendor/github.com/genuinetools/reg/README.md
generated
vendored
Normal file
|
@ -0,0 +1,190 @@
|
|||
# reg
|
||||
|
||||
[](https://travis-ci.org/genuinetools/reg)
|
||||
|
||||
Docker registry v2 command line client.
|
||||
|
||||
- [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
|
||||
|
||||
#### 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)
|
||||
|
||||
#### Via Go
|
||||
|
||||
```bash
|
||||
$ go get github.com/genuinetools/reg
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
$ reg
|
||||
NAME:
|
||||
reg - Docker registry v2 client.
|
||||
|
||||
USAGE:
|
||||
reg [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
version v0.13.0, build 3b7dafb
|
||||
|
||||
AUTHOR:
|
||||
The Genuinetools Authors <no-reply@butts.com>
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
**Repositories**
|
||||
|
||||
```console
|
||||
# this command might take a while if you have hundreds of images like I do
|
||||
$ reg -r r.j3ss.co ls
|
||||
Repositories for r.j3ss.co
|
||||
REPO TAGS
|
||||
awscli latest
|
||||
beeswithmachineguns latest
|
||||
camlistore latest
|
||||
chrome beta, latest, stable
|
||||
...
|
||||
```
|
||||
|
||||
**Tags**
|
||||
|
||||
```console
|
||||
$ reg tags tor-browser
|
||||
alpha
|
||||
hardened
|
||||
latest
|
||||
stable
|
||||
```
|
||||
|
||||
## Get a Manifest
|
||||
|
||||
```console
|
||||
$ reg manifest htop
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "htop",
|
||||
"tag": "latest",
|
||||
"architecture": "amd64",
|
||||
"fsLayers": [
|
||||
{
|
||||
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
|
||||
},
|
||||
....
|
||||
],
|
||||
"history": [
|
||||
....
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Download a Layer
|
||||
|
||||
```console
|
||||
$ reg layer -o chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
OR
|
||||
$ reg layer chrome@sha256:a3ed95caeb0.. > layer.tar
|
||||
```
|
||||
|
||||
|
||||
## Delete an Image
|
||||
|
||||
```console
|
||||
$ reg rm chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
Deleted chrome@sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
```
|
||||
|
||||
## Vulnerability Reports
|
||||
|
||||
```console
|
||||
$ reg vulns --clair https://clair.j3ss.co chrome
|
||||
Found 32 vulnerabilities
|
||||
CVE-2015-5180: [Low]
|
||||
|
||||
https://security-tracker.debian.org/tracker/CVE-2015-5180
|
||||
-----------------------------------------
|
||||
CVE-2016-9401: [Low]
|
||||
popd in bash might allow local users to bypass the restricted shell and cause a use-after-free via a crafted address.
|
||||
https://security-tracker.debian.org/tracker/CVE-2016-9401
|
||||
-----------------------------------------
|
||||
CVE-2016-3189: [Low]
|
||||
Use-after-free vulnerability in bzip2recover in bzip2 1.0.6 allows remote attackers to cause a denial of service (crash) via a crafted bzip2 file, related to block ends set to before the start of the block.
|
||||
https://security-tracker.debian.org/tracker/CVE-2016-3189
|
||||
-----------------------------------------
|
||||
CVE-2011-3389: [Medium]
|
||||
The SSL protocol, as used in certain configurations in Microsoft Windows and Microsoft Internet Explorer, Mozilla Firefox, Google Chrome, Opera, and other products, encrypts data by using CBC mode with chained initialization vectors, which allows man-in-the-middle attackers to obtain plaintext HTTP headers via a blockwise chosen-boundary attack (BCBA) on an HTTPS session, in conjunction with JavaScript code that uses (1) the HTML5 WebSocket API, (2) the Java URLConnection API, or (3) the Silverlight WebClient API, aka a "BEAST" attack.
|
||||
https://security-tracker.debian.org/tracker/CVE-2011-3389
|
||||
-----------------------------------------
|
||||
CVE-2016-5318: [Medium]
|
||||
Stack-based buffer overflow in the _TIFFVGetField function in libtiff 4.0.6 and earlier allows remote attackers to crash the application via a crafted tiff.
|
||||
https://security-tracker.debian.org/tracker/CVE-2016-5318
|
||||
-----------------------------------------
|
||||
CVE-2016-9318: [Medium]
|
||||
libxml2 2.9.4 and earlier, as used in XMLSec 1.2.23 and earlier and other products, does not offer a flag directly indicating that the current document may be read but other files may not be opened, which makes it easier for remote attackers to conduct XML External Entity (XXE) attacks via a crafted document.
|
||||
https://security-tracker.debian.org/tracker/CVE-2016-9318
|
||||
-----------------------------------------
|
||||
CVE-2015-7554: [High]
|
||||
The _TIFFVGetField function in tif_dir.c in libtiff 4.0.6 allows attackers to cause a denial of service (invalid memory write and crash) or possibly have unspecified other impact via crafted field data in an extension tag in a TIFF image.
|
||||
https://security-tracker.debian.org/tracker/CVE-2015-7554
|
||||
-----------------------------------------
|
||||
Unknown: 2
|
||||
Negligible: 23
|
||||
Low: 3
|
||||
Medium: 3
|
||||
High: 1
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
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
|
||||
need to make one modification to your docker daemon config so that you can talk
|
||||
to the local registry for the tests.
|
||||
|
||||
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
|
||||
to run the tests as docker-in-docker.
|
1
vendor/github.com/genuinetools/reg/VERSION.txt
generated
vendored
Normal file
1
vendor/github.com/genuinetools/reg/VERSION.txt
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
v0.13.0
|
76
vendor/github.com/genuinetools/reg/clair/clair.go
generated
vendored
Normal file
76
vendor/github.com/genuinetools/reg/clair/clair.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Clair defines the client for retriving information from the clair API.
|
||||
type Clair struct {
|
||||
URL string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
|
||||
// New creates a new Clair struct with the given URL and credentials.
|
||||
func New(url string, debug bool) (*Clair, error) {
|
||||
transport := http.DefaultTransport
|
||||
|
||||
errorTransport := &ErrorTransport{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
// set the logging
|
||||
logf := Quiet
|
||||
if debug {
|
||||
logf = Log
|
||||
}
|
||||
|
||||
registry := &Clair{
|
||||
URL: url,
|
||||
Client: &http.Client{
|
||||
Timeout: 5 * time.Minute,
|
||||
Transport: errorTransport,
|
||||
},
|
||||
Logf: logf,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
78
vendor/github.com/genuinetools/reg/clair/layer.go
generated
vendored
Normal file
78
vendor/github.com/genuinetools/reg/clair/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
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
|
||||
}
|
||||
|
||||
resp, err := c.Client.Post(url, "application/json", bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
c.Logf("clair.clair 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)
|
||||
}
|
76
vendor/github.com/genuinetools/reg/clair/types.go
generated
vendored
Normal file
76
vendor/github.com/genuinetools/reg/clair/types.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
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 {
|
||||
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"`
|
||||
}
|
131
vendor/github.com/genuinetools/reg/clair/vulns.go
generated
vendored
Normal file
131
vendor/github.com/genuinetools/reg/clair/vulns.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
package clair
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"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),
|
||||
}
|
||||
|
||||
// Get the v1 manifest to pass to clair.
|
||||
m, err := r.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
return report, fmt.Errorf("getting the v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
}
|
||||
|
||||
// Filter out the empty layers.
|
||||
var filteredLayers []schema1.FSLayer
|
||||
for _, layer := range m.FSLayers {
|
||||
if layer.BlobSum != EmptyLayerBlobSum {
|
||||
filteredLayers = append(filteredLayers, layer)
|
||||
}
|
||||
}
|
||||
|
||||
m.FSLayers = filteredLayers
|
||||
if len(m.FSLayers) == 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(m.FSLayers) - 1; i >= 0; i-- {
|
||||
// Form the clair layer.
|
||||
l, err := c.NewClairLayer(r, repo, m.FSLayers, i)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
// Post the layer.
|
||||
if _, err := c.PostLayer(l); err != nil {
|
||||
return report, err
|
||||
}
|
||||
}
|
||||
|
||||
vl, err := c.GetLayer(m.FSLayers[0].BlobSum.String(), false, 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
|
||||
}
|
||||
|
||||
// NewClairLayer will form a layer struct required for a clar scan
|
||||
func (c *Clair) NewClairLayer(r *registry.Registry, image string, fsLayers []schema1.FSLayer, index int) (*Layer, error) {
|
||||
var parentName string
|
||||
if index < len(fsLayers)-1 {
|
||||
parentName = fsLayers[index+1].BlobSum.String()
|
||||
}
|
||||
|
||||
// form the path
|
||||
p := strings.Join([]string{r.URL, "v2", image, "blobs", fsLayers[index].BlobSum.String()}, "/")
|
||||
|
||||
useBasicAuth := false
|
||||
|
||||
// get the token
|
||||
token, err := r.Token(p)
|
||||
if err != nil {
|
||||
// if we get an error here of type: malformed auth challenge header: 'Basic realm="Registry Realm"'
|
||||
// we need to use basic auth for the registry
|
||||
if !strings.Contains(err.Error(), `malformed auth challenge header: 'Basic realm="Registry`) {
|
||||
return nil, err
|
||||
}
|
||||
useBasicAuth = true
|
||||
}
|
||||
|
||||
h := make(map[string]string)
|
||||
if token != "" && !useBasicAuth {
|
||||
h = map[string]string{
|
||||
"Authorization": fmt.Sprintf("Bearer %s", token),
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" || useBasicAuth {
|
||||
c.Logf("clair.vulns using basic auth")
|
||||
h = map[string]string{
|
||||
"Authorization": fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(r.Username+":"+r.Password))),
|
||||
}
|
||||
}
|
||||
|
||||
return &Layer{
|
||||
Name: fsLayers[index].BlobSum.String(),
|
||||
Path: p,
|
||||
ParentName: parentName,
|
||||
Format: "Docker",
|
||||
Headers: h,
|
||||
}, nil
|
||||
}
|
31
vendor/github.com/genuinetools/reg/delete.go
generated
vendored
Normal file
31
vendor/github.com/genuinetools/reg/delete.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
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
Normal file
37
vendor/github.com/genuinetools/reg/delete_test.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
52
vendor/github.com/genuinetools/reg/layer.go
generated
vendored
Normal file
52
vendor/github.com/genuinetools/reg/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
}
|
62
vendor/github.com/genuinetools/reg/list.go
generated
vendored
Normal file
62
vendor/github.com/genuinetools/reg/list.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"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
|
||||
}
|
||||
|
||||
fmt.Printf("Repositories for %s\n", auth.ServerAddress)
|
||||
|
||||
// Setup the tab writer.
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
|
||||
// Print header.
|
||||
fmt.Fprintln(w, "REPO\tTAGS")
|
||||
|
||||
var (
|
||||
l sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
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, ", "))
|
||||
|
||||
// Lock around the tabwriter to prevent garbled output.
|
||||
// See: https://github.com/genuinetools/reg/issues/54
|
||||
l.Lock()
|
||||
w.Write([]byte(out))
|
||||
l.Unlock()
|
||||
|
||||
wg.Done()
|
||||
}(repo)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
w.Flush()
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
19
vendor/github.com/genuinetools/reg/list_test.go
generated
vendored
Normal file
19
vendor/github.com/genuinetools/reg/list_test.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
out, err := run("ls")
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(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)
|
||||
}
|
||||
}
|
||||
}
|
109
vendor/github.com/genuinetools/reg/main.go
generated
vendored
Normal file
109
vendor/github.com/genuinetools/reg/main.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"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
|
||||
)
|
||||
|
||||
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."
|
||||
|
||||
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",
|
||||
},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
deleteCommand,
|
||||
layerCommand,
|
||||
listCommand,
|
||||
manifestCommand,
|
||||
tagsCommand,
|
||||
vulnsCommand,
|
||||
}
|
||||
|
||||
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") {
|
||||
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
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
97
vendor/github.com/genuinetools/reg/main_test.go
generated
vendored
Normal file
97
vendor/github.com/genuinetools/reg/main_test.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/genuinetools/reg/testutils"
|
||||
)
|
||||
|
||||
var (
|
||||
exeSuffix string // ".exe" on Windows
|
||||
|
||||
registryConfigs = []struct {
|
||||
config string
|
||||
username string
|
||||
password string
|
||||
}{
|
||||
{
|
||||
config: "noauth.yml",
|
||||
username: "blah",
|
||||
password: "blah",
|
||||
},
|
||||
{
|
||||
config: "basicauth.yml",
|
||||
username: "admin",
|
||||
password: "testing",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
exeSuffix = ".exe"
|
||||
}
|
||||
}
|
||||
|
||||
// The TestMain function creates a reg command for testing purposes and
|
||||
// deletes it after the tests have been run.
|
||||
// It also spins up a local registry prefilled with an alpine image and
|
||||
// removes that after the tests have been run.
|
||||
func TestMain(m *testing.M) {
|
||||
// build the test binary
|
||||
args := []string{"build", "-o", "testreg" + exeSuffix}
|
||||
out, err := exec.Command("go", args...).CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "building testreg failed: %v\n%s", err, out)
|
||||
os.Exit(2)
|
||||
}
|
||||
// remove test binary
|
||||
defer os.Remove("testreg" + exeSuffix)
|
||||
|
||||
// create the docker client
|
||||
dcli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not connect to docker: %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)
|
||||
panic(fmt.Errorf("starting registry container %s failed: %v", regConfig.config, err))
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
merr := m.Run()
|
||||
|
||||
// remove registry
|
||||
if err := testutils.RemoveContainer(dcli, regID); err != nil {
|
||||
log.Printf("couldn't remove registry container %s: %v", regConfig.config, err)
|
||||
}
|
||||
|
||||
if merr != 0 {
|
||||
fmt.Printf("testing config %s failed\n", regConfig.config)
|
||||
os.Exit(merr)
|
||||
}
|
||||
}
|
||||
|
||||
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...)
|
||||
cmd := exec.Command(prog, newargs...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
return string(out), err
|
||||
}
|
54
vendor/github.com/genuinetools/reg/manifest.go
generated
vendored
Normal file
54
vendor/github.com/genuinetools/reg/manifest.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
repo, ref, err := repoutils.GetRepoAndRef(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
}
|
69
vendor/github.com/genuinetools/reg/registry/authchallenge.go
generated
vendored
Normal file
69
vendor/github.com/genuinetools/reg/registry/authchallenge.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
bearerRegex = regexp.MustCompile(
|
||||
`^\s*Bearer\s+(.*)$`)
|
||||
basicRegex = regexp.MustCompile(`^\s*Basic\s+.*$`)
|
||||
)
|
||||
|
||||
func parseAuthHeader(header http.Header) (*authService, error) {
|
||||
ch, err := parseChallenge(header.Get("www-authenticate"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func parseChallenge(challengeHeader string) (*authService, error) {
|
||||
if basicRegex.MatchString(challengeHeader) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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]), ",")
|
||||
|
||||
var realm, service string
|
||||
var scope []string
|
||||
for _, s := range parts {
|
||||
p := strings.SplitN(s, "=", 2)
|
||||
if len(p) != 2 {
|
||||
return nil, fmt.Errorf("malformed auth challenge header: '%s'", challengeHeader)
|
||||
}
|
||||
key := p[0]
|
||||
value := strings.TrimSuffix(strings.TrimPrefix(p[1], `"`), `"`)
|
||||
switch key {
|
||||
case "realm":
|
||||
realm = value
|
||||
case "service":
|
||||
service = value
|
||||
case "scope":
|
||||
scope = strings.Fields(value)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown field in challege header %s: %v", key, challengeHeader)
|
||||
}
|
||||
}
|
||||
parsedRealm, err := url.Parse(realm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &authService{
|
||||
Realm: parsedRealm,
|
||||
Service: service,
|
||||
Scope: scope,
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
55
vendor/github.com/genuinetools/reg/registry/authchallenge_test.go
generated
vendored
Normal file
55
vendor/github.com/genuinetools/reg/registry/authchallenge_test.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type authServiceMock struct {
|
||||
service string
|
||||
realm string
|
||||
scope []string
|
||||
}
|
||||
|
||||
type challengeTestCase struct {
|
||||
header string
|
||||
errorString string
|
||||
value authServiceMock
|
||||
}
|
||||
|
||||
func (asm authServiceMock) equalTo(v *authService) bool {
|
||||
if asm.service != v.Service {
|
||||
return false
|
||||
}
|
||||
if reflect.DeepEqual(asm.scope, v.Scope) {
|
||||
return false
|
||||
}
|
||||
if asm.realm != v.Realm.String() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestParseChallenge(t *testing.T) {
|
||||
challengeHeaderCases := []challengeTestCase{
|
||||
{
|
||||
header: `Bearer realm="https://foobar.com/api/v1/token",service=foobar.com,scope=""`,
|
||||
value: authServiceMock{
|
||||
service: "foobar.com",
|
||||
realm: "https://foobar.com/api/v1/token",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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 !tc.value.equalTo(val) {
|
||||
t.Fatalf("got %v, expected %v", val, tc.value)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
25
vendor/github.com/genuinetools/reg/registry/basictransport.go
generated
vendored
Normal file
25
vendor/github.com/genuinetools/reg/registry/basictransport.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BasicTransport defines the data structure for authentication via basic auth.
|
||||
type BasicTransport struct {
|
||||
Transport http.RoundTripper
|
||||
URL string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// 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 t.Username != "" || t.Password != "" {
|
||||
req.SetBasicAuth(t.Username, t.Password)
|
||||
}
|
||||
}
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
return resp, err
|
||||
}
|
39
vendor/github.com/genuinetools/reg/registry/catalog.go
generated
vendored
Normal file
39
vendor/github.com/genuinetools/reg/registry/catalog.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/peterhellberg/link"
|
||||
)
|
||||
|
||||
type catalogResponse struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
// Catalog returns the repositories in a registry.
|
||||
func (r *Registry) Catalog(u string) ([]string, error) {
|
||||
if u == "" {
|
||||
u = "/v2/_catalog"
|
||||
}
|
||||
uri := r.url(u)
|
||||
r.Logf("registry.catalog url=%s", uri)
|
||||
|
||||
var response catalogResponse
|
||||
h, err := r.getJSON(uri, &response, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, l := range link.ParseHeader(h) {
|
||||
if l.Rel == "next" {
|
||||
unescaped, _ := url.QueryUnescape(l.URI)
|
||||
repos, err := r.Catalog(unescaped)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Repositories = append(response.Repositories, repos...)
|
||||
}
|
||||
}
|
||||
|
||||
return response.Repositories, nil
|
||||
}
|
47
vendor/github.com/genuinetools/reg/registry/delete.go
generated
vendored
Normal file
47
vendor/github.com/genuinetools/reg/registry/delete.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
ocd "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Delete removes a repository digest or reference 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.
|
||||
url := r.url("/v2/%s/manifests/%s", repository, digest)
|
||||
r.Logf("registry.manifests.delete url=%s repository=%s digest=%s",
|
||||
url, repository, digest)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
34
vendor/github.com/genuinetools/reg/registry/digest.go
generated
vendored
Normal file
34
vendor/github.com/genuinetools/reg/registry/digest.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
)
|
||||
|
||||
// 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)
|
||||
r.Logf("registry.manifests.get url=%s repository=%s ref=%s",
|
||||
url, repository, ref)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Accept", schema2.MediaTypeManifest)
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound {
|
||||
return "", fmt.Errorf("Got status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
digest := resp.Header.Get("Docker-Content-Digest")
|
||||
return digest, nil
|
||||
}
|
46
vendor/github.com/genuinetools/reg/registry/errortransport.go
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/registry/errortransport.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package registry
|
||||
|
||||
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 || resp.StatusCode == http.StatusUnauthorized {
|
||||
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
|
||||
}
|
89
vendor/github.com/genuinetools/reg/registry/layer.go
generated
vendored
Normal file
89
vendor/github.com/genuinetools/reg/registry/layer.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// DownloadLayer downloads a specific layer by digest for a repository.
|
||||
func (r *Registry) DownloadLayer(repository string, digest digest.Digest) (io.ReadCloser, error) {
|
||||
url := r.url("/v2/%s/blobs/%s", repository, digest)
|
||||
r.Logf("registry.layer.download url=%s repository=%s digest=%s", url, repository, digest)
|
||||
|
||||
resp, err := r.Client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q := uploadURL.Query()
|
||||
q.Set("digest", digest.String())
|
||||
uploadURL.RawQuery = q.Encode()
|
||||
|
||||
r.Logf("registry.layer.upload url=%s repository=%s digest=%s", uploadURL, repository, digest)
|
||||
|
||||
upload, err := http.NewRequest("PUT", uploadURL.String(), content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upload.Header.Set("Content-Type", "application/octet-stream")
|
||||
|
||||
_, err = r.Client.Do(upload)
|
||||
return err
|
||||
}
|
||||
|
||||
// HasLayer returns if the registry contains the specific digest for a repository.
|
||||
func (r *Registry) HasLayer(repository string, digest digest.Digest) (bool, error) {
|
||||
checkURL := r.url("/v2/%s/blobs/%s", repository, digest)
|
||||
r.Logf("registry.layer.check url=%s repository=%s digest=%s", checkURL, repository, digest)
|
||||
|
||||
resp, err := r.Client.Head(checkURL)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
return resp.StatusCode == http.StatusOK, nil
|
||||
}
|
||||
|
||||
urlErr, ok := err.(*url.Error)
|
||||
if !ok {
|
||||
return false, err
|
||||
}
|
||||
httpErr, ok := urlErr.Err.(*httpStatusError)
|
||||
if !ok {
|
||||
return false, err
|
||||
}
|
||||
if httpErr.Response.StatusCode == http.StatusNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (r *Registry) initiateUpload(repository string) (*url.URL, 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
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
location := resp.Header.Get("Location")
|
||||
locationURL, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return locationURL, nil
|
||||
}
|
86
vendor/github.com/genuinetools/reg/registry/manifest.go
generated
vendored
Normal file
86
vendor/github.com/genuinetools/reg/registry/manifest.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema1"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
)
|
||||
|
||||
// Manifest returns the manifest for a specific repository:tag.
|
||||
func (r *Registry) Manifest(repository, ref string) (distribution.Manifest, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Logf("registry.manifests resp.Status=%s, body=%s", resp.Status, body)
|
||||
|
||||
m, _, err := distribution.UnmarshalManifest(resp.Header.Get("Content-Type"), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ManifestList gets the registry v2 manifest list.
|
||||
func (r *Registry) ManifestList(repository, ref string) (manifestlist.ManifestList, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
var m manifestlist.ManifestList
|
||||
if _, err := r.getJSON(uri, &m, true); err != nil {
|
||||
r.Logf("registry.manifests response=%v", m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ManifestV2 gets the registry v2 manifest.
|
||||
func (r *Registry) ManifestV2(repository, ref string) (schema2.Manifest, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
var m schema2.Manifest
|
||||
if _, err := r.getJSON(uri, &m, true); err != nil {
|
||||
r.Logf("registry.manifests response=%v", m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ManifestV1 gets the registry v1 manifest.
|
||||
func (r *Registry) ManifestV1(repository, ref string) (schema1.SignedManifest, error) {
|
||||
uri := r.url("/v2/%s/manifests/%s", repository, ref)
|
||||
r.Logf("registry.manifests uri=%s repository=%s ref=%s", uri, repository, ref)
|
||||
|
||||
var m schema1.SignedManifest
|
||||
if _, err := r.getJSON(uri, &m, false); err != nil {
|
||||
r.Logf("registry.manifests response=%v", m)
|
||||
return m, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
12
vendor/github.com/genuinetools/reg/registry/ping.go
generated
vendored
Normal file
12
vendor/github.com/genuinetools/reg/registry/ping.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package registry
|
||||
|
||||
// Ping tries to contact a registry URL to make sure it is up and accessible.
|
||||
func (r *Registry) Ping() error {
|
||||
url := r.url("/v2/")
|
||||
r.Logf("registry.ping url=%s", url)
|
||||
resp, err := r.Client.Get(url)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
133
vendor/github.com/genuinetools/reg/registry/registry.go
generated
vendored
Normal file
133
vendor/github.com/genuinetools/reg/registry/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// Registry defines the client for retriving information from the registry API.
|
||||
type Registry struct {
|
||||
URL string
|
||||
Domain string
|
||||
Username string
|
||||
Password string
|
||||
Client *http.Client
|
||||
Logf LogfCallback
|
||||
}
|
||||
|
||||
var reProtocol = regexp.MustCompile("^https?://")
|
||||
|
||||
// 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...)
|
||||
}
|
||||
|
||||
// New creates a new Registry struct with the given URL and credentials.
|
||||
func New(auth types.AuthConfig, debug bool) (*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,
|
||||
},
|
||||
}
|
||||
|
||||
return newFromTransport(auth, transport, debug)
|
||||
}
|
||||
|
||||
func newFromTransport(auth types.AuthConfig, transport http.RoundTripper, debug bool) (*Registry, error) {
|
||||
url := strings.TrimSuffix(auth.ServerAddress, "/")
|
||||
|
||||
if !reProtocol.MatchString(url) {
|
||||
url = "https://" + url
|
||||
}
|
||||
|
||||
tokenTransport := &TokenTransport{
|
||||
Transport: transport,
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
}
|
||||
basicAuthTransport := &BasicTransport{
|
||||
Transport: tokenTransport,
|
||||
URL: url,
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
}
|
||||
errorTransport := &ErrorTransport{
|
||||
Transport: basicAuthTransport,
|
||||
}
|
||||
|
||||
// set the logging
|
||||
logf := Quiet
|
||||
if debug {
|
||||
logf = Log
|
||||
}
|
||||
|
||||
registry := &Registry{
|
||||
URL: url,
|
||||
Domain: reProtocol.ReplaceAllString(url, ""),
|
||||
Client: &http.Client{
|
||||
Transport: errorTransport,
|
||||
},
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
Logf: logf,
|
||||
}
|
||||
|
||||
if err := registry.Ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
// url returns a registry URL with the passed arguements concatenated.
|
||||
func (r *Registry) url(pathTemplate string, args ...interface{}) string {
|
||||
pathSuffix := fmt.Sprintf(pathTemplate, args...)
|
||||
url := fmt.Sprintf("%s%s", r.URL, pathSuffix)
|
||||
return url
|
||||
}
|
||||
|
||||
func (r *Registry) getJSON(url string, response interface{}, addV2Header bool) (http.Header, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addV2Header {
|
||||
req.Header.Add("Accept", schema2.MediaTypeManifest)
|
||||
req.Header.Add("Accept", manifestlist.MediaTypeManifestList)
|
||||
}
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
r.Logf("registry.registry resp.Status=%s", resp.Status)
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Header, nil
|
||||
}
|
18
vendor/github.com/genuinetools/reg/registry/tags.go
generated
vendored
Normal file
18
vendor/github.com/genuinetools/reg/registry/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package registry
|
||||
|
||||
type tagsResponse struct {
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// Tags returns the tags for a specific repository.
|
||||
func (r *Registry) Tags(repository string) ([]string, error) {
|
||||
url := r.url("/v2/%s/tags/list", repository)
|
||||
r.Logf("registry.tags url=%s repository=%s", url, repository)
|
||||
|
||||
var response tagsResponse
|
||||
if _, err := r.getJSON(url, &response, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Tags, nil
|
||||
}
|
166
vendor/github.com/genuinetools/reg/registry/tokentransport.go
generated
vendored
Normal file
166
vendor/github.com/genuinetools/reg/registry/tokentransport.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// TokenTransport defines the data structure for authentication via tokens.
|
||||
type TokenTransport struct {
|
||||
Transport http.RoundTripper
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// RoundTrip defines the round tripper for token transport.
|
||||
func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
resp, err := t.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
authService, err := isTokenDemand(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authService == nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return t.authAndRetry(authService, req)
|
||||
}
|
||||
|
||||
type authToken struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (t *TokenTransport) authAndRetry(authService *authService, req *http.Request) (*http.Response, error) {
|
||||
token, authResp, err := t.auth(authService)
|
||||
if err != nil {
|
||||
return authResp, err
|
||||
}
|
||||
|
||||
return t.retry(req, token)
|
||||
}
|
||||
|
||||
func (t *TokenTransport) auth(authService *authService) (string, *http.Response, error) {
|
||||
authReq, err := authService.Request(t.Username, t.Password)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
c := http.Client{
|
||||
Transport: t.Transport,
|
||||
}
|
||||
|
||||
resp, err := c.Do(authReq)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", resp, err
|
||||
}
|
||||
|
||||
var authToken authToken
|
||||
if err := json.NewDecoder(resp.Body).Decode(&authToken); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return authToken.Token, nil, nil
|
||||
}
|
||||
|
||||
func (t *TokenTransport) retry(req *http.Request, token string) (*http.Response, error) {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return t.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
Realm *url.URL
|
||||
Service string
|
||||
Scope []string
|
||||
}
|
||||
|
||||
func (a *authService) Request(username, password string) (*http.Request, error) {
|
||||
q := a.Realm.Query()
|
||||
q.Set("service", a.Service)
|
||||
for _, s := range a.Scope {
|
||||
q.Set("scope", s)
|
||||
}
|
||||
// q.Set("scope", "repository:r.j3ss.co/htop:push,pull")
|
||||
a.Realm.RawQuery = q.Encode()
|
||||
|
||||
req, err := http.NewRequest("GET", a.Realm.String(), nil)
|
||||
|
||||
if username != "" || password != "" {
|
||||
req.SetBasicAuth(username, password)
|
||||
}
|
||||
|
||||
return req, err
|
||||
}
|
||||
|
||||
func isTokenDemand(resp *http.Response) (*authService, error) {
|
||||
if resp == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if resp.StatusCode != http.StatusUnauthorized {
|
||||
return nil, nil
|
||||
}
|
||||
return parseAuthHeader(resp.Header)
|
||||
}
|
||||
|
||||
// Token returns the required token for the specific resource url.
|
||||
func (r *Registry) Token(url string) (string, error) {
|
||||
r.Logf("registry.token url=%s", url)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := r.Client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
a, err := isTokenDemand(resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
r.Logf("registry.token authService=nil")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
authReq, err := a.Request(r.Username, r.Password)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp, err = r.Client.Do(authReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("Getting token failed with StatusCode != StatusOK but %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var authToken authToken
|
||||
if err := json.NewDecoder(resp.Body).Decode(&authToken); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if authToken.Token == "" {
|
||||
return "", errors.New("Auth token cannot be empty")
|
||||
}
|
||||
|
||||
return authToken.Token, nil
|
||||
}
|
141
vendor/github.com/genuinetools/reg/repoutils/repoutils.go
generated
vendored
Normal file
141
vendor/github.com/genuinetools/reg/repoutils/repoutils.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
package repoutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker-ce/components/cli/cli/config"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultDockerRegistry is the default docker registry address.
|
||||
DefaultDockerRegistry = "https://registry-1.docker.io"
|
||||
|
||||
latestTagSuffix = ":latest"
|
||||
)
|
||||
|
||||
// GetAuthConfig returns the docker registry AuthConfig.
|
||||
// Optionally takes in the authentication values, otherwise pulls them from the
|
||||
// docker config file.
|
||||
func GetAuthConfig(username, password, registry string) (types.AuthConfig, error) {
|
||||
if username != "" && password != "" && registry != "" {
|
||||
return types.AuthConfig{
|
||||
Username: username,
|
||||
Password: password,
|
||||
ServerAddress: registry,
|
||||
}, nil
|
||||
}
|
||||
|
||||
dcfg, err := config.Load(config.Dir())
|
||||
if err != nil {
|
||||
return types.AuthConfig{}, fmt.Errorf("Loading config file failed: %v", err)
|
||||
}
|
||||
|
||||
// return error early if there are no auths saved
|
||||
if !dcfg.ContainsAuth() {
|
||||
// If we were passed a registry, just use that.
|
||||
if registry != "" {
|
||||
return setDefaultRegistry(types.AuthConfig{
|
||||
ServerAddress: registry,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Otherwise, just use an empty auth config.
|
||||
return types.AuthConfig{}, nil
|
||||
}
|
||||
|
||||
authConfigs, err := dcfg.GetAllCredentials()
|
||||
if err != nil {
|
||||
return types.AuthConfig{}, fmt.Errorf("Getting credentials failed: %v", err)
|
||||
}
|
||||
|
||||
// if they passed a specific registry, return those creds _if_ they exist
|
||||
if registry != "" {
|
||||
// try with the user input
|
||||
if creds, ok := authConfigs[registry]; ok {
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// remove https:// from user input and try again
|
||||
if strings.HasPrefix(registry, "https://") {
|
||||
if creds, ok := authConfigs[strings.TrimPrefix(registry, "https://")]; ok {
|
||||
return creds, nil
|
||||
}
|
||||
}
|
||||
|
||||
// remove http:// from user input and try again
|
||||
if strings.HasPrefix(registry, "http://") {
|
||||
if creds, ok := authConfigs[strings.TrimPrefix(registry, "http://")]; ok {
|
||||
return creds, nil
|
||||
}
|
||||
}
|
||||
|
||||
// add https:// to user input and try again
|
||||
// see https://github.com/genuinetools/reg/issues/32
|
||||
if !strings.HasPrefix(registry, "https://") && !strings.HasPrefix(registry, "http://") {
|
||||
if creds, ok := authConfigs["https://"+registry]; ok {
|
||||
return creds, nil
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Using registry %q with no authentication\n", registry)
|
||||
|
||||
// Otherwise just use the registry with no auth.
|
||||
return setDefaultRegistry(types.AuthConfig{
|
||||
ServerAddress: registry,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Just set the auth config as the first registryURL, username and password
|
||||
// found in the auth config.
|
||||
for _, creds := range authConfigs {
|
||||
fmt.Printf("No registry passed. Using registry %q\n", creds.ServerAddress)
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// Don't use any authentication.
|
||||
// We should never get here.
|
||||
fmt.Println("Not using any authentication")
|
||||
return types.AuthConfig{}, nil
|
||||
}
|
||||
|
||||
// GetRepoAndRef parses the repo name and reference.
|
||||
func GetRepoAndRef(image string) (repo, ref string, err error) {
|
||||
if image == "" {
|
||||
return "", "", reference.ErrNameEmpty
|
||||
}
|
||||
|
||||
image = addLatestTagSuffix(image)
|
||||
|
||||
var parts []string
|
||||
if strings.Contains(image, "@") {
|
||||
parts = strings.Split(image, "@")
|
||||
} else if strings.Contains(image, ":") {
|
||||
parts = strings.Split(image, ":")
|
||||
}
|
||||
|
||||
repo = parts[0]
|
||||
if len(parts) > 1 {
|
||||
ref = parts[1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// addLatestTagSuffix adds :latest to the image if it does not have a tag
|
||||
func addLatestTagSuffix(image string) string {
|
||||
if !strings.Contains(image, ":") {
|
||||
return image + latestTagSuffix
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
func setDefaultRegistry(auth types.AuthConfig) types.AuthConfig {
|
||||
if auth.ServerAddress == "docker.io" {
|
||||
auth.ServerAddress = DefaultDockerRegistry
|
||||
}
|
||||
|
||||
return auth
|
||||
}
|
200
vendor/github.com/genuinetools/reg/repoutils/repoutils_test.go
generated
vendored
Normal file
200
vendor/github.com/genuinetools/reg/repoutils/repoutils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,200 @@
|
|||
package repoutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker-ce/components/cli/cli/config"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestGetAuthConfig(t *testing.T) {
|
||||
configTestcases := []struct {
|
||||
name string
|
||||
username, password, registry string
|
||||
configdir string
|
||||
err error
|
||||
config types.AuthConfig
|
||||
}{
|
||||
{
|
||||
name: "pass in all details",
|
||||
username: "jess",
|
||||
password: "password",
|
||||
registry: "r.j3ss.co",
|
||||
config: types.AuthConfig{
|
||||
Username: "jess",
|
||||
Password: "password",
|
||||
ServerAddress: "r.j3ss.co",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid config dir",
|
||||
configdir: "testdata/invalid",
|
||||
err: errors.New("Loading config file failed: "),
|
||||
config: types.AuthConfig{},
|
||||
},
|
||||
{
|
||||
name: "empty config",
|
||||
configdir: "testdata/empty",
|
||||
config: types.AuthConfig{},
|
||||
},
|
||||
{
|
||||
name: "empty config with docker.io",
|
||||
registry: "docker.io",
|
||||
configdir: "testdata/empty",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: DefaultDockerRegistry,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty config with registry",
|
||||
registry: "r.j3ss.co",
|
||||
configdir: "testdata/empty",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "r.j3ss.co",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid with multiple",
|
||||
registry: "r.j3ss.co",
|
||||
configdir: "testdata/valid",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "r.j3ss.co",
|
||||
Username: "user",
|
||||
Password: "blah\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid with multiple and https:// prefix",
|
||||
registry: "https://r.j3ss.co",
|
||||
configdir: "testdata/valid",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "r.j3ss.co",
|
||||
Username: "user",
|
||||
Password: "blah\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid with multiple and http:// prefix",
|
||||
registry: "http://r.j3ss.co",
|
||||
configdir: "testdata/valid",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "r.j3ss.co",
|
||||
Username: "user",
|
||||
Password: "blah\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid with multiple and no https:// prefix",
|
||||
registry: "reg.j3ss.co",
|
||||
configdir: "testdata/valid",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "https://reg.j3ss.co",
|
||||
Username: "joe",
|
||||
Password: "otherthing\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid with multiple and but registry not found",
|
||||
registry: "otherreg.j3ss.co",
|
||||
configdir: "testdata/valid",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "otherreg.j3ss.co",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid and no registry passed",
|
||||
configdir: "testdata/singlevalid",
|
||||
config: types.AuthConfig{
|
||||
ServerAddress: "https://index.docker.io/v1/",
|
||||
Username: "user",
|
||||
Password: "thing\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range configTestcases {
|
||||
if testcase.configdir != "" {
|
||||
// Set the config directory.
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("get working directory failed: %v", err)
|
||||
}
|
||||
config.SetDir(filepath.Join(wd, testcase.configdir))
|
||||
}
|
||||
|
||||
cfg, err := GetAuthConfig(testcase.username, testcase.password, testcase.registry)
|
||||
if err != nil || testcase.err != nil {
|
||||
if err == nil || testcase.err == nil {
|
||||
t.Fatalf("%q: expected err (%v), got err (%v)", testcase.name, testcase.err, err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), testcase.err.Error()) {
|
||||
t.Fatalf("%q: expected err (%v), got err (%v)", testcase.name, testcase.err, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(testcase.config, cfg); diff != "" {
|
||||
t.Errorf("%s: authconfig differs: (-got +want)\n%s", testcase.name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRepoAndRef(t *testing.T) {
|
||||
imageTestcases := []struct {
|
||||
// input is the repository name or name component testcase
|
||||
input string
|
||||
// err is the error expected from Parse, or nil
|
||||
err error
|
||||
// repository is the string representation for the reference
|
||||
repository string
|
||||
// ref the reference
|
||||
ref string
|
||||
}{
|
||||
{
|
||||
input: "alpine",
|
||||
repository: "alpine",
|
||||
ref: "latest",
|
||||
},
|
||||
{
|
||||
input: "docker:dind",
|
||||
repository: "docker",
|
||||
ref: "dind",
|
||||
},
|
||||
{
|
||||
input: "",
|
||||
err: reference.ErrNameEmpty,
|
||||
},
|
||||
{
|
||||
input: "chrome@sha256:2a6c8ad38c41ae5122d76be59b34893d7fa1bdfaddd85bf0e57d0d16c0f7f91e",
|
||||
repository: "chrome",
|
||||
ref: "sha256:2a6c8ad38c41ae5122d76be59b34893d7fa1bdfaddd85bf0e57d0d16c0f7f91e",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range imageTestcases {
|
||||
repo, ref, err := GetRepoAndRef(testcase.input)
|
||||
if err != nil || testcase.err != nil {
|
||||
if err == nil || testcase.err == nil {
|
||||
t.Fatalf("%q: expected err (%v), got err (%v)", testcase.input, testcase.err, err)
|
||||
}
|
||||
if err.Error() != testcase.err.Error() {
|
||||
t.Fatalf("%q: expected err (%v), got err (%v)", testcase.input, testcase.err, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if testcase.repository != repo {
|
||||
t.Fatalf("%q: expected repo (%s), got repo (%s)", testcase.input, testcase.repository, repo)
|
||||
}
|
||||
|
||||
if testcase.ref != ref {
|
||||
t.Fatalf("%q: expected ref (%s), got ref (%s)", testcase.input, testcase.ref, ref)
|
||||
}
|
||||
}
|
||||
}
|
34
vendor/github.com/genuinetools/reg/server/Dockerfile
generated
vendored
Normal file
34
vendor/github.com/genuinetools/reg/server/Dockerfile
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
FROM golang:alpine as builder
|
||||
MAINTAINER Jessica Frazelle <jess@linux.com>
|
||||
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache --virtual .build-deps \
|
||||
git \
|
||||
gcc \
|
||||
libc-dev \
|
||||
libgcc \
|
||||
&& go get -v github.com/genuinetools/reg \
|
||||
&& cd /go/src/github.com/genuinetools/reg \
|
||||
&& CGO_ENABLED=0 go build -a -tags netgo -ldflags '-extldflags "-static"' -o /usr/bin/reg-server ./server \
|
||||
&& apk del .build-deps \
|
||||
&& rm -rf /go \
|
||||
&& echo "Build complete."
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /usr/bin/reg-server /usr/bin/reg-server
|
||||
COPY --from=builder /etc/ssl/certs/ /etc/ssl/certs
|
||||
|
||||
COPY static /src/static
|
||||
COPY templates /src/templates
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
ENTRYPOINT [ "reg-server" ]
|
||||
CMD [ "--help" ]
|
46
vendor/github.com/genuinetools/reg/server/README.md
generated
vendored
Normal file
46
vendor/github.com/genuinetools/reg/server/README.md
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
# reg-server
|
||||
|
||||
A static UI for a docker registry. Comes with vulnerability scanning if you
|
||||
have a [CoreOS Clair](https://github.com/coreos/clair) server set up.
|
||||
|
||||
Demo at [r.j3ss.co](https://r.j3ss.co).
|
||||
|
||||
## Usage
|
||||
|
||||
```console
|
||||
$ reg-server -h
|
||||
NAME:
|
||||
reg-server - Docker registry v2 static UI server.
|
||||
|
||||
USAGE:
|
||||
reg-server [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
v0.1.0
|
||||
|
||||
AUTHOR:
|
||||
The Genuinetools Authors <no-reply@butts.com>
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--debug, -d run in debug mode
|
||||
--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)
|
||||
--insecure, -k do not verify tls certificates of registry
|
||||
--port value port for server to run on (default: "8080")
|
||||
--cert value path to ssl cert
|
||||
--key value path to ssl key
|
||||
--interval value interval to generate new index.html's at (default: "1h")
|
||||
--clair value url to clair instance
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
257
vendor/github.com/genuinetools/reg/server/handlers.go
generated
vendored
Normal file
257
vendor/github.com/genuinetools/reg/server/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"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
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
func (rc *registryController) repositories(staticDir string) error {
|
||||
updating = true
|
||||
logrus.Info("fetching catalog")
|
||||
|
||||
result := AnalysisResult{
|
||||
RegistryDomain: rc.reg.Domain,
|
||||
LastUpdated: time.Now().Local().Format(time.RFC1123),
|
||||
}
|
||||
|
||||
repoList, err := r.Catalog("")
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting catalog failed: %v", err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// parse & execute the template
|
||||
logrus.Info("executing the template repositories")
|
||||
|
||||
path := filepath.Join(staticDir, "index.html")
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("creating/opening file %s", path)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := tmpl.ExecuteTemplate(f, "repositories", result); err != nil {
|
||||
f.Close()
|
||||
return fmt.Errorf("execute template repositories failed: %v", err)
|
||||
}
|
||||
|
||||
updating = false
|
||||
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")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
repo, err := url.QueryUnescape(vars["repo"])
|
||||
if err != nil || repo == "" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Empty repo")
|
||||
return
|
||||
}
|
||||
|
||||
tags, err := rc.reg.Tags(repo)
|
||||
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.StatusNotFound)
|
||||
fmt.Fprint(w, "No tags found")
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "No tags found")
|
||||
return
|
||||
}
|
||||
|
||||
result := AnalysisResult{
|
||||
RegistryDomain: rc.reg.Domain,
|
||||
LastUpdated: time.Now().Local().Format(time.RFC1123),
|
||||
Name: repo,
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
// get the manifest
|
||||
m1, err := rc.reg.ManifestV1(repo, tag)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
"repo": repo,
|
||||
"tag": tag,
|
||||
}).Errorf("getting v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "Manifest not found")
|
||||
return
|
||||
}
|
||||
|
||||
var createdDate time.Time
|
||||
for _, h := range m1.History {
|
||||
var comp v1Compatibility
|
||||
|
||||
if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("unmarshal v1 manifest for %s:%s failed: %v", repo, tag, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if err := tmpl.ExecuteTemplate(w, "tags", result); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "tags",
|
||||
"URL": r.URL,
|
||||
"method": r.Method,
|
||||
}).Errorf("template rendering failed: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
result := clair.VulnerabilityReport{}
|
||||
|
||||
if rc.cl != nil {
|
||||
result, err = rc.cl.Vulnerabilities(rc.reg, repo, tag)
|
||||
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
|
||||
}
|
||||
|
||||
if err := 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
|
||||
}
|
||||
}
|
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: 65 KiB |
248
vendor/github.com/genuinetools/reg/server/server.go
generated
vendored
Normal file
248
vendor/github.com/genuinetools/reg/server/server.go
generated
vendored
Normal file
|
@ -0,0 +1,248 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/registry"
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"github.com/gorilla/mux"
|
||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
// VERSION is the binary version.
|
||||
VERSION = "v0.2.0"
|
||||
)
|
||||
|
||||
var (
|
||||
updating = false
|
||||
r *registry.Registry
|
||||
cl *clair.Clair
|
||||
tmpl *template.Template
|
||||
)
|
||||
|
||||
// preload initializes any global options and configuration
|
||||
// before the main or sub commands are run.
|
||||
func preload(c *cli.Context) (err error) {
|
||||
if c.GlobalBool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "reg-server"
|
||||
app.Version = VERSION
|
||||
app.Author = "The Genuinetools Authors"
|
||||
app.Email = "no-reply@butts.com"
|
||||
app.Usage = "Docker registry v2 static UI server."
|
||||
app.Before = preload
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug, d",
|
||||
Usage: "run in debug mode",
|
||||
},
|
||||
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)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "insecure, k",
|
||||
Usage: "do not verify tls certificates of registry",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "once, o",
|
||||
Usage: "generate an output once and then exit",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "port",
|
||||
Value: "8080",
|
||||
Usage: "port for server to run on",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cert",
|
||||
Usage: "path to ssl cert",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
Usage: "path to ssl key",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "interval",
|
||||
Value: "1h",
|
||||
Usage: "interval to generate new index.html's at",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "clair",
|
||||
Usage: "url to clair instance",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
auth, err := repoutils.GetAuthConfig(c.GlobalString("username"), c.GlobalString("password"), c.GlobalString("registry"))
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
// create the registry client
|
||||
if c.GlobalBool("insecure") {
|
||||
r, err = registry.NewInsecure(auth, c.GlobalBool("debug"))
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
r, err = registry.New(auth, c.GlobalBool("debug"))
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// create a clair instance if needed
|
||||
if c.GlobalString("clair") != "" {
|
||||
cl, err = clair.New(c.GlobalString("clair"), c.GlobalBool("debug"))
|
||||
if err != nil {
|
||||
logrus.Warnf("creation of clair failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// get the path to the static directory
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
staticDir := filepath.Join(wd, "static")
|
||||
|
||||
// create the template
|
||||
templateDir := filepath.Join(staticDir, "../templates")
|
||||
|
||||
// make sure all the templates exist
|
||||
vulns := filepath.Join(templateDir, "vulns.html")
|
||||
if _, err := os.Stat(vulns); os.IsNotExist(err) {
|
||||
logrus.Fatalf("Template %s not found", vulns)
|
||||
}
|
||||
layout := filepath.Join(templateDir, "repositories.html")
|
||||
if _, err := os.Stat(layout); os.IsNotExist(err) {
|
||||
logrus.Fatalf("Template %s not found", layout)
|
||||
}
|
||||
tags := filepath.Join(templateDir, "tags.html")
|
||||
if _, err := os.Stat(tags); os.IsNotExist(err) {
|
||||
logrus.Fatalf("Template %s not found", tags)
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
tmpl = template.Must(template.New("").Funcs(funcMap).ParseGlob(templateDir + "/*.html"))
|
||||
|
||||
rc := registryController{
|
||||
reg: r,
|
||||
cl: cl,
|
||||
}
|
||||
|
||||
// create the initial index
|
||||
logrus.Info("creating initial static index")
|
||||
if err := rc.repositories(staticDir); err != nil {
|
||||
logrus.Fatalf("Error creating index: %v", err)
|
||||
}
|
||||
|
||||
if c.GlobalBool("once") {
|
||||
logrus.Info("Output generated")
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse the duration
|
||||
dur, err := time.ParseDuration(c.String("interval"))
|
||||
if err != nil {
|
||||
logrus.Fatalf("parsing %s as duration failed: %v", c.String("interval"), err)
|
||||
}
|
||||
ticker := time.NewTicker(dur)
|
||||
|
||||
go func() {
|
||||
// create more indexes every X minutes based off interval
|
||||
for range ticker.C {
|
||||
if !updating {
|
||||
logrus.Info("creating timer based static index")
|
||||
if err := rc.repositories(staticDir); err != nil {
|
||||
logrus.Warnf("creating static index failed: %v", err)
|
||||
updating = false
|
||||
}
|
||||
} else {
|
||||
logrus.Warnf("skipping timer based static index update for %s", c.String("interval"))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// create mux server
|
||||
mux := mux.NewRouter()
|
||||
mux.UseEncodedPath()
|
||||
|
||||
// static files handler
|
||||
staticHandler := http.FileServer(http.Dir(staticDir))
|
||||
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)
|
||||
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)
|
||||
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticHandler))
|
||||
mux.Handle("/", staticHandler)
|
||||
|
||||
// set up the server
|
||||
port := c.String("port")
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: mux,
|
||||
}
|
||||
logrus.Infof("Starting server on port %q", port)
|
||||
if c.String("cert") != "" && c.String("key") != "" {
|
||||
logrus.Fatal(server.ListenAndServeTLS(c.String("cert"), c.String("key")))
|
||||
} else {
|
||||
logrus.Fatal(server.ListenAndServe())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
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('');
|
||||
});
|
||||
}
|
60
vendor/github.com/genuinetools/reg/server/templates/repositories.html
generated
vendored
Normal file
60
vendor/github.com/genuinetools/reg/server/templates/repositories.html
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
{{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">
|
||||
<a href="https://twitter.com/jessfraz">@jessfraz</a>
|
||||
<p>Last Updated: {{ .LastUpdated }}</p>
|
||||
</div><!--/.footer-->
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-29404280-12', 'jessfraz.com');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
77
vendor/github.com/genuinetools/reg/server/templates/tags.html
generated
vendored
Normal file
77
vendor/github.com/genuinetools/reg/server/templates/tags.html
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
{{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>
|
||||
<th>Vulnerabilities</th>
|
||||
</tr>
|
||||
{{ range $key, $value := .Repositories }}
|
||||
<tr>
|
||||
<td valign="left" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns">
|
||||
{{ $value.Name }}
|
||||
</a>
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
<a href="/repo/{{ $value.Name | urlquery }}/tag/{{ $value.Tag }}/vulns">
|
||||
{{ $value.Tag }}
|
||||
</a>
|
||||
</td>
|
||||
<td align="right" nowrap>
|
||||
{{ $value.Created.Format "02 Jan, 2006 15:04:05 UTC" }}
|
||||
</td>
|
||||
<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>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://twitter.com/jessfraz">@jessfraz</a>
|
||||
<p>Last Updated: {{ .LastUpdated }}</p>
|
||||
</div><!--/.footer-->
|
||||
<script src="/static/js/scripts.js"></script>
|
||||
<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>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-29404280-12', 'jessfraz.com');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
78
vendor/github.com/genuinetools/reg/server/templates/vulns.html
generated
vendored
Normal file
78
vendor/github.com/genuinetools/reg/server/templates/vulns.html
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
{{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}}
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-29404280-12', 'jessfraz.com');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
15
vendor/github.com/genuinetools/reg/server/templates/vulns.txt
generated
vendored
Normal file
15
vendor/github.com/genuinetools/reg/server/templates/vulns.txt
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{{define "vulns"}}CVE Report for {{.Repo}}:{{.Tag}}
|
||||
Generated on: {{.Date}}
|
||||
|
||||
Vulnerabilities Found: {{len .Vulns}}
|
||||
|
||||
{{range $key, $value := .VulnsBySeverity}}{{$key}}: {{len $value}}
|
||||
{{end}}
|
||||
{{if gt .BadVulns 5}}------------------------------------ ALERT ------------------------------------
|
||||
{{.BadVulns}} High, Critical, and/or Defcon1 vulnerabilities found
|
||||
|
||||
{{end}}{{range $vulns := .VulnsBySeverity}}{{range $value := $vulns}}{{$value.Name}}: [{{$value.Severity}}]
|
||||
{{trim $value.Description}}
|
||||
{{$value.Link}}
|
||||
-------------------------------------------------------------------------------
|
||||
{{end}}{{end}}{{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: 88 KiB |
28
vendor/github.com/genuinetools/reg/tags.go
generated
vendored
Normal file
28
vendor/github.com/genuinetools/reg/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
tags, err := r.Tags(c.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print the tags.
|
||||
fmt.Println(strings.Join(tags, "\n"))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
19
vendor/github.com/genuinetools/reg/tags_test.go
generated
vendored
Normal file
19
vendor/github.com/genuinetools/reg/tags_test.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTags(t *testing.T) {
|
||||
out, err := run("tags", "busybox")
|
||||
if err != nil {
|
||||
t.Fatalf("output: %s, error: %v", string(out), err)
|
||||
}
|
||||
expected := `glibc
|
||||
musl
|
||||
`
|
||||
if !strings.HasSuffix(out, expected) {
|
||||
t.Logf("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
|
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
|
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-----
|
||||
MIIDAjCCAeqgAwIBAgIQC+Tw335jnu9Z46unSVFfeTANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMB4XDTE3MDYwNjAwMDAyNFoXDTE4MDYwNjAwMDAy
|
||||
NFowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAN8/Xqef2iozUMXLRCvHnGc+SMaEDuW/iM9n/IL68F11esAR6tXhaiRQ
|
||||
RdEsM9MyfyDYt9juE+XLaMyqhTAXwK9YzULE4BTVbAr9uOgxLtyWspA4uWfxhcrq
|
||||
CqQTRc0U95ZEnAVSjytDAXtLQyP+UlnMzmMhDpzRuH1YXqm6qB07G5yOJyPKkDrq
|
||||
EyqUsjqBJmBkUJiYfSx95Jen+4ZzlSR7wOsoxei09QYyvo3DMfcJO8Wb8IQFjOT0
|
||||
ohhBFBR3v1HOOT6bKuhUHif3K+STguMEhHrgAFmcFW3NPQ3It2SyKfGBZ8nbmSA9
|
||||
2tjojQEFUHqRKp0UWyObuUmNAo1U1w8CAwEAAaNUMFIwDgYDVR0PAQH/BAQDAgKk
|
||||
MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0RBBMw
|
||||
EYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCl+yJgekN3dMbR
|
||||
mxs7B8VSFv5a7zElDvTnagX3pSBHq7fLf45qVHOmZK/esgD3K8H5Kvft6u100b1j
|
||||
4TLn2oFYVMME8BV0qNl5wgynNrJs131G3jgxcrgqu9NdlFpWZm8S+DCHo+h1ZH4Z
|
||||
LmlUt2uvwCbmZK/e6U0ZDICDKRwMaC6LdUCfLfn9F+ACpPTpAZBVbi0rpAYimBDf
|
||||
j2QZJBWD9tV5xUVSLEmqFvi42g2khK43HFu9N35vPIqyrl4Gh5x3erZR7t8pGEu0
|
||||
kOiqfCmV1GHL8egxYew4wJ1P6TzhYNk/7vpiJxrwPs3vW+WXaSBFdvoV1qQ6onjm
|
||||
CxG31l+z
|
||||
-----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-----
|
||||
MIIEowIBAAKCAQEA3z9ep5/aKjNQxctEK8ecZz5IxoQO5b+Iz2f8gvrwXXV6wBHq
|
||||
1eFqJFBF0Swz0zJ/INi32O4T5ctozKqFMBfAr1jNQsTgFNVsCv246DEu3JaykDi5
|
||||
Z/GFyuoKpBNFzRT3lkScBVKPK0MBe0tDI/5SWczOYyEOnNG4fVheqbqoHTsbnI4n
|
||||
I8qQOuoTKpSyOoEmYGRQmJh9LH3kl6f7hnOVJHvA6yjF6LT1BjK+jcMx9wk7xZvw
|
||||
hAWM5PSiGEEUFHe/Uc45Ppsq6FQeJ/cr5JOC4wSEeuAAWZwVbc09Dci3ZLIp8YFn
|
||||
yduZID3a2OiNAQVQepEqnRRbI5u5SY0CjVTXDwIDAQABAoIBACw3LNQeQONiznie
|
||||
TZ4uJrf8CgXnWdv/F2WcvtJiSQD5p5oq8kvyHUeb7ngDPTBzK+KhiagZXy+AHf2L
|
||||
OF3SFoOkHuM+gvMdYgy7O8ghFZry7eLKmU4Q8+LAf+MHPifkIzVL2Wrkcx6qYry8
|
||||
p0uVr1HB0o6nmXFNyDBrNDSBl5JSJ+IyvtPr7ow5iO2iZLh0nV5CfM65vX2ESkvD
|
||||
+uil1uFfLdmNkfItK/0oTLngiXzJBCgPzTwBnKGlmoYzWvO/CMMIEkv0tdy/b1l9
|
||||
BTXiuRQEBy+CzSmoPyNnBCE/SOhZLH8+eGzsnlaty66AKWA1EwSjq7lDO1MOAL5Y
|
||||
dPqwk8ECgYEA51T895d707Hl2/ggpEP1Wg8p96nz2iCt903WIOLn2X/9su2mNu5r
|
||||
+Xtkd39ZYUuJIIja8hyx3q9E761jSI/F3lr4jwhTYO15aNvyD9S0supqASVodT33
|
||||
VKYxrFH2PbRfb7RyuTUjlusJeP0QFz7hZ81eooYcgqkv8Gim14Y6XgkCgYEA9w2w
|
||||
P5bTEPHweGCJ8I9AgCGUsg39x23qwN8xkxKb6jQcj6wHBpSw/yAPpAZ/1o+GFWDO
|
||||
xiiNfqc+pLHAgPwEY2iUFKKJtKaS1kFIljTK353HVrDcqviCa4GCpSlBRi4xBkfi
|
||||
vxS81eKaf8ChoiqfOuz3g6dHl6n3RGoQ8KpgUlcCgYBzbJh8AX2rdww14WyICdCW
|
||||
CxLpnEcsAzpKNvAsoIsGnzI64REaP4RoiwTqCwTR4xqcvSxhuaeWcOV4oY70Wahk
|
||||
9gcndwQDTPpTM8tn0r4Gt6gEwmGIfk62UeZfENZIm4My/Vpwxu7nEoc7cylgL+PQ
|
||||
I0yg00HOgBSHY/A7gaIF4QKBgQDwjfaQZEaOGFYCkFWf04yFdq03lmIF/qP3Oxwl
|
||||
TZhdOnKY/nM02DFjqY8xMlblz4hKZqHP1wq3SRe4+48qyLlpJhoR4ZXePdd6IcUQ
|
||||
5MSpahL/+WRUYXd0QH26Xeo98JoxuGszjXi1dljjjeiUY5X5pWT4XzhZl9i5V+G4
|
||||
xNzXLwKBgHtH/cPeR5O5gSHG+Fi5Sb/Ip6YYg00N8vtGwYYyc2i/uqz1N20igHJY
|
||||
df7D5eYRIqrhBUVxqaqqs43oa1fi7CIFITYmof+qpxzRWKq9PPFc8D9mV0/03lba
|
||||
0+i0kAvJB76WBiX48z8h+Rbc0IrZRDrVz9fk4Yfh+gHT4KDPmuII
|
||||
-----END RSA PRIVATE KEY-----
|
241
vendor/github.com/genuinetools/reg/testutils/testutils.go
generated
vendored
Normal file
241
vendor/github.com/genuinetools/reg/testutils/testutils.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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",
|
||||
},
|
||||
},
|
||||
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: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
|
||||
}
|
||||
|
||||
// RemoveContainer removes with force a container by it's container ID.
|
||||
func RemoveContainer(dcli *client.Client, ctrID string) error {
|
||||
return dcli.ContainerRemove(context.Background(), ctrID,
|
||||
types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
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
|
103
vendor/github.com/genuinetools/reg/vulns.go
generated
vendored
Normal file
103
vendor/github.com/genuinetools/reg/vulns.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/genuinetools/reg/clair"
|
||||
"github.com/genuinetools/reg/repoutils"
|
||||
"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")
|
||||
}
|
||||
|
||||
if c.Int("fixable-threshold") < 0 {
|
||||
return errors.New("fixable threshold must be a positive integer")
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue