diff --git a/Gopkg.lock b/Gopkg.lock index 03f926607..e79bc7a91 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -100,12 +100,12 @@ revision = "de5bf2ad457846296e2031421a34e2568e304e35" [[projects]] - digest = "1:69b1cc331fca23d702bd72f860c6a647afd0aa9fcbc1d0659b1365e26546dd70" - name = "github.com/Sirupsen/logrus" + branch = "master" + digest = "1:5b8a3b9e8d146a93f6d0538d3be408c9adff07fd694d4094b814a376b4727b14" + name = "github.com/Shopify/logrus-bugsnag" packages = ["."] pruneopts = "UT" - revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95" - version = "v1.2.0" + revision = "577dee27f20dd8f1a529f82210094af593be12bd" [[projects]] digest = "1:8f5416c7f59da8600725ae1ff00a99af1da8b04c211ae6f3c8f8bcab0164f650" @@ -123,6 +123,43 @@ revision = "7664702784775e51966f0885f5cd27435916517b" version = "v4" +[[projects]] + branch = "master" + digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "UT" + revision = "3a771d992973f24aa725d07868b467d1ddfceafb" + +[[projects]] + digest = "1:7b81d2ed76bf960333a8020c4b8c22abd6072f0b54ad31c66e90e6a17a19315a" + name = "github.com/bshuster-repo/logrus-logstash-hook" + packages = ["."] + pruneopts = "UT" + revision = "dbc1e22735aa6ed7bd9579a407c17bc7c4a4e046" + version = "v0.4.1" + +[[projects]] + digest = "1:7643865a2cdb66fb14d8361611a75b370a99211ac44983505b3eecb4a6a7a882" + name = "github.com/bugsnag/bugsnag-go" + packages = [ + ".", + "device", + "errors", + "headers", + "sessions", + ] + pruneopts = "UT" + revision = "109a9d63f2b57c4623f4912b256bac4a7a63517d" + +[[projects]] + digest = "1:3049c43c6d1cfaa347acd27d6342187f8f38d9f416bbba7b02b43f82848302d2" + name = "github.com/bugsnag/panicwrap" + packages = ["."] + pruneopts = "UT" + revision = "4009b2b7c78d820cc4a2e42f035bb557ce4ae45b" + version = "v1.2.0" + [[projects]] digest = "1:65b0d980b428a6ad4425f2df4cd5410edd81f044cf527bd1c345368444649e58" name = "github.com/census-instrumentation/opencensus-proto" @@ -149,6 +186,23 @@ pruneopts = "UT" revision = "bf70f2a70fb1b1f36d90d671a72795984eab0fcb" +[[projects]] + digest = "1:1872596d45cb52913fcdea90d468f3e57435959e9c0d99ccb316ee76de341313" + name = "github.com/containerd/containerd" + packages = [ + "content", + "errdefs", + "images", + "log", + "platforms", + "reference", + "remotes", + "remotes/docker", + ] + pruneopts = "UT" + revision = "9b32062dc1f5a7c2564315c269b5059754f12b9d" + version = "v1.2.1" + [[projects]] digest = "1:7cb4fdca4c251b3ef8027c90ea35f70c7b661a593b9eeae34753c65499098bb1" name = "github.com/cpuguy83/go-md2man" @@ -165,6 +219,17 @@ revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" +[[projects]] + digest = "1:8285cd51b86f5c3af447ad1db7c8572578a422cf9a50f1f07eea1d021151044f" + name = "github.com/deislabs/oras" + packages = [ + "pkg/content", + "pkg/oras", + ] + pruneopts = "UT" + revision = "e8a1fa6ff9a507b99eedd45745959e8c5b826d9f" + version = "v0.3.3" + [[projects]] digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55" name = "github.com/dgrijalva/jwt-go" @@ -174,26 +239,87 @@ version = "v3.2.0" [[projects]] - digest = "1:4ddc17aeaa82cb18c5f0a25d7c253a10682f518f4b2558a82869506eec223d76" + digest = "1:888aaacf886021e4a0fa6b09a61f1158063bd6c2e2ddefe14f3a7ccbc93ffe27" name = "github.com/docker/distribution" packages = [ + ".", + "configuration", + "context", "digestset", + "health", + "health/checks", + "manifest", + "manifest/manifestlist", + "manifest/ocischema", + "manifest/schema1", + "manifest/schema2", + "metrics", + "notifications", "reference", + "registry", + "registry/api/errcode", + "registry/api/v2", + "registry/auth", + "registry/client", + "registry/client/auth", + "registry/client/auth/challenge", + "registry/client/transport", + "registry/handlers", + "registry/listener", + "registry/middleware/registry", + "registry/middleware/repository", + "registry/proxy", + "registry/proxy/scheduler", + "registry/storage", + "registry/storage/cache", + "registry/storage/cache/memory", + "registry/storage/cache/redis", + "registry/storage/driver", + "registry/storage/driver/base", + "registry/storage/driver/factory", + "registry/storage/driver/inmemory", + "registry/storage/driver/middleware", + "uuid", + "version", ] pruneopts = "UT" revision = "40b7b5830a2337bb07627617740c0e39eb92800c" version = "v2.7.0" [[projects]] - digest = "1:53e99d883df3e940f5f0223795f300eb32b8c044f226132bfc0e74930f24ea4b" + branch = "master" + digest = "1:8da8bb2b12c31c632e96ca6f15666a36c36cd390326b6c5e1c5e309cf4b5419a" name = "github.com/docker/docker" packages = [ "pkg/term", "pkg/term/windows", ] pruneopts = "UT" - revision = "092cba3727bb9b4a2f0e922cd6c0f93ea270e363" - version = "v1.13.1" + revision = "2cb26cfe9cbf8a64c5046c74d65f4528b22e67f4" + +[[projects]] + branch = "master" + digest = "1:2b126e77be4ab4b92cdb3924c87894dd76bf365ba282f358a13133e848aa0059" + name = "github.com/docker/go-metrics" + packages = ["."] + pruneopts = "UT" + revision = "b84716841b82eab644a0c64fc8b42d480e49add5" + +[[projects]] + digest = "1:6f82cacd0af5921e99bf3f46748705239b36489464f4529a1589bc895764fb18" + name = "github.com/docker/go-units" + packages = ["."] + pruneopts = "UT" + revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" + version = "v0.3.3" + +[[projects]] + branch = "master" + digest = "1:4841e14252a2cecf11840bd05230412ad469709bbacfc12467e2ce5ad07f339b" + name = "github.com/docker/libtrust" + packages = ["."] + pruneopts = "UT" + revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20" [[projects]] branch = "master" @@ -207,12 +333,23 @@ revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85" [[projects]] - digest = "1:f1f2bd73c025d24c3b93abf6364bccb802cf2fdedaa44360804c67800e8fab8d" + digest = "1:899234af23e5793c34e06fd397f86ba33af5307b959b6a7afd19b63db065a9d7" + name = "github.com/emicklei/go-restful" + packages = [ + ".", + "log", + ] + pruneopts = "UT" + revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0" + version = "v2.8.0" + +[[projects]] + digest = "1:b48d19e79fa607e9e3715ff9a73cdd3777a9cac254b50b9af721466f687e850d" name = "github.com/evanphx/json-patch" packages = ["."] pruneopts = "UT" - revision = "72bf35d0ff611848c1dc9df0f976c81192392fa5" - version = "v4.1.0" + revision = "afac545df32f2287a079e2dfb7ba2745a643747e" + version = "v3.0.0" [[projects]] branch = "master" @@ -230,6 +367,17 @@ revision = "44e46d280b43ec1531bb25252440e34f1b800b65" version = "v1.0.0" +[[projects]] + digest = "1:0594af97b2f4cec6554086eeace6597e20a4b69466eb4ada25adf9f4300dddd2" + name = "github.com/garyburd/redigo" + packages = [ + "internal", + "redis", + ] + pruneopts = "UT" + revision = "a69d19351219b6dd56f274f96d85a7014a2ec34e" + version = "v1.6.0" + [[projects]] digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" name = "github.com/ghodss/yaml" @@ -288,15 +436,23 @@ version = "v0.2.3" [[projects]] - digest = "1:34e709f36fd4f868fb00dbaf8a6cab4c1ae685832d392874ba9d7c5dec2429d1" + digest = "1:bed9d72d596f94e65fff37f4d6c01398074a6bb1c3f3ceff963516bd01db6ff5" + name = "github.com/gofrs/uuid" + packages = ["."] + pruneopts = "UT" + revision = "6b08a5c5172ba18946672b49749cde22873dd7c2" + version = "v3.2.0" + +[[projects]] + digest = "1:b402bb9a24d108a9405a6f34675091b036c8b056aac843bf6ef2389a65c5cf48" name = "github.com/gogo/protobuf" packages = [ "proto", "sortkeys", ] pruneopts = "UT" - revision = "636bf0302bc95575d69441b25a2603156ffdddf1" - version = "v1.1.1" + revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" + version = "v1.2.0" [[projects]] branch = "master" @@ -359,7 +515,7 @@ [[projects]] branch = "master" - digest = "1:8105da6944d9227dd7c47cf290fbeafeb0b8f3b7f77a89e20b6ae4da7c71bb46" + digest = "1:d47549022c929925679aec031329f59f250d704f69ee44a194998cdb8d873393" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -371,7 +527,23 @@ "pagination", ] pruneopts = "UT" - revision = "26de66c23d78a75fc37e5dad5b6f16ffbfe9ee31" + revision = "94924357ebf6c7d448c70d65082ff7ca6f78ddc5" + +[[projects]] + digest = "1:664d37ea261f0fc73dd17f4a1f5f46d01fbb0b0d75f6375af064824424109b7d" + name = "github.com/gorilla/handlers" + packages = ["."] + pruneopts = "UT" + revision = "7e0847f9db758cdebd26c149d0ae9d5d0b9c98ce" + version = "v1.4.0" + +[[projects]] + digest = "1:ca59b1175189b3f0e9f1793d2c350114be36eaabbe5b9f554b35edee1de50aea" + name = "github.com/gorilla/mux" + packages = ["."] + pruneopts = "UT" + revision = "a7962380ca08b5a188038c69871b8d3fbdf31e89" + version = "v1.7.0" [[projects]] branch = "master" @@ -439,6 +611,14 @@ revision = "1624edc4454b8682399def8740d46db5e4362ba4" version = "v1.1.5" +[[projects]] + branch = "master" + digest = "1:caf6db28595425c0e0f2301a00257d11712f65c1878e12cffc42f6b9a9cf3f23" + name = "github.com/kardianos/osext" + packages = ["."] + pruneopts = "UT" + revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" + [[projects]] digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8" name = "github.com/konsorten/go-windows-terminal-sequences" @@ -460,12 +640,12 @@ revision = "60711f1a8329503b04e1c88535f419d0bb440bff" [[projects]] - digest = "1:cdb899c199f907ac9fb50495ec71212c95cb5b0e0a8ee0800da0238036091033" + digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7" name = "github.com/mattn/go-runewidth" packages = ["."] pruneopts = "UT" - revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb" - version = "v0.0.3" + revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3" + version = "v0.0.4" [[projects]] digest = "1:7efe48dea4db6b35dcc15e15394b627247e5b3fb814242de986b746ba8e0abf0" @@ -475,6 +655,21 @@ revision = "02e3cf038dcea8290e44424da473dd12be796a8a" version = "v1.0.3" +[[projects]] + digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "UT" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:fac4398a5e6e7a30f0e5a13c47fd95f153fa0d94be371a6c01568d56808a9791" + name = "github.com/miekg/dns" + packages = ["."] + pruneopts = "UT" + revision = "0d29b283ac0f967dd3a02739bf26a22702210d7a" + [[projects]] digest = "1:abf08734a6527df70ed361d7c369fb580e6840d8f7a6012e5f609fdfd93b4e48" name = "github.com/mitchellh/go-wordwrap" @@ -507,6 +702,17 @@ revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" version = "v1.0.0-rc1" +[[projects]] + digest = "1:11db38d694c130c800d0aefb502fb02519e514dc53d9804ce51d1ad25ec27db6" + name = "github.com/opencontainers/image-spec" + packages = [ + "specs-go", + "specs-go/v1", + ] + pruneopts = "UT" + revision = "d60099175f88c47cd379c4738d158884749ed235" + version = "v1.0.1" + [[projects]] branch = "master" digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" @@ -539,6 +745,51 @@ revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" +[[projects]] + digest = "1:93a746f1060a8acbcf69344862b2ceced80f854170e1caae089b2834c5fbf7f4" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/internal", + "prometheus/promhttp", + ] + pruneopts = "UT" + revision = "505eaef017263e299324067d40ca2c48f6a2cf50" + version = "v0.9.2" + +[[projects]] + branch = "master" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "UT" + revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" + +[[projects]] + digest = "1:35cf6bdf68db765988baa9c4f10cc5d7dda1126a54bd62e252dbcd0b1fc8da90" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "UT" + revision = "cfeb6f9992ffa54aaa4f2170ade4067ee478b250" + version = "v0.2.0" + +[[projects]] + branch = "master" + digest = "1:5833c61ebbd625a6bad8e5a1ada2b3e13710cf3272046953a2c8915340fe60a3" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/util", + "nfs", + "xfs", + ] + pruneopts = "UT" + revision = "316cf8ccfec56d206735d46333ca162eb374da8b" + [[projects]] digest = "1:b36a0ede02c4c2aef7df7f91cbbb7bb88a98b5d253509d4f997dda526e50c88c" name = "github.com/russross/blackfriday" @@ -547,6 +798,14 @@ revision = "05f3235734ad95d0016f6a23902f06461fcf567a" version = "v1.5.2" +[[projects]] + digest = "1:69b1cc331fca23d702bd72f860c6a647afd0aa9fcbc1d0659b1365e26546dd70" + name = "github.com/sirupsen/logrus" + packages = ["."] + pruneopts = "UT" + revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95" + version = "v1.2.0" + [[projects]] digest = "1:e01b05ba901239c783dfe56450bcde607fc858908529868259c9a8765dc176d0" name = "github.com/spf13/cobra" @@ -567,13 +826,47 @@ version = "v1.0.3" [[projects]] - digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83" + digest = "1:5110e3d4f130772fd39e6ce8208ad1955b242ccfcc8ad9d158857250579c82f4" name = "github.com/stretchr/testify" - packages = ["assert"] + packages = [ + "assert", + "require", + "suite", + ] pruneopts = "UT" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" +[[projects]] + digest = "1:340553b2fdaab7d53e63fd40f8ed82203bdd3274253055bdb80a46828482ef81" + name = "github.com/xenolf/lego" + packages = ["acme"] + pruneopts = "UT" + revision = "a9d8cec0e6563575e5868a005359ac97911b5985" + +[[projects]] + branch = "master" + digest = "1:dcc3219685abd95a08e219859c5f0d80ad25d365b854849d04896ccde94e2422" + name = "github.com/yvasiyarov/go-metrics" + packages = ["."] + pruneopts = "UT" + revision = "c25f46c4b94079672242ec48a545e7ca9ebe3aec" + +[[projects]] + digest = "1:c14faa3573c79c4a37d49b9081e062208ad1f27ec6e67c74c59eec45f17d9ae9" + name = "github.com/yvasiyarov/gorelic" + packages = ["."] + pruneopts = "UT" + revision = "4dc1bb7ab951bc884feb2c009092b1a454152355" + version = "v0.0.6" + +[[projects]] + digest = "1:140012d43042bb7748adc780c767abaab8cf6027fe8cb4bca5099d74e2292656" + name = "github.com/yvasiyarov/newrelic_platform_go" + packages = ["."] + pruneopts = "UT" + revision = "b21fdbd4370f3717f3bbd2bf41c223bc273068e6" + [[projects]] digest = "1:2ae8314c44cd413cfdb5b1df082b350116dd8d2fff973e62c01b285b7affd89e" name = "go.opencensus.io" @@ -600,12 +893,13 @@ [[projects]] branch = "master" - digest = "1:77b908a63f61c2e63c69b4b8f89bbecb0138899d236f656f1d255d74249531cb" + digest = "1:599ef9ff10026292c425292ab1d2bb1521cd671fe89a6034df07bf1411daa44b" name = "golang.org/x/crypto" packages = [ "cast5", "ed25519", "ed25519/internal/edwards25519", + "ocsp", "openpgp", "openpgp/armor", "openpgp/clearsign", @@ -622,20 +916,26 @@ [[projects]] branch = "master" - digest = "1:29fe5460430a338b64f4a0259a6c59a1e2350bbcff54fa66f906fa8d10515c4d" + digest = "1:647b0128e9a9886335bfb6c9a1fc97758b7f846ec42f222933f6fee6730c96e2" name = "golang.org/x/net" packages = [ + "bpf", "context", "context/ctxhttp", "http/httpguts", "http2", "http2/hpack", "idna", + "internal/iana", + "internal/socket", "internal/timeseries", + "ipv4", + "ipv6", + "publicsuffix", "trace", ] pruneopts = "UT" - revision = "610586996380ceef02dd726cc09df7e00a3f8e56" + revision = "927f97764cc334a6575f4b7a1584a147864d5723" [[projects]] branch = "master" @@ -653,22 +953,25 @@ [[projects]] branch = "master" - digest = "1:5e4d81c50cffcb124b899e4f3eabec3930c73532f0096c27f94476728ba03028" + digest = "1:04a5b0e4138f98eef79ce12a955a420ee358e9f787044cc3a553ac3c3ade997e" name = "golang.org/x/sync" - packages = ["semaphore"] + packages = [ + "errgroup", + "semaphore", + ] pruneopts = "UT" - revision = "42b317875d0fa942474b76e1b46a6060d720ae6e" + revision = "37e7f081c4d4c64e13b10787722085407fe5d15f" [[projects]] branch = "master" - digest = "1:b9dceb1408ba5105803d5859193bc7d89ac3199b611cf8681dbaa0aa09c10d9c" + digest = "1:3d5e79e10549fd9119cbefd614b6d351ef5bd0be2f2b103a4199788e784cbc68" name = "golang.org/x/sys" packages = [ "unix", "windows", ] pruneopts = "UT" - revision = "70b957f3b65e069b4930ea94e2721eefa0f8f695" + revision = "b4a75ba826a64a70990f11a225237acd6ef35c9f" [[projects]] digest = "1:28756bf526c1af662d24519f2fa7abca7237bebb06e3e02941b2b6e5b6ceb7b9" @@ -714,10 +1017,10 @@ name = "google.golang.org/api" packages = ["support/bundler"] pruneopts = "UT" - revision = "1a5ef82f9af45ef51c486291ef2b0a16d82fdb95" + revision = "65a46cafb132eff435c7d1e0f439cc73c8eebb85" [[projects]] - digest = "1:d2a8db567a76203e3b41c1f632d86485ffd57f8e650a0d1b19d240671c2fddd7" + digest = "1:fa026a5c59bd2df343ec4a3538e6288dcf4e2ec5281d743ae82c120affe6926a" name = "google.golang.org/appengine" packages = [ ".", @@ -732,8 +1035,8 @@ "urlfetch", ] pruneopts = "UT" - revision = "4a4468ece617fc8205e99368fa2200e9d1fad421" - version = "v1.3.0" + revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1" + version = "v1.4.0" [[projects]] branch = "master" @@ -741,7 +1044,7 @@ name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] pruneopts = "UT" - revision = "bd91e49a0898e27abb88c339b432fa53d7497ac0" + revision = "bd9b4fb69e2ffd37621a6caa54dcbead29b546f2" [[projects]] digest = "1:9edd250a3c46675d0679d87540b30c9ed253b19bd1fd1af08f4f5fb3c79fc487" @@ -791,6 +1094,18 @@ revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" version = "v0.9.1" +[[projects]] + digest = "1:0c9d7630c981ff09c7d297db73b3a94358e6572e8de063853c38d1e2c500272e" + name = "gopkg.in/square/go-jose.v1" + packages = [ + ".", + "cipher", + "json", + ] + pruneopts = "UT" + revision = "56062818b5e15ee405eb8363f9498c7113e98337" + version = "v1.1.2" + [[projects]] digest = "1:550221cdc42e1ad44a1942d01c4135c30f6808b61dbad3c52d325e01d8e7cc07" name = "gopkg.in/square/go-jose.v2" @@ -937,19 +1252,26 @@ "pkg/util/feature", ] pruneopts = "UT" - revision = "6e69081b521942e4f4e46a657140d81bc913df73" + revision = "c3083108fa3bda1f3da8742881d36d20330d07a3" [[projects]] branch = "master" - digest = "1:63793246976569a95e534c731e79cc555dabee6f8efa29a0b28ca33f23b7e28b" + digest = "1:79d60410b8f3339b77a36d6096cea81ebe4974b7cb8108446a121266b58bff01" name = "k8s.io/cli-runtime" packages = [ "pkg/genericclioptions", "pkg/genericclioptions/printers", "pkg/genericclioptions/resource", + "pkg/kustomize/k8sdeps", + "pkg/kustomize/k8sdeps/configmapandsecret", + "pkg/kustomize/k8sdeps/kunstruct", + "pkg/kustomize/k8sdeps/transformer", + "pkg/kustomize/k8sdeps/transformer/hash", + "pkg/kustomize/k8sdeps/transformer/patch", + "pkg/kustomize/k8sdeps/validator", ] pruneopts = "UT" - revision = "2f0d1d0a58f22eae7a0ec3d6cf01f4a122a57dae" + revision = "8abb1aeb8307ee1f2335c4331d850a232efb1cbd" [[projects]] digest = "1:ba0a20ca14958ddb44159a08daf69b6acbdc0ef99f449ec7035759e9362bc058" @@ -1088,9 +1410,10 @@ [[projects]] branch = "master" - digest = "1:4b8768c5b052c2c2a2ba468ec9392657f8bec881b5a24fd172b4d81b0eac6a55" + digest = "1:d8f05387026197d55244f5866ea172e360c3f1602bea51ba90c72a7a43ecdce6" name = "k8s.io/kube-openapi" packages = [ + "pkg/common", "pkg/util/proto", "pkg/util/proto/testing", "pkg/util/proto/validation", @@ -1222,7 +1545,7 @@ "pkg/version", ] pruneopts = "UT" - revision = "f2c8f1cadf1808ec28476682e49a3cce2b09efbf" + revision = "598a01989dcd06ec776248506fe6eb32f499bd38" [[projects]] branch = "master" @@ -1233,7 +1556,44 @@ "pointer", ] pruneopts = "UT" - revision = "0d26856f57b32ec3398579285e5c8a2bfe8c5243" + revision = "8a16e7dd8fb6d97d1331b0c79a16722f934b00b1" + +[[projects]] + digest = "1:3253435bcec387270fe70c39a4ffcb8bdc9bcc82c32d320b5546ec02a0808007" + name = "rsc.io/letsencrypt" + packages = ["."] + pruneopts = "UT" + revision = "33926faef6d434b854ea994228f11d0185faa0c1" + version = "v0.0.1" + +[[projects]] + digest = "1:443f6048f55fc9e389e8f7fa20b25bc30ef565953dd5c6425c9032432a677301" + name = "sigs.k8s.io/kustomize" + packages = [ + "pkg/commands/build", + "pkg/constants", + "pkg/expansion", + "pkg/factory", + "pkg/fs", + "pkg/gvk", + "pkg/ifc", + "pkg/ifc/transformer", + "pkg/internal/error", + "pkg/loader", + "pkg/patch", + "pkg/patch/transformer", + "pkg/resid", + "pkg/resmap", + "pkg/resource", + "pkg/target", + "pkg/transformers", + "pkg/transformers/config", + "pkg/transformers/config/defaultconfig", + "pkg/types", + ] + pruneopts = "UT" + revision = "8f701a00417a812558a7b785e8354957afa469ae" + version = "v1.0.11" [[projects]] digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" @@ -1260,22 +1620,36 @@ "github.com/Masterminds/sprig", "github.com/Masterminds/vcs", "github.com/asaskevich/govalidator", + "github.com/containerd/containerd/reference", + "github.com/containerd/containerd/remotes", + "github.com/containerd/containerd/remotes/docker", + "github.com/deislabs/oras/pkg/content", + "github.com/deislabs/oras/pkg/oras", + "github.com/docker/distribution/configuration", + "github.com/docker/distribution/registry", + "github.com/docker/distribution/registry/storage/driver/inmemory", + "github.com/docker/go-units", "github.com/evanphx/json-patch", "github.com/ghodss/yaml", "github.com/gobwas/glob", "github.com/gosuri/uitable", "github.com/gosuri/uitable/util/strutil", "github.com/mattn/go-shellwords", + "github.com/opencontainers/go-digest", + "github.com/opencontainers/image-spec/specs-go/v1", "github.com/pkg/errors", + "github.com/sirupsen/logrus", "github.com/spf13/cobra", "github.com/spf13/cobra/doc", "github.com/spf13/pflag", "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/suite", "golang.org/x/crypto/openpgp", "golang.org/x/crypto/openpgp/clearsign", "golang.org/x/crypto/openpgp/errors", "golang.org/x/crypto/openpgp/packet", "golang.org/x/crypto/ssh/terminal", + "gopkg.in/yaml.v2", "k8s.io/api/apps/v1", "k8s.io/api/apps/v1beta1", "k8s.io/api/apps/v1beta2", diff --git a/Gopkg.toml b/Gopkg.toml index b9e90934b..e835158e0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -55,9 +55,34 @@ name = "github.com/imdario/mergo" version = "v0.3.5" +[[constraint]] + name = "github.com/deislabs/oras" + version = "v0.3.3" + +[[constraint]] + name = "github.com/docker/go-units" + version = "v0.3.3" + [prune] go-tests = true unused-packages = true [[constraint]] name = "github.com/stretchr/testify" + +# These overrides below necessary for using docker/distribution as a test dependency +[[override]] + name = "github.com/yvasiyarov/newrelic_platform_go" + revision = "b21fdbd4370f3717f3bbd2bf41c223bc273068e6" + +[[override]] + name = "github.com/miekg/dns" + revision = "0d29b283ac0f967dd3a02739bf26a22702210d7a" + +[[override]] + name = "github.com/xenolf/lego" + revision = "a9d8cec0e6563575e5868a005359ac97911b5985" + +[[override]] + name = "github.com/bugsnag/bugsnag-go" + revision = "109a9d63f2b57c4623f4912b256bac4a7a63517d" \ No newline at end of file diff --git a/Makefile b/Makefile index 7ab2e485b..df582695f 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,12 @@ coverage: # dependencies .PHONY: bootstrap -bootstrap: vendor +bootstrap: vendor fix-vendor + +# fix dependecies that are incorrect +.PHONY: fix-vendor +fix-vendor: + wget -O vendor/rsc.io/letsencrypt/lets.go https://raw.githubusercontent.com/dmcgowan/letsencrypt/e770c10b0f1a64775ae91d240407ce00d1a5bdeb/lets.go $(DEP): go get -u github.com/golang/dep/cmd/dep diff --git a/cmd/helm/chart.go b/cmd/helm/chart.go new file mode 100644 index 000000000..1c13e12bd --- /dev/null +++ b/cmd/helm/chart.go @@ -0,0 +1,53 @@ +/* +Copyright The Helm Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "k8s.io/helm/pkg/action" +) + +const chartHelp = ` +This command consists of multiple subcommands to interact with charts and registries. + +It can be used to push, pull, tag, list, or remove Helm charts. +Example usage: + $ helm chart pull [URL] +` + +func newChartCmd(cfg *action.Configuration) *cobra.Command { + cmd := &cobra.Command{ + Use: "chart", + Short: "push, pull, tag, or remove Helm charts", + Long: chartHelp, + } + cmd.AddCommand( + newChartListCmd(cfg), + newChartExportCmd(cfg), + newChartPullCmd(cfg), + newChartPushCmd(cfg), + newChartRemoveCmd(cfg), + newChartSaveCmd(cfg), + ) + return cmd +} + +// TODO remove once WARN lines removed from oras or containerd +func init() { + logrus.SetLevel(logrus.ErrorLevel) +} diff --git a/cmd/helm/chart_export.go b/cmd/helm/chart_export.go new file mode 100644 index 000000000..238021299 --- /dev/null +++ b/cmd/helm/chart_export.go @@ -0,0 +1,41 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" + + "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" +) + +const chartExportDesc = ` +TODO +` + +func newChartExportCmd(cfg *action.Configuration) *cobra.Command { + return &cobra.Command{ + Use: "export [ref]", + Short: "export a chart to directory", + Long: chartExportDesc, + Args: require.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ref := args[0] + return action.NewChartExport(cfg).Run(ref) + }, + } +} diff --git a/cmd/helm/chart_list.go b/cmd/helm/chart_list.go new file mode 100644 index 000000000..6347a50a0 --- /dev/null +++ b/cmd/helm/chart_list.go @@ -0,0 +1,39 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" + + "k8s.io/helm/pkg/action" +) + +const chartListDesc = ` +TODO +` + +func newChartListCmd(cfg *action.Configuration) *cobra.Command { + return &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "list all saved charts", + Long: chartListDesc, + RunE: func(cmd *cobra.Command, args []string) error { + return action.NewChartList(cfg).Run() + }, + } +} diff --git a/cmd/helm/chart_pull.go b/cmd/helm/chart_pull.go new file mode 100644 index 000000000..70c5ddb06 --- /dev/null +++ b/cmd/helm/chart_pull.go @@ -0,0 +1,41 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" + + "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" +) + +const chartPullDesc = ` +TODO +` + +func newChartPullCmd(cfg *action.Configuration) *cobra.Command { + return &cobra.Command{ + Use: "pull [ref]", + Short: "pull a chart from remote", + Long: chartPullDesc, + Args: require.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ref := args[0] + return action.NewChartPull(cfg).Run(ref) + }, + } +} diff --git a/cmd/helm/chart_push.go b/cmd/helm/chart_push.go new file mode 100644 index 000000000..eb3776104 --- /dev/null +++ b/cmd/helm/chart_push.go @@ -0,0 +1,41 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" + + "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" +) + +const chartPushDesc = ` +TODO +` + +func newChartPushCmd(cfg *action.Configuration) *cobra.Command { + return &cobra.Command{ + Use: "push [ref]", + Short: "push a chart to remote", + Long: chartPushDesc, + Args: require.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ref := args[0] + return action.NewChartPush(cfg).Run(ref) + }, + } +} diff --git a/cmd/helm/chart_remove.go b/cmd/helm/chart_remove.go new file mode 100644 index 000000000..9609d51ae --- /dev/null +++ b/cmd/helm/chart_remove.go @@ -0,0 +1,42 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" + + "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" +) + +const chartRemoveDesc = ` +TODO +` + +func newChartRemoveCmd(cfg *action.Configuration) *cobra.Command { + return &cobra.Command{ + Use: "remove [ref]", + Aliases: []string{"rm"}, + Short: "remove a chart", + Long: chartRemoveDesc, + Args: require.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ref := args[0] + return action.NewChartRemove(cfg).Run(ref) + }, + } +} diff --git a/cmd/helm/chart_save.go b/cmd/helm/chart_save.go new file mode 100644 index 000000000..85e821796 --- /dev/null +++ b/cmd/helm/chart_save.go @@ -0,0 +1,42 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "github.com/spf13/cobra" + + "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/action" +) + +const chartSaveDesc = ` +TODO +` + +func newChartSaveCmd(cfg *action.Configuration) *cobra.Command { + return &cobra.Command{ + Use: "save [path] [ref]", + Short: "save a chart directory", + Long: chartSaveDesc, + Args: require.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + path := args[0] + ref := args[1] + return action.NewChartSave(cfg).Run(path, ref) + }, + } +} diff --git a/cmd/helm/root.go b/cmd/helm/root.go index a45f1d58d..0407a25fe 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -19,11 +19,13 @@ package main // import "k8s.io/helm/cmd/helm" import ( "io" + "github.com/containerd/containerd/remotes/docker" "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" "k8s.io/helm/pkg/action" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/registry" ) var globalUsage = `The Kubernetes package manager @@ -61,6 +63,21 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri settings.AddFlags(flags) + flags.Parse(args) + + // set defaults from environment + settings.Init(flags) + + // Add the registry client based on settings + // TODO: Move this elsewhere (first, settings.Init() must move) + actionConfig.RegistryClient = registry.NewClient(®istry.ClientOptions{ + Out: out, + Resolver: registry.Resolver{ + Resolver: docker.NewResolver(docker.ResolverOptions{}), + }, + CacheRootDir: settings.Home.Registry(), + }) + cmd.AddCommand( // chart commands newCreateCmd(out), @@ -72,6 +89,7 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri newRepoCmd(out), newSearchCmd(out), newVerifyCmd(out), + newChartCmd(actionConfig), // release commands newGetCmd(c, out), @@ -95,11 +113,6 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri newDocsCmd(out), ) - flags.Parse(args) - - // set defaults from environment - settings.Init(flags) - // Find and add plugins loadPlugins(cmd, out) diff --git a/pkg/action/action.go b/pkg/action/action.go index 7e05ac1b2..9d097788e 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -23,6 +23,7 @@ import ( "k8s.io/client-go/discovery" "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/registry" "k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/tiller/environment" ) @@ -40,9 +41,13 @@ type Configuration struct { // Releases stores records of releases. Releases *storage.Storage + // KubeClient is a Kubernetes API client. KubeClient environment.KubeClient + // RegistryClient is a client for working with registries + RegistryClient *registry.Client + Capabilities *chartutil.Capabilities Log func(string, ...interface{}) diff --git a/pkg/action/chart_export.go b/pkg/action/chart_export.go new file mode 100644 index 000000000..9625f8e87 --- /dev/null +++ b/pkg/action/chart_export.go @@ -0,0 +1,70 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "io/ioutil" + "os" + + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/registry" +) + +// ChartExport performs a chart export operation. +type ChartExport struct { + cfg *Configuration +} + +// NewChartExport creates a new ChartExport object with the given configuration. +func NewChartExport(cfg *Configuration) *ChartExport { + return &ChartExport{ + cfg: cfg, + } +} + +// Run executes the chart export operation +func (a *ChartExport) Run(ref string) error { + r, err := registry.ParseReference(ref) + if err != nil { + return err + } + + ch, err := a.cfg.RegistryClient.LoadChart(r) + if err != nil { + return err + } + + // Save the chart to local directory + // TODO: init in Helm home? Or no file creation at all? + tempDirPrefix := ".helm-chart-export" + os.MkdirAll(tempDirPrefix, 0755) + tempDir, err := ioutil.TempDir(tempDirPrefix, "") + if err != nil { + return err + } + defer os.RemoveAll(tempDir) + tarballAbsPath, err := chartutil.Save(ch, tempDir) + if err != nil { + return err + } + err = chartutil.ExpandFile("", tarballAbsPath) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/action/chart_list.go b/pkg/action/chart_list.go new file mode 100644 index 000000000..d26de578f --- /dev/null +++ b/pkg/action/chart_list.go @@ -0,0 +1,34 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +// ChartList performs a chart list operation. +type ChartList struct { + cfg *Configuration +} + +// NewChartList creates a new ChartList object with the given configuration. +func NewChartList(cfg *Configuration) *ChartList { + return &ChartList{ + cfg: cfg, + } +} + +// Run executes the chart list operation +func (a *ChartList) Run() error { + return a.cfg.RegistryClient.PrintChartTable() +} diff --git a/pkg/action/chart_pull.go b/pkg/action/chart_pull.go new file mode 100644 index 000000000..fe2de4cf3 --- /dev/null +++ b/pkg/action/chart_pull.go @@ -0,0 +1,42 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "k8s.io/helm/pkg/registry" +) + +// ChartPull performs a chart pull operation. +type ChartPull struct { + cfg *Configuration +} + +// NewChartPull creates a new ChartPull object with the given configuration. +func NewChartPull(cfg *Configuration) *ChartPull { + return &ChartPull{ + cfg: cfg, + } +} + +// Run executes the chart pull operation +func (a *ChartPull) Run(ref string) error { + r, err := registry.ParseReference(ref) + if err != nil { + return err + } + return a.cfg.RegistryClient.PullChart(r) +} diff --git a/pkg/action/chart_push.go b/pkg/action/chart_push.go new file mode 100644 index 000000000..6ff7205e6 --- /dev/null +++ b/pkg/action/chart_push.go @@ -0,0 +1,42 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "k8s.io/helm/pkg/registry" +) + +// ChartPush performs a chart push operation. +type ChartPush struct { + cfg *Configuration +} + +// NewChartPush creates a new ChartPush object with the given configuration. +func NewChartPush(cfg *Configuration) *ChartPush { + return &ChartPush{ + cfg: cfg, + } +} + +// Run executes the chart push operation +func (a *ChartPush) Run(ref string) error { + r, err := registry.ParseReference(ref) + if err != nil { + return err + } + return a.cfg.RegistryClient.PushChart(r) +} diff --git a/pkg/action/chart_remove.go b/pkg/action/chart_remove.go new file mode 100644 index 000000000..ca2dab705 --- /dev/null +++ b/pkg/action/chart_remove.go @@ -0,0 +1,42 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "k8s.io/helm/pkg/registry" +) + +// ChartRemove performs a chart remove operation. +type ChartRemove struct { + cfg *Configuration +} + +// NewChartRemove creates a new ChartRemove object with the given configuration. +func NewChartRemove(cfg *Configuration) *ChartRemove { + return &ChartRemove{ + cfg: cfg, + } +} + +// Run executes the chart remove operation +func (a *ChartRemove) Run(ref string) error { + r, err := registry.ParseReference(ref) + if err != nil { + return err + } + return a.cfg.RegistryClient.RemoveChart(r) +} diff --git a/pkg/action/chart_save.go b/pkg/action/chart_save.go new file mode 100644 index 000000000..3ced9e7a1 --- /dev/null +++ b/pkg/action/chart_save.go @@ -0,0 +1,56 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "path/filepath" + + "k8s.io/helm/pkg/chart/loader" + "k8s.io/helm/pkg/registry" +) + +// ChartSave performs a chart save operation. +type ChartSave struct { + cfg *Configuration +} + +// NewChartSave creates a new ChartSave object with the given configuration. +func NewChartSave(cfg *Configuration) *ChartSave { + return &ChartSave{ + cfg: cfg, + } +} + +// Run executes the chart save operation +func (a *ChartSave) Run(path string, ref string) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + + ch, err := loader.LoadDir(path) + if err != nil { + return err + } + + r, err := registry.ParseReference(ref) + if err != nil { + return err + } + + return a.cfg.RegistryClient.SaveChart(ch, r) +} diff --git a/pkg/helm/helmpath/helmhome.go b/pkg/helm/helmpath/helmhome.go index 6227a22dd..7641225c7 100644 --- a/pkg/helm/helmpath/helmhome.go +++ b/pkg/helm/helmpath/helmhome.go @@ -40,6 +40,11 @@ func (h Home) Path(elem ...string) string { return filepath.Join(p...) } +// Registry returns the path to the local registry cache. +func (h Home) Registry() string { + return h.Path("registry") +} + // Repository returns the path to the local repository. func (h Home) Repository() string { return h.Path("repository") diff --git a/pkg/helm/helmpath/helmhome_unix_test.go b/pkg/helm/helmpath/helmhome_unix_test.go index ff866bfc0..2017696c9 100644 --- a/pkg/helm/helmpath/helmhome_unix_test.go +++ b/pkg/helm/helmpath/helmhome_unix_test.go @@ -31,6 +31,7 @@ func TestHelmHome(t *testing.T) { } isEq(t, hh.String(), "/r") + isEq(t, hh.Registry(), "/r/registry") isEq(t, hh.Repository(), "/r/repository") isEq(t, hh.RepositoryFile(), "/r/repository/repositories.yaml") isEq(t, hh.Cache(), "/r/repository/cache") diff --git a/pkg/helm/helmpath/helmhome_windows_test.go b/pkg/helm/helmpath/helmhome_windows_test.go index b962c6298..5117578f9 100644 --- a/pkg/helm/helmpath/helmhome_windows_test.go +++ b/pkg/helm/helmpath/helmhome_windows_test.go @@ -28,6 +28,7 @@ func TestHelmHome(t *testing.T) { } isEq(t, hh.String(), "r:\\") + isEq(t, hh.Registry(), "r:\\registry") isEq(t, hh.Repository(), "r:\\repository") isEq(t, hh.RepositoryFile(), "r:\\repository\\repositories.yaml") isEq(t, hh.Cache(), "r:\\repository\\cache") diff --git a/pkg/registry/.gitignore b/pkg/registry/.gitignore new file mode 100644 index 000000000..04a480749 --- /dev/null +++ b/pkg/registry/.gitignore @@ -0,0 +1 @@ +helm-registry-test \ No newline at end of file diff --git a/pkg/registry/cache.go b/pkg/registry/cache.go new file mode 100644 index 000000000..a661e3141 --- /dev/null +++ b/pkg/registry/cache.go @@ -0,0 +1,446 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry // import "k8s.io/helm/pkg/registry" + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "time" + + orascontent "github.com/deislabs/oras/pkg/content" + "github.com/docker/go-units" + checksum "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" + "k8s.io/helm/pkg/chartutil" +) + +type ( + filesystemCache struct { + out io.Writer + rootDir string + store *orascontent.Memorystore + } +) + +func (cache *filesystemCache) LayersToChart(layers []ocispec.Descriptor) (*chart.Chart, error) { + metaLayer, contentLayer, err := extractLayers(layers) + if err != nil { + return nil, err + } + + name, version, err := extractChartNameVersionFromLayer(contentLayer) + if err != nil { + return nil, err + } + + // Obtain raw chart meta content (json) + _, metaJSONRaw, ok := cache.store.Get(metaLayer) + if !ok { + return nil, errors.New("error retrieving meta layer") + } + + // Construct chart metadata object + metadata := chart.Metadata{} + err = json.Unmarshal(metaJSONRaw, &metadata) + if err != nil { + return nil, err + } + metadata.Name = name + metadata.Version = version + + // Obtain raw chart content + _, contentRaw, ok := cache.store.Get(contentLayer) + if !ok { + return nil, errors.New("error retrieving meta layer") + } + + // Construct chart object and attach metadata + ch, err := loader.LoadArchive(bytes.NewBuffer(contentRaw)) + if err != nil { + return nil, err + } + ch.Metadata = &metadata + + return ch, nil +} + +func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descriptor, error) { + + // extract/separate the name and version from other metadata + if ch.Metadata == nil { + return nil, errors.New("chart does not contain metadata") + } + name := ch.Metadata.Name + version := ch.Metadata.Version + + // Create meta layer, clear name and version from Chart.yaml and convert to json + ch.Metadata.Name = "" + ch.Metadata.Version = "" + metaJSONRaw, err := json.Marshal(ch.Metadata) + if err != nil { + return nil, err + } + metaLayer := cache.store.Add(HelmChartMetaFileName, HelmChartMetaMediaType, metaJSONRaw) + + // Create content layer + // TODO: something better than this hack. Currently needed for chartutil.Save() + ch.Metadata = &chart.Metadata{Name: "-", Version: "-"} + destDir := mkdir(filepath.Join(cache.rootDir, "blobs", ".build")) + tmpFile, err := chartutil.Save(ch, destDir) + defer os.Remove(tmpFile) + if err != nil { + return nil, errors.Wrap(err, "failed to save") + } + contentRaw, err := ioutil.ReadFile(tmpFile) + if err != nil { + return nil, err + } + contentLayer := cache.store.Add(HelmChartContentFileName, HelmChartContentMediaType, contentRaw) + + // Set annotations + contentLayer.Annotations[HelmChartNameAnnotation] = name + contentLayer.Annotations[HelmChartVersionAnnotation] = version + + layers := []ocispec.Descriptor{metaLayer, contentLayer} + return layers, nil +} + +func (cache *filesystemCache) LoadReference(ref *Reference) ([]ocispec.Descriptor, error) { + tagDir := filepath.Join(cache.rootDir, "refs", ref.Locator, "tags", tagOrDefault(ref.Object)) + + // add meta layer + metaJSONRaw, err := getSymlinkDestContent(filepath.Join(tagDir, "meta")) + if err != nil { + return nil, err + } + metaLayer := cache.store.Add(HelmChartMetaFileName, HelmChartMetaMediaType, metaJSONRaw) + + // add content layer + contentRaw, err := getSymlinkDestContent(filepath.Join(tagDir, "content")) + if err != nil { + return nil, err + } + contentLayer := cache.store.Add(HelmChartContentFileName, HelmChartContentMediaType, contentRaw) + + // set annotations on content layer (chart name and version) + err = setLayerAnnotationsFromChartLink(contentLayer, filepath.Join(tagDir, "chart")) + if err != nil { + return nil, err + } + + layers := []ocispec.Descriptor{metaLayer, contentLayer} + return layers, nil +} + +func (cache *filesystemCache) StoreReference(ref *Reference, layers []ocispec.Descriptor) error { + tagDir := mkdir(filepath.Join(cache.rootDir, "refs", ref.Locator, "tags", tagOrDefault(ref.Object))) + + // Retrieve just the meta and content layers + metaLayer, contentLayer, err := extractLayers(layers) + if err != nil { + return err + } + + // Extract chart name and version + name, version, err := extractChartNameVersionFromLayer(contentLayer) + if err != nil { + return err + } + + // Create chart file + chartPath, err := createChartFile(filepath.Join(cache.rootDir, "charts"), name, version) + if err != nil { + return err + } + + // Create chart symlink + err = createSymlink(chartPath, filepath.Join(tagDir, "chart")) + if err != nil { + return err + } + + // Save meta blob + _, metaJSONRaw, ok := cache.store.Get(metaLayer) + if !ok { + return errors.New("error retrieving meta layer") + } + metaPath, err := createDigestFile(filepath.Join(cache.rootDir, "blobs", "meta"), metaJSONRaw) + if err != nil { + return err + } + + // Create meta symlink + err = createSymlink(metaPath, filepath.Join(tagDir, "meta")) + if err != nil { + return err + } + + // Save content blob + _, contentRaw, ok := cache.store.Get(contentLayer) + if !ok { + return errors.New("error retrieving content layer") + } + contentPath, err := createDigestFile(filepath.Join(cache.rootDir, "blobs", "content"), contentRaw) + if err != nil { + return err + } + + // Create content symlink + return createSymlink(contentPath, filepath.Join(tagDir, "content")) +} + +func (cache *filesystemCache) DeleteReference(ref *Reference) error { + tagDir := filepath.Join(cache.rootDir, "refs", ref.Locator, "tags", tagOrDefault(ref.Object)) + if _, err := os.Stat(tagDir); os.IsNotExist(err) { + return errors.New("ref not found") + } + return os.RemoveAll(tagDir) +} + +func (cache *filesystemCache) TableRows() ([][]string, error) { + return getRefsSorted(filepath.Join(cache.rootDir, "refs")) +} + +// mkdir will create a directory (no error check) and return the path +func mkdir(dir string) string { + os.MkdirAll(dir, 0755) + return dir +} + +// createSymlink creates a symbolic link, deleting existing one if exists +func createSymlink(src string, dest string) error { + os.Remove(dest) + err := os.Symlink(src, dest) + return err +} + +// getSymlinkDestContent returns the file contents of a symlink's destination +func getSymlinkDestContent(linkPath string) ([]byte, error) { + src, err := os.Readlink(linkPath) + if err != nil { + return nil, err + } + return ioutil.ReadFile(src) +} + +// setLayerAnnotationsFromChartLink will set chart name/version annotations on a layer +// based on the path of the chart link destination +func setLayerAnnotationsFromChartLink(layer ocispec.Descriptor, chartLinkPath string) error { + src, err := os.Readlink(chartLinkPath) + if err != nil { + return err + } + // example path: /some/path/charts/mychart/versions/1.2.0 + chartName := filepath.Base(filepath.Dir(filepath.Dir(src))) + chartVersion := filepath.Base(src) + layer.Annotations[HelmChartNameAnnotation] = chartName + layer.Annotations[HelmChartVersionAnnotation] = chartVersion + return nil +} + +// extractLayers obtains the meta and content layers from a list of layers +func extractLayers(layers []ocispec.Descriptor) (ocispec.Descriptor, ocispec.Descriptor, error) { + var metaLayer, contentLayer ocispec.Descriptor + + if len(layers) != 2 { + return metaLayer, contentLayer, errors.New("manifest does not contain exactly 2 layers") + } + + for _, layer := range layers { + switch layer.MediaType { + case HelmChartMetaMediaType: + metaLayer = layer + case HelmChartContentMediaType: + contentLayer = layer + } + } + + if metaLayer.Size == 0 { + return metaLayer, contentLayer, errors.New("manifest does not contain a Helm chart meta layer") + } + + if contentLayer.Size == 0 { + return metaLayer, contentLayer, errors.New("manifest does not contain a Helm chart content layer") + } + + return metaLayer, contentLayer, nil +} + +// extractChartNameVersionFromLayer retrieves the chart name and version from layer annotations +func extractChartNameVersionFromLayer(layer ocispec.Descriptor) (string, string, error) { + name, ok := layer.Annotations[HelmChartNameAnnotation] + if !ok { + return "", "", errors.New("could not find chart name in annotations") + } + version, ok := layer.Annotations[HelmChartVersionAnnotation] + if !ok { + return "", "", errors.New("could not find chart version in annotations") + } + return name, version, nil +} + +// createChartFile creates a file under "" dir which is linked to by ref +func createChartFile(chartsRootDir string, name string, version string) (string, error) { + chartPathDir := filepath.Join(chartsRootDir, name, "versions") + chartPath := filepath.Join(chartPathDir, version) + if _, err := os.Stat(chartPath); err != nil && os.IsNotExist(err) { + os.MkdirAll(chartPathDir, 0755) + err := ioutil.WriteFile(chartPath, []byte("-"), 0644) + if err != nil { + return "", err + } + } + return chartPath, nil +} + +// createDigestFile calcultaes the sha256 digest of some content and creates a file, returning the path +func createDigestFile(rootDir string, c []byte) (string, error) { + digest := checksum.FromBytes(c).String() + digestLeft, digestRight := splitDigest(digest) + pathDir := filepath.Join(rootDir, "sha256", digestLeft) + path := filepath.Join(pathDir, digestRight) + if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { + os.MkdirAll(pathDir, 0755) + err := ioutil.WriteFile(path, c, 0644) + if err != nil { + return "", err + } + } + return path, nil +} + +// splitDigest returns a sha256 digest in two parts, on with first 2 chars and one with second 62 chars +func splitDigest(digest string) (string, string) { + var digestLeft, digestRight string + digest = strings.TrimPrefix(digest, "sha256:") + if len(digest) == 64 { + digestLeft = digest[0:2] + digestRight = digest[2:64] + } + return digestLeft, digestRight +} + +// byteCountBinary produces a human-readable file size +func byteCountBinary(b int64) string { + const unit = 1024 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) +} + +// tagOrDefault returns the tag if present, if not the default tag +func tagOrDefault(tag string) string { + if tag != "" { + return tag + } + return HelmChartDefaultTag +} + +// getRefsSorted returns a map of all refs stored in a refsRootDir +func getRefsSorted(refsRootDir string) ([][]string, error) { + refsMap := map[string]map[string]string{} + + // Walk the storage dir, check for symlinks under "refs" dir pointing to valid files in "blobs/" and "charts/" + err := filepath.Walk(refsRootDir, func(path string, fileInfo os.FileInfo, fileError error) error { + + // Check if this file is a symlink + linkPath, err := os.Readlink(path) + if err == nil { + destFileInfo, err := os.Stat(linkPath) + if err == nil { + tagDir := filepath.Dir(path) + + // Determine the ref + locator := strings.TrimLeft( + strings.TrimPrefix(filepath.Dir(filepath.Dir(tagDir)), refsRootDir), "/\\") + object := filepath.Base(tagDir) + ref := fmt.Sprintf("%s:%s", locator, object) + + // Init hashmap entry if does not exist + if _, ok := refsMap[ref]; !ok { + refsMap[ref] = map[string]string{} + } + + // Add data to entry based on file name (symlink name) + base := filepath.Base(path) + switch base { + case "chart": + refsMap[ref]["name"] = filepath.Base(filepath.Dir(filepath.Dir(linkPath))) + refsMap[ref]["version"] = destFileInfo.Name() + case "content": + shaPrefix := filepath.Base(filepath.Dir(linkPath)) + digest := fmt.Sprintf("%s%s", shaPrefix, destFileInfo.Name()) + + // Make sure the filename looks like a sha256 digest (64 chars) + if len(digest) == 64 { + refsMap[ref]["digest"] = digest[:7] + refsMap[ref]["size"] = byteCountBinary(destFileInfo.Size()) + refsMap[ref]["created"] = units.HumanDuration(time.Now().UTC().Sub(destFileInfo.ModTime())) + } + } + } + } + + return nil + }) + + // Filter out any refs that are incomplete (do not have all required fields) + for k, ref := range refsMap { + allKeysFound := true + for _, v := range []string{"name", "version", "digest", "size", "created"} { + if _, ok := ref[v]; !ok { + allKeysFound = false + break + } + } + if !allKeysFound { + delete(refsMap, k) + } + } + + // Sort and convert to slice of slices + var refs [][]string + keys := make([]string, 0, len(refsMap)) + for key := range refsMap { + keys = append(keys, key) + } + sort.Strings(keys) + for _, key := range keys { + ref := refsMap[key] + ref["ref"] = key + refs = append(refs, []string{key, ref["name"], ref["version"], ref["digest"], ref["size"], ref["created"]}) + } + + return refs, err +} diff --git a/pkg/registry/client.go b/pkg/registry/client.go new file mode 100644 index 000000000..68cb5d314 --- /dev/null +++ b/pkg/registry/client.go @@ -0,0 +1,122 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry // import "k8s.io/helm/pkg/registry" + +import ( + "context" + "fmt" + "io" + + orascontent "github.com/deislabs/oras/pkg/content" + "github.com/deislabs/oras/pkg/oras" + "github.com/gosuri/uitable" + + "k8s.io/helm/pkg/chart" +) + +type ( + // ClientOptions is used to construct a new client + ClientOptions struct { + Out io.Writer + Resolver Resolver + CacheRootDir string + } + + // Client works with OCI-compliant registries and local Helm chart cache + Client struct { + out io.Writer + resolver Resolver + cache *filesystemCache // TODO: something more robust + } +) + +// NewClient returns a new registry client with config +func NewClient(options *ClientOptions) *Client { + return &Client{ + out: options.Out, + resolver: options.Resolver, + cache: &filesystemCache{ + out: options.Out, + rootDir: options.CacheRootDir, + store: orascontent.NewMemoryStore(), + }, + } +} + +// PushChart uploads a chart to a registry +func (c *Client) PushChart(ref *Reference) error { + layers, err := c.cache.LoadReference(ref) + if err != nil { + return err + } + err = oras.Push(context.Background(), c.resolver, ref.String(), c.cache.store, layers) + return err +} + +// PullChart downloads a chart from a registry +func (c *Client) PullChart(ref *Reference) error { + layers, err := oras.Pull(context.Background(), c.resolver, ref.String(), c.cache.store, KnownMediaTypes()...) + if err != nil { + return err + } + err = c.cache.StoreReference(ref, layers) + return err +} + +// SaveChart stores a copy of chart in local cache +func (c *Client) SaveChart(ch *chart.Chart, ref *Reference) error { + layers, err := c.cache.ChartToLayers(ch) + if err != nil { + return err + } + err = c.cache.StoreReference(ref, layers) + return err +} + +// LoadChart retrieves a chart object by reference +func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) { + layers, err := c.cache.LoadReference(ref) + if err != nil { + return nil, err + } + ch, err := c.cache.LayersToChart(layers) + return ch, err +} + +// RemoveChart deletes a locally saved chart +func (c *Client) RemoveChart(ref *Reference) error { + err := c.cache.DeleteReference(ref) + return err +} + +// PrintChartTable prints a list of locally stored charts +func (c *Client) PrintChartTable() error { + table := uitable.New() + table.MaxColWidth = 60 + table.AddRow("REF", "NAME", "VERSION", "DIGEST", "SIZE", "CREATED") + rows, err := c.cache.TableRows() + if err != nil { + return err + } + for _, row := range rows { + if len(row) == 6 { + table.AddRow(row[0], row[1], row[2], row[3], row[4], row[5]) + } + } + fmt.Fprintln(c.out, table.String()) + return nil +} diff --git a/pkg/registry/client_test.go b/pkg/registry/client_test.go new file mode 100644 index 000000000..db1f777f0 --- /dev/null +++ b/pkg/registry/client_test.go @@ -0,0 +1,178 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry + +import ( + "bytes" + "context" + "fmt" + "io" + "k8s.io/helm/pkg/chart" + "net" + "os" + "testing" + "time" + + "github.com/containerd/containerd/remotes/docker" + "github.com/docker/distribution/configuration" + "github.com/docker/distribution/registry" + _ "github.com/docker/distribution/registry/storage/driver/inmemory" + + "github.com/stretchr/testify/suite" +) + +type RegistryClientTestSuite struct { + suite.Suite + Out io.Writer + DockerRegistryHost string + RegistryClient *Client +} + +func (suite *RegistryClientTestSuite) SetupSuite() { + os.RemoveAll("helm-registry-test") + + // Init test client + var out bytes.Buffer + suite.Out = &out + suite.RegistryClient = NewClient(&ClientOptions{ + Out: suite.Out, + Resolver: Resolver{ + Resolver: docker.NewResolver(docker.ResolverOptions{}), + }, + CacheRootDir: "helm-registry-test", + }) + + // Registry config + config := &configuration.Configuration{} + port, err := getFreePort() + if err != nil { + suite.Nil(err, "no error finding free port for test registry") + } + suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port) + config.HTTP.Addr = fmt.Sprintf(":%d", port) + config.HTTP.DrainTimeout = time.Duration(10) * time.Second + config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} + dockerRegistry, err := registry.NewRegistry(context.Background(), config) + suite.Nil(err, "no error creating test registry") + + // Start Docker registry + go dockerRegistry.ListenAndServe() +} + +func (suite *RegistryClientTestSuite) Test_0_SaveChart() { + ref, err := ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) + suite.Nil(err) + + // empty chart + err = suite.RegistryClient.SaveChart(&chart.Chart{}, ref) + suite.NotNil(err) + + // valid chart + ch := &chart.Chart{} + ch.Metadata = &chart.Metadata{ + Name: "testchart", + Version: "1.2.3", + } + err = suite.RegistryClient.SaveChart(ch, ref) + suite.Nil(err) +} + +func (suite *RegistryClientTestSuite) Test_1_LoadChart() { + + // non-existent ref + ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) + suite.Nil(err) + ch, err := suite.RegistryClient.LoadChart(ref) + suite.NotNil(err) + + // existing ref + ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) + suite.Nil(err) + ch, err = suite.RegistryClient.LoadChart(ref) + suite.Nil(err) + suite.Equal("testchart", ch.Metadata.Name) + suite.Equal("1.2.3", ch.Metadata.Version) +} + +func (suite *RegistryClientTestSuite) Test_2_PushChart() { + + // non-existent ref + ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) + suite.Nil(err) + err = suite.RegistryClient.PushChart(ref) + suite.NotNil(err) + + // existing ref + ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) + suite.Nil(err) + err = suite.RegistryClient.PushChart(ref) + suite.Nil(err) +} + +func (suite *RegistryClientTestSuite) Test_3_PullChart() { + + // non-existent ref + ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) + suite.Nil(err) + err = suite.RegistryClient.PullChart(ref) + suite.NotNil(err) + + // existing ref + ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) + suite.Nil(err) + err = suite.RegistryClient.PullChart(ref) + suite.Nil(err) +} + +func (suite *RegistryClientTestSuite) Test_4_PrintChartTable() { + err := suite.RegistryClient.PrintChartTable() + suite.Nil(err) +} + +func (suite *RegistryClientTestSuite) Test_5_RemoveChart() { + + // non-existent ref + ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) + suite.Nil(err) + err = suite.RegistryClient.RemoveChart(ref) + suite.NotNil(err) + + // existing ref + ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) + suite.Nil(err) + err = suite.RegistryClient.RemoveChart(ref) + suite.Nil(err) +} + +func TestRegistryClientTestSuite(t *testing.T) { + suite.Run(t, new(RegistryClientTestSuite)) +} + +// borrowed from https://github.com/phayes/freeport +func getFreePort() (int, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0, err + } + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil +} diff --git a/pkg/registry/constants.go b/pkg/registry/constants.go new file mode 100644 index 000000000..a973a5ee3 --- /dev/null +++ b/pkg/registry/constants.go @@ -0,0 +1,48 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry // import "k8s.io/helm/pkg/registry" + +const ( + // HelmChartDefaultTag is the default tag used when storing a chart reference with no tag + HelmChartDefaultTag = "latest" + + // HelmChartMetaMediaType is the reserved media type for Helm chart metadata + HelmChartMetaMediaType = "application/vnd.cncf.helm.chart.meta.v1+json" + + // HelmChartContentMediaType is the reserved media type for Helm chart package content + HelmChartContentMediaType = "application/vnd.cncf.helm.chart.content.v1+tar" + + // HelmChartMetaFileName is the reserved file name for Helm chart metadata + HelmChartMetaFileName = "chart-meta.json" + + // HelmChartContentFileName is the reserved file name for Helm chart package content + HelmChartContentFileName = "chart-content.tgz" + + // HelmChartNameAnnotation is the reserved annotation key for Helm chart name + HelmChartNameAnnotation = "sh.helm.chart.name" + + // HelmChartVersionAnnotation is the reserved annotation key for Helm chart version + HelmChartVersionAnnotation = "sh.helm.chart.version" +) + +// KnownMediaTypes returns a list of layer mediaTypes that the Helm client knows about +func KnownMediaTypes() []string { + return []string{ + HelmChartMetaMediaType, + HelmChartContentMediaType, + } +} diff --git a/pkg/registry/constants_test.go b/pkg/registry/constants_test.go new file mode 100644 index 000000000..046f7b730 --- /dev/null +++ b/pkg/registry/constants_test.go @@ -0,0 +1,29 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConstants(t *testing.T) { + knownMediaTypes := KnownMediaTypes() + assert.Contains(t, knownMediaTypes, HelmChartMetaMediaType) + assert.Contains(t, knownMediaTypes, HelmChartContentMediaType) +} diff --git a/pkg/registry/reference.go b/pkg/registry/reference.go new file mode 100644 index 000000000..f2b57b48b --- /dev/null +++ b/pkg/registry/reference.go @@ -0,0 +1,45 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry // import "k8s.io/helm/pkg/registry" + +import ( + "strings" + + "github.com/containerd/containerd/reference" +) + +type ( + // Reference defines the main components of a reference specification + Reference struct { + *reference.Spec + } +) + +// ParseReference converts a string to a Reference +func ParseReference(s string) (*Reference, error) { + spec, err := reference.Parse(s) + if err != nil { + return nil, err + } + ref := Reference{&spec} + return &ref, nil +} + +// Repo returns a reference's repo minus the hostname +func (ref *Reference) Repo() string { + return strings.TrimPrefix(strings.TrimPrefix(ref.Locator, ref.Hostname()), "/") +} diff --git a/pkg/registry/reference_test.go b/pkg/registry/reference_test.go new file mode 100644 index 000000000..f4cb78862 --- /dev/null +++ b/pkg/registry/reference_test.go @@ -0,0 +1,49 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReference(t *testing.T) { + is := assert.New(t) + + // bad ref + s := "" + _, err := ParseReference(s) + is.Error(err) + + // good refs + s = "localhost:5000/mychart:latest" + ref, err := ParseReference(s) + is.NoError(err) + is.Equal("localhost:5000", ref.Hostname()) + is.Equal("mychart", ref.Repo()) + is.Equal("localhost:5000/mychart", ref.Locator) + is.Equal("latest", ref.Object) + + s = "my.host.com/my/nested/repo:1.2.3" + ref, err = ParseReference(s) + is.NoError(err) + is.Equal("my.host.com", ref.Hostname()) + is.Equal("my/nested/repo", ref.Repo()) + is.Equal("my.host.com/my/nested/repo", ref.Locator) + is.Equal("1.2.3", ref.Object) +} diff --git a/pkg/registry/resolver.go b/pkg/registry/resolver.go new file mode 100644 index 000000000..afecd454d --- /dev/null +++ b/pkg/registry/resolver.go @@ -0,0 +1,28 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package registry // import "k8s.io/helm/pkg/registry" + +import ( + "github.com/containerd/containerd/remotes" +) + +type ( + // Resolver provides remotes based on a locator + Resolver struct { + remotes.Resolver + } +)