diff --git a/Gopkg.lock b/Gopkg.lock index 7c4d43c05..9b4df9ee0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,27 +2,34 @@ [[projects]] + digest = "1:e4549155be72f065cf860ada7148bbeb0857360e81da2d5e28b799bd8720f1bc" name = "cloud.google.com/go" packages = ["compute/metadata"] + pruneopts = "T" revision = "0ebda48a7f143b1cce9eb37a8c1106ac762a3430" version = "v0.34.0" [[projects]] + digest = "1:b92928b73320648b38c93cacb9082c0fe3f8ac3383ad9bd537eef62c380e0e7a" name = "contrib.go.opencensus.io/exporter/ocagent" packages = ["."] + pruneopts = "T" revision = "00af367e65149ff1f2f4b93bbfbb84fd9297170d" version = "v0.2.0" [[projects]] branch = "master" + digest = "1:6da51e5ec493ad2b44cb04129e2d0a068c8fb9bd6cb5739d199573558696bb94" name = "github.com/Azure/go-ansiterm" packages = [ ".", - "winterm" + "winterm", ] + pruneopts = "T" revision = "d6e3b3328b783f23731bc4d058875b0371ff8109" [[projects]] + digest = "1:4827c7440869600b1e44806702a649c5055692063056e165026d46518e33db12" name = "github.com/Azure/go-autorest" packages = [ "autorest", @@ -30,184 +37,269 @@ "autorest/azure", "autorest/date", "logger", - "tracing" + "tracing", ] + pruneopts = "T" revision = "f401b1ccc8eb505927fae7a0c7f6406d37ca1c7e" version = "v11.2.8" [[projects]] + digest = "1:147748cfa709da38076c3df47f6bca6814c8ced6cba510065ec03f2120cc4819" name = "github.com/BurntSushi/toml" packages = ["."] + pruneopts = "T" revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" version = "v0.3.1" [[projects]] branch = "master" + digest = "1:414b0f57170d23e2941aa5cd393e99d0ab7a639e27d9784ef3949eae6cddfdb3" name = "github.com/MakeNowJust/heredoc" packages = ["."] + pruneopts = "T" revision = "e9091a26100e9cfb2b6a8f470085bfa541931a91" [[projects]] + digest = "1:3b10c6fd33854dc41de2cf78b7bae105da94c2789b6fa5b9ac9e593ea43484ac" name = "github.com/Masterminds/goutils" packages = ["."] + pruneopts = "T" revision = "41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0" version = "v1.1.0" [[projects]] + digest = "1:55388fd080150b9a072912f97b1f5891eb0b50df43401f8b75fb4273d3fec9fc" name = "github.com/Masterminds/semver" packages = ["."] + pruneopts = "T" revision = "c7af12943936e8c39859482e61f0574c2fd7fc75" version = "v1.4.2" [[projects]] + digest = "1:167d20f2417c3188e4f4d02a8870ac65b4d4f9fe0ac6f450873fcffa39623a37" name = "github.com/Masterminds/sprig" packages = ["."] + pruneopts = "T" revision = "258b00ffa7318e8b109a141349980ffbd30a35db" version = "v2.20.0" [[projects]] + digest = "1:74334b154a542b15a7391df3db5428675fdaa56b4d3f3de7b750289b9500d70e" name = "github.com/Masterminds/vcs" packages = ["."] + pruneopts = "T" revision = "b4f55832432b95a611cf1495272b5c8e24952a1a" version = "v1.13.0" [[projects]] + digest = "1:ef2f0eff765cd6c60594654adc602ac5ba460462ac395c6f3c144e5bea24babe" name = "github.com/Microsoft/go-winio" packages = ["."] + pruneopts = "T" revision = "1a8911d1ed007260465c3bfbbc785ac6915a0bb8" version = "v0.4.12" +[[projects]] + digest = "1:7d96aed6ffe311c3bc9f4657b887ca2b118a43e702ac4da13ef27fdf28cfc374" + name = "github.com/Microsoft/hcsshim" + packages = [ + ".", + "internal/guestrequest", + "internal/guid", + "internal/hcs", + "internal/hcserror", + "internal/hns", + "internal/interop", + "internal/logfields", + "internal/longpath", + "internal/mergemaps", + "internal/safefile", + "internal/schema1", + "internal/schema2", + "internal/timeout", + "internal/wclayer", + ] + pruneopts = "T" + revision = "f92b8fb9c92e17da496af5a69e3ee13fbe9916e1" + version = "v0.8.6" + [[projects]] branch = "master" + digest = "1:3721a10686511b80c052323423f0de17a8c06d417dbdd3b392b1578432a33aae" name = "github.com/Nvveen/Gotty" packages = ["."] + pruneopts = "T" revision = "cd527374f1e5bff4938207604a14f2e38a9cf512" [[projects]] + digest = "1:352fc094dbd1438593b64251de6788bffdf30f9925cf763c7f62e1fd27142b76" name = "github.com/PuerkitoBio/purell" packages = ["."] + pruneopts = "T" revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:c739832d67eb1e9cc478a19cc1a1ccd78df0397bf8a32978b759152e205f644b" name = "github.com/PuerkitoBio/urlesc" packages = ["."] + pruneopts = "T" revision = "de5bf2ad457846296e2031421a34e2568e304e35" [[projects]] branch = "master" + digest = "1:5b8a3b9e8d146a93f6d0538d3be408c9adff07fd694d4094b814a376b4727b14" name = "github.com/Shopify/logrus-bugsnag" packages = ["."] + pruneopts = "T" revision = "577dee27f20dd8f1a529f82210094af593be12bd" [[projects]] + digest = "1:297a3c21bf1d3b4695a222e43e982bb52b4b9e156ca2eadbe32b898d0a1ae551" name = "github.com/asaskevich/govalidator" packages = ["."] + pruneopts = "T" revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f" version = "v9" [[projects]] branch = "master" + digest = "1:ad4589ec239820ee99eb01c1ad47ebc5f8e02c4f5103a9b210adff9696d89f36" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "T" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] + digest = "1:7b81d2ed76bf960333a8020c4b8c22abd6072f0b54ad31c66e90e6a17a19315a" name = "github.com/bshuster-repo/logrus-logstash-hook" packages = ["."] + pruneopts = "T" revision = "dbc1e22735aa6ed7bd9579a407c17bc7c4a4e046" version = "v0.4.1" [[projects]] + digest = "1:f18852f146423bf4c60dcd2f84e8819cfeabac15a875d7ea49daddb2d021f9d5" name = "github.com/bugsnag/bugsnag-go" packages = [ ".", - "errors" + "errors", ] + pruneopts = "T" revision = "3f5889f222e9c07aa1f62c5e8c202e402ce574cd" version = "v1.3.2" [[projects]] + digest = "1:3049c43c6d1cfaa347acd27d6342187f8f38d9f416bbba7b02b43f82848302d2" name = "github.com/bugsnag/panicwrap" packages = ["."] + pruneopts = "T" revision = "4009b2b7c78d820cc4a2e42f035bb557ce4ae45b" version = "v1.2.0" [[projects]] + digest = "1:0b2d5839372f6dc106fcaa70b6bd5832789a633c4e470540f76c2ec6c560e1c1" name = "github.com/census-instrumentation/opencensus-proto" packages = [ "gen-go/agent/common/v1", "gen-go/agent/trace/v1", "gen-go/resource/v1", - "gen-go/trace/v1" + "gen-go/trace/v1", ] + pruneopts = "T" revision = "7f2434bc10da710debe5c4315ed6d4df454b4024" version = "v0.1.0" [[projects]] + digest = "1:b5f139796b532342966b835fb26fe41b6b488e94b914f1af1aba4cd3a9fee6dc" name = "github.com/containerd/containerd" packages = [ "content", + "content/local", "errdefs", + "filters", "images", "log", "platforms", "reference", "remotes", - "remotes/docker" + "remotes/docker", + "sys", ] + pruneopts = "T" revision = "894b81a4b802e4eb2a91d1ce216b8817763c29fb" version = "v1.2.6" [[projects]] branch = "master" + digest = "1:1271f7f8cc5f5b2eb0c683f92c7adf8fca1813b9da5218d6df1c9cf4bdc3f8d5" name = "github.com/containerd/continuity" - packages = ["pathdriver"] + packages = [ + ".", + "devices", + "driver", + "pathdriver", + "proto", + "syscallx", + "sysx", + ] + pruneopts = "T" revision = "004b46473808b3e7a4a3049c20e4376c91eb966d" [[projects]] + digest = "1:607c4f1f646bfe5ec1a4eac4a505608f280829550ed546a243698a525d3c5fe8" name = "github.com/cpuguy83/go-md2man" packages = ["md2man"] + pruneopts = "T" revision = "20f5889cbdc3c73dbd2862796665e7c465ade7d1" version = "v1.0.8" [[projects]] + digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "T" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" [[projects]] + digest = "1:543d5301a51341bbdaba9ddf14ae52ffcc8302fc5e79b01f1954c31f02a4aac5" name = "github.com/deislabs/oras" packages = [ "pkg/auth", "pkg/auth/docker", "pkg/content", "pkg/context", - "pkg/oras" + "pkg/oras", ] - revision = "b3b6ce7eeb31a5c0d891d33f585c84ae3dcc9046" - version = "v0.5.0" + pruneopts = "T" + revision = "b285197778e05cd348abb8ff50faf0ef7e3554a2" + version = "v0.6.0" [[projects]] + digest = "1:6b014c67cb522566c30ef02116f9acb50cd60954708cf92c6654e2985696db18" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "T" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] + digest = "1:82905edd0b7a5bca3774725e162e1befecd500cd95df2c9909358d4835d36310" name = "github.com/docker/cli" packages = [ "cli/config", "cli/config/configfile", "cli/config/credentials", - "cli/config/types" + "cli/config/types", ] + pruneopts = "T" revision = "f28d9cc92972044feb72ab6833699102992d40a2" version = "v19.03.0-beta3" [[projects]] + digest = "1:bd3ffa8395e5f3cee7a1d38a7f4de0df088301e25c4e6910fc53936c4be09278" name = "github.com/docker/distribution" packages = [ ".", @@ -249,13 +341,15 @@ "registry/storage/driver/inmemory", "registry/storage/driver/middleware", "uuid", - "version" + "version", ] + pruneopts = "T" revision = "40b7b5830a2337bb07627617740c0e39eb92800c" version = "v2.7.0" [[projects]] branch = "master" + digest = "1:3d23e50eab6b3aa4ced1b1cc8b5c40534b9c9a54b0f5648a2e76d72599134e4e" name = "github.com/docker/docker" packages = [ "api/types", @@ -282,123 +376,157 @@ "pkg/term", "pkg/term/windows", "registry", - "registry/resumable" + "registry/resumable", ] + pruneopts = "T" revision = "2cb26cfe9cbf8a64c5046c74d65f4528b22e67f4" [[projects]] + digest = "1:523611f6876df8a1fd1aea07499e6ae33585238e8fdd8793f48a2441438a12d6" name = "github.com/docker/docker-credential-helpers" packages = [ "client", - "credentials" + "credentials", ] + pruneopts = "T" revision = "5241b46610f2491efdf9d1c85f1ddf5b02f6d962" version = "v0.6.1" [[projects]] + digest = "1:b64eea95d41af3792092af9c951efcd2d8d8bfd2335c851f7afaf54d6be12c66" name = "github.com/docker/go-connections" packages = [ "nat", "sockets", - "tlsconfig" + "tlsconfig", ] + pruneopts = "T" revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55" version = "v0.4.0" [[projects]] branch = "master" + digest = "1:2b126e77be4ab4b92cdb3924c87894dd76bf365ba282f358a13133e848aa0059" name = "github.com/docker/go-metrics" packages = ["."] + pruneopts = "T" revision = "b84716841b82eab644a0c64fc8b42d480e49add5" [[projects]] + digest = "1:6f82cacd0af5921e99bf3f46748705239b36489464f4529a1589bc895764fb18" name = "github.com/docker/go-units" packages = ["."] + pruneopts = "T" revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" version = "v0.3.3" [[projects]] branch = "master" + digest = "1:46cb138b11721830161dd8293356e3dc5dd7ec774825b7fc5f2bf2fbbf2bed33" name = "github.com/docker/libtrust" packages = ["."] + pruneopts = "T" revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20" [[projects]] branch = "master" + digest = "1:0d4540d92fd82f9957e1f718e2b1e5f2d301ed8169e2923bba23558fbbbd08a1" name = "github.com/docker/spdystream" packages = [ ".", - "spdy" + "spdy", ] + pruneopts = "T" revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85" [[projects]] + digest = "1:0ffd93121f3971aea43f6a26b3eaaa64c8af20fb0ff0731087d8dab7164af5a8" name = "github.com/emicklei/go-restful" packages = [ ".", - "log" + "log", ] + pruneopts = "T" revision = "3eb9738c1697594ea6e71a7156a9bb32ed216cf0" version = "v2.8.0" [[projects]] + digest = "1:75c3d1e7907ed7a800afd0569783a99a2b6421b634c404565de537b224826703" name = "github.com/evanphx/json-patch" packages = ["."] + pruneopts = "T" revision = "afac545df32f2287a079e2dfb7ba2745a643747e" version = "v3.0.0" [[projects]] branch = "master" + digest = "1:5e0da1aba1a7b125f46e6ddca43e98b40cf6eaea3322b016c331cf6afe53c30a" name = "github.com/exponent-io/jsonpath" packages = ["."] + pruneopts = "T" revision = "d6023ce2651d8eafb5c75bb0c7167536102ec9f5" [[projects]] + digest = "1:bbc4aacabe6880bdbce849c64cb061b7eddf39f132af4ea2853ddd32f85fbec3" name = "github.com/fatih/camelcase" packages = ["."] + pruneopts = "T" revision = "44e46d280b43ec1531bb25252440e34f1b800b65" version = "v1.0.0" [[projects]] + digest = "1:30c81df6bc8e5518535aee2b1eacb1a9dab172ee608eeadb40f4db30f007027e" name = "github.com/garyburd/redigo" packages = [ "internal", - "redis" + "redis", ] + pruneopts = "T" revision = "a69d19351219b6dd56f274f96d85a7014a2ec34e" version = "v1.6.0" [[projects]] + digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" name = "github.com/ghodss/yaml" packages = ["."] + pruneopts = "T" revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" version = "v1.0.0" [[projects]] + digest = "1:701ec53dfa0182bf25e5c09e664906f11d697e779b59461a2607dbd4dc75a4f9" name = "github.com/go-openapi/jsonpointer" packages = ["."] + pruneopts = "T" revision = "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004" version = "v0.17.2" [[projects]] + digest = "1:3f17ebd557845adeb347c9e398394e96ebc18e0ec94cc04972be87851a4679e0" name = "github.com/go-openapi/jsonreference" packages = ["."] + pruneopts = "T" revision = "8483a886a90412cd6858df4ea3483dce9c8e35a3" version = "v0.17.2" [[projects]] + digest = "1:76b8b440ca412e287dff607469a5a40a9445fe7168ad1fb85916d87c66011c83" name = "github.com/go-openapi/spec" packages = ["."] + pruneopts = "T" revision = "5bae59e25b21498baea7f9d46e9c147ec106a42e" version = "v0.17.2" [[projects]] + digest = "1:0d8057a212a27a625bb8e57b1e25fb8e8e4a0feb0b7df543fd46d8d15c31d870" name = "github.com/go-openapi/swag" packages = ["."] + pruneopts = "T" revision = "5899d5c5e619fda5fa86e14795a835f473ca284c" version = "v0.17.2" [[projects]] + digest = "1:0a5d2a670ac050354afcf572e65aceabefdebdbb90973ea729d8640fa211a9e2" name = "github.com/gobwas/glob" packages = [ ".", @@ -408,27 +536,33 @@ "syntax/ast", "syntax/lexer", "util/runes", - "util/strings" + "util/strings", ] + pruneopts = "T" revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" version = "v0.2.3" [[projects]] + digest = "1:f5ccd717b5f093cbabc51ee2e7a5979b92f17d217f9031d6d64f337101c408e4" name = "github.com/gogo/protobuf" packages = [ "proto", - "sortkeys" + "sortkeys", ] + pruneopts = "T" revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:8f3489cb7352125027252a6517757cbd1706523119f1e14e20741ae8d2f70428" name = "github.com/golang/groupcache" packages = ["lru"] + pruneopts = "T" revision = "c65c006176ff7ff98bb916961c7abbc6b0afc0aa" [[projects]] + digest = "1:a2ecb56e5053d942aafc86738915fb94c9131bac848c543b8b6764365fd69080" name = "github.com/golang/protobuf" packages = [ "proto", @@ -436,53 +570,65 @@ "ptypes/any", "ptypes/duration", "ptypes/timestamp", - "ptypes/wrappers" + "ptypes/wrappers", ] + pruneopts = "T" revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:0bfbe13936953a98ae3cfe8ed6670d396ad81edf069a806d2f6515d7bb6950df" name = "github.com/google/btree" packages = ["."] + pruneopts = "T" revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" [[projects]] + digest = "1:ec7c114271a6226a146c64cb0d95348ac85350fde7dbb2564a1911165aa63ced" name = "github.com/google/go-cmp" packages = [ "cmp", "cmp/internal/diff", "cmp/internal/flags", "cmp/internal/function", - "cmp/internal/value" + "cmp/internal/value", ] + pruneopts = "T" revision = "6f77996f0c42f7b84e5a2b252227263f93432e9b" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:3ee90c0d94da31b442dde97c99635aaafec68d0b8a3c12ee2075c6bdabeec6bb" name = "github.com/google/gofuzz" packages = ["."] + pruneopts = "T" revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" [[projects]] + digest = "1:236d7e1bdb50d8f68559af37dbcf9d142d56b431c9b2176d41e2a009b664cda8" name = "github.com/google/uuid" packages = ["."] + pruneopts = "T" revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8" version = "v1.1.0" [[projects]] + digest = "1:35735e2255fa34521c2a1355fb2a3a2300bc9949f487be1c1ce8ee8efcfa2d04" name = "github.com/googleapis/gnostic" packages = [ "OpenAPIv2", "compiler", - "extensions" + "extensions", ] + pruneopts = "T" revision = "7c663266750e7d82587642f65e60bc4083f1f84e" version = "v0.2.0" [[projects]] branch = "master" + digest = "1:115dd91e62130f4751ab7bf3f9e892bc3b46670a99d5f680128082fe470cbcf4" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -491,297 +637,386 @@ "openstack/identity/v2/tokens", "openstack/identity/v3/tokens", "openstack/utils", - "pagination" + "pagination", ] + pruneopts = "T" revision = "94924357ebf6c7d448c70d65082ff7ca6f78ddc5" [[projects]] + digest = "1:664d37ea261f0fc73dd17f4a1f5f46d01fbb0b0d75f6375af064824424109b7d" name = "github.com/gorilla/handlers" packages = ["."] + pruneopts = "T" revision = "7e0847f9db758cdebd26c149d0ae9d5d0b9c98ce" version = "v1.4.0" [[projects]] + digest = "1:03e234a7f71e1bab87804517e5f729b4cc3534cabd2b7cc692052282f6215192" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "T" revision = "a7962380ca08b5a188038c69871b8d3fbdf31e89" version = "v1.7.0" [[projects]] branch = "master" + digest = "1:fae5efc46b655e70e982e4d8b6af5a829333bc98edfaa91dde5ac608f142c00c" name = "github.com/gosuri/uitable" packages = [ ".", "util/strutil", - "util/wordwrap" + "util/wordwrap", ] + pruneopts = "T" revision = "36ee7e946282a3fb1cfecd476ddc9b35d8847e42" [[projects]] branch = "master" + digest = "1:8c0ceab65d43f49dce22aac0e8f670c170fc74dcf2dfba66d3a89516f7ae2c15" name = "github.com/gregjones/httpcache" packages = [ ".", - "diskcache" + "diskcache", ] + pruneopts = "T" revision = "c63ab54fda8f77302f8d414e19933f2b6026a089" [[projects]] + digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5" name = "github.com/hashicorp/golang-lru" packages = [ ".", - "simplelru" + "simplelru", ] + pruneopts = "T" revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" version = "v0.5.0" [[projects]] + digest = "1:f9a5e090336881be43cfc1cf468330c1bdd60abdc9dd194e0b1ab69f4b94dd7c" name = "github.com/huandu/xstrings" packages = ["."] + pruneopts = "T" revision = "f02667b379e2fb5916c3cda2cf31e0eb885d79f8" version = "v1.2.0" [[projects]] + digest = "1:3477d9dd8c135faab978bac762eaeafb31f28d6da97ef500d5c271966f74140a" name = "github.com/imdario/mergo" packages = ["."] + pruneopts = "T" revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" version = "v0.3.6" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "T" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] + digest = "1:5d713dbcad44f3358fec51fd5573d4f733c02cac5a40dcb177787ad5ffe9272f" name = "github.com/json-iterator/go" packages = ["."] + pruneopts = "T" revision = "1624edc4454b8682399def8740d46db5e4362ba4" version = "v1.1.5" [[projects]] branch = "master" + digest = "1:caf6db28595425c0e0f2301a00257d11712f65c1878e12cffc42f6b9a9cf3f23" name = "github.com/kardianos/osext" packages = ["."] + pruneopts = "T" revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" [[projects]] + digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8" name = "github.com/konsorten/go-windows-terminal-sequences" packages = ["."] + pruneopts = "T" revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242" version = "v1.0.1" [[projects]] branch = "master" + digest = "1:4be65cb3a11626a0d89fc72b34f62a8768040512d45feb086184ac30fdfbef65" name = "github.com/mailru/easyjson" packages = [ "buffer", "jlexer", - "jwriter" + "jwriter", ] + pruneopts = "T" revision = "60711f1a8329503b04e1c88535f419d0bb440bff" [[projects]] + digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7" name = "github.com/mattn/go-runewidth" packages = ["."] + pruneopts = "T" revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3" version = "v0.0.4" [[projects]] + digest = "1:7efe48dea4db6b35dcc15e15394b627247e5b3fb814242de986b746ba8e0abf0" name = "github.com/mattn/go-shellwords" packages = ["."] + pruneopts = "T" revision = "02e3cf038dcea8290e44424da473dd12be796a8a" version = "v1.0.3" [[projects]] + digest = "1:a8e3d14801bed585908d130ebfc3b925ba642208e6f30d879437ddfc7bb9b413" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "T" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] + digest = "1:ceb81990372dadfe39e96b9b3df793d4838bbc21cfa02d2f34e7fcbbed227f37" name = "github.com/miekg/dns" packages = ["."] + pruneopts = "T" revision = "0d29b283ac0f967dd3a02739bf26a22702210d7a" [[projects]] + digest = "1:abf08734a6527df70ed361d7c369fb580e6840d8f7a6012e5f609fdfd93b4e48" name = "github.com/mitchellh/go-wordwrap" packages = ["."] + pruneopts = "T" revision = "9e67c67572bc5dd02aef930e2b0ae3c02a4b5a5c" version = "v1.0.0" [[projects]] + digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" name = "github.com/modern-go/concurrent" packages = ["."] + pruneopts = "T" revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" version = "1.0.3" [[projects]] + digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" name = "github.com/modern-go/reflect2" packages = ["."] + pruneopts = "T" revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" version = "1.0.1" [[projects]] + digest = "1:ee4d4af67d93cc7644157882329023ce9a7bcfce956a079069a9405521c7cc8d" name = "github.com/opencontainers/go-digest" packages = ["."] + pruneopts = "T" revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" version = "v1.0.0-rc1" [[projects]] + digest = "1:eb47da2fdabb69f64ce3a42a1790ec0ed9da6718c8378f3fdc41cbe6af184519" name = "github.com/opencontainers/image-spec" packages = [ "specs-go", - "specs-go/v1" + "specs-go/v1", ] + pruneopts = "T" revision = "d60099175f88c47cd379c4738d158884749ed235" version = "v1.0.1" [[projects]] + digest = "1:52254f0d6ce1358972c08cb1ecd91a449af3a7f927f829abec962613fb167403" name = "github.com/opencontainers/runc" - packages = ["libcontainer/user"] + packages = [ + "libcontainer/system", + "libcontainer/user", + ] + pruneopts = "T" revision = "baf6536d6259209c3edfa2b22237af82942d3dfa" version = "v0.1.1" [[projects]] branch = "master" + digest = "1:0c29d499ffc3b9f33e7136444575527d0c3a9463a89b3cbeda0523b737f910b3" name = "github.com/petar/GoLLRB" packages = ["llrb"] + pruneopts = "T" revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" [[projects]] + digest = "1:598241bd36d3a5f6d9102a306bd9bf78f3bc253672460d92ac70566157eae648" name = "github.com/peterbourgon/diskv" packages = ["."] + pruneopts = "T" revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" [[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "T" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:22aa691fe0213cb5c07d103f9effebcb7ad04bee45a0ce5fe5369d0ca2ec3a1f" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "T" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:3b5729e3fc486abc6fc16ce026331c3d196e788c3b973081ecf5d28ae3e1050d" name = "github.com/prometheus/client_golang" packages = [ "prometheus", "prometheus/internal", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "T" revision = "505eaef017263e299324067d40ca2c48f6a2cf50" version = "v0.9.2" [[projects]] branch = "master" + digest = "1:cd67319ee7536399990c4b00fae07c3413035a53193c644549a676091507cadc" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "T" revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" [[projects]] + digest = "1:1688d03416102590c2a93f23d44e4297cb438bff146830aae35e66b33c9af114" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model" + "model", ] + pruneopts = "T" revision = "cfeb6f9992ffa54aaa4f2170ade4067ee478b250" version = "v0.2.0" [[projects]] branch = "master" + digest = "1:eb94fa4ad4d1e3c7a084f6f19d60a7dbafa3194147655e2b5db14e8bc9dcef74" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "T" revision = "316cf8ccfec56d206735d46333ca162eb374da8b" [[projects]] + digest = "1:40e527269f1feb16b3069bfe80ff05a462d190eacfe07eb0a59fa25c381db7af" name = "github.com/russross/blackfriday" packages = ["."] + pruneopts = "T" revision = "05f3235734ad95d0016f6a23902f06461fcf567a" version = "v1.5.2" [[projects]] + digest = "1:c3498d1186a4f84897812aa2dccfbd5d805955846f2cd020aa384bf0b218e9e9" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "T" revision = "8bdbc7bcc01dcbb8ec23dc8a28e332258d25251f" version = "v1.4.1" [[projects]] + digest = "1:674fedb5641490b913f0f01e4f97f3f578f7a1c5f106cd47cfd5394eca8155db" name = "github.com/spf13/cobra" packages = [ ".", - "doc" + "doc", ] + pruneopts = "T" revision = "67fc4837d267bc9bfd6e47f77783fcc3dffc68de" version = "v0.0.4" [[projects]] + digest = "1:0f775ea7a72e30d5574267692aaa9ff265aafd15214a7ae7db26bc77f2ca04dc" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "T" revision = "298182f68c66c05229eb03ac171abe6e309ee79a" version = "v1.0.3" [[projects]] + digest = "1:17c4ccf5cdb1627aaaeb5c1725cb13aec97b63ea2033d4a6824dcaedf94223dc" name = "github.com/stretchr/testify" packages = [ "assert", "require", - "suite" + "suite", ] + pruneopts = "T" revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" version = "v1.3.0" [[projects]] branch = "master" + digest = "1:f4e5276a3b356f4692107047fd2890f2fe534f4feeb6b1fd2f6dfbd87f1ccf54" name = "github.com/xeipuuv/gojsonpointer" packages = ["."] + pruneopts = "T" revision = "4e3ac2762d5f479393488629ee9370b50873b3a6" [[projects]] branch = "master" + digest = "1:dc6a6c28ca45d38cfce9f7cb61681ee38c5b99ec1425339bfc1e1a7ba769c807" name = "github.com/xeipuuv/gojsonreference" packages = ["."] + pruneopts = "T" revision = "bd5ef7bd5415a7ac448318e64f11a24cd21e594b" [[projects]] + digest = "1:6d01aadbf9c582bc90c520707fcab1e63af19649218d785c49d6aa561c8948a8" name = "github.com/xeipuuv/gojsonschema" packages = ["."] + pruneopts = "T" revision = "f971f3cd73b2899de6923801c147f075263e0c50" version = "v1.1.0" [[projects]] + digest = "1:89d64b91ff6c1ede3cbc3f3ff6ac101977ee5e56547aebb85af5504adbdb4c63" name = "github.com/xenolf/lego" packages = ["acme"] + pruneopts = "T" revision = "a9d8cec0e6563575e5868a005359ac97911b5985" [[projects]] branch = "master" + digest = "1:f5abbb52b11b97269d74b7ebf9e057e8e34d477eedd171741fe21cc190bedb02" name = "github.com/yvasiyarov/go-metrics" packages = ["."] + pruneopts = "T" revision = "c25f46c4b94079672242ec48a545e7ca9ebe3aec" [[projects]] + digest = "1:6ce16ef7a4c7f60d648676d2c1fbf142c6dbdb96624743472078260150d519c2" name = "github.com/yvasiyarov/gorelic" packages = ["."] + pruneopts = "T" revision = "4dc1bb7ab951bc884feb2c009092b1a454152355" version = "v0.0.6" [[projects]] + digest = "1:c20b060d66ecf0fe4dfbef1bc7d314b352289b8f6cd37069abb5ba6a0f8e0b91" name = "github.com/yvasiyarov/newrelic_platform_go" packages = ["."] + pruneopts = "T" revision = "b21fdbd4370f3717f3bbd2bf41c223bc273068e6" [[projects]] + digest = "1:b6f574d818cb549185939ce3c228983b339eeba4aee229c74c5848ff9e806836" name = "go.opencensus.io" packages = [ ".", @@ -798,13 +1033,15 @@ "trace", "trace/internal", "trace/propagation", - "trace/tracestate" + "trace/tracestate", ] + pruneopts = "T" revision = "b7bf3cdb64150a8c8c53b769fdeb2ba581bd4d4b" version = "v0.18.0" [[projects]] branch = "master" + digest = "1:d470cb69884835b1800e93ceceb85afcf981ea647e61d99398a76af7a95bad6a" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -822,12 +1059,14 @@ "openpgp/s2k", "pbkdf2", "scrypt", - "ssh/terminal" + "ssh/terminal", ] + pruneopts = "T" revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" [[projects]] branch = "master" + digest = "1:6a668f89e7e121bf970a6dc37c729f05f5261ae64e27945326d896ad158e5a10" name = "golang.org/x/net" packages = [ "bpf", @@ -845,41 +1084,49 @@ "ipv6", "proxy", "publicsuffix", - "trace" + "trace", ] + pruneopts = "T" revision = "927f97764cc334a6575f4b7a1584a147864d5723" [[projects]] branch = "master" + digest = "1:320e5ba9ea8000060bec710764b8b26c251ee28f6012422b669cb8cb100c9815" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] + pruneopts = "T" revision = "d668ce993890a79bda886613ee587a69dd5da7a6" [[projects]] branch = "master" + digest = "1:6932d1ef4294f3ea819748e89d1003f5df3804b20b84b5f1c60f8f1d7c933e2d" name = "golang.org/x/sync" packages = [ "errgroup", - "semaphore" + "semaphore", ] + pruneopts = "T" revision = "37e7f081c4d4c64e13b10787722085407fe5d15f" [[projects]] branch = "master" + digest = "1:50eb9e3f847dc29971fecac71bf84a32e9d756dd34216cf9219c50bd3801b4c4" name = "golang.org/x/sys" packages = [ "unix", - "windows" + "windows", ] + pruneopts = "T" revision = "b4a75ba826a64a70990f11a225237acd6ef35c9f" [[projects]] + digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a" name = "golang.org/x/text" packages = [ "collate", @@ -902,24 +1149,30 @@ "unicode/cldr", "unicode/norm", "unicode/rangetable", - "width" + "width", ] + pruneopts = "T" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:077216d94c076b8cd7bd057cb6f7c6d224970cc991bdfe49c0c7a24e8e39ee33" name = "golang.org/x/time" packages = ["rate"] + pruneopts = "T" revision = "85acf8d2951cb2a3bde7632f9ff273ef0379bcbd" [[projects]] branch = "master" + digest = "1:9eaf0fc3f9a9b24531d89e1e0adf916e0d3f5ac7d3ce61f520af19212b1798b0" name = "google.golang.org/api" packages = ["support/bundler"] + pruneopts = "T" revision = "65a46cafb132eff435c7d1e0f439cc73c8eebb85" [[projects]] + digest = "1:1469235a5a8e192cfe6a99c4804b883a02f0ff96a693cd1660515a3a3b94d5ac" name = "google.golang.org/appengine" packages = [ ".", @@ -931,18 +1184,22 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch" + "urlfetch", ] + pruneopts = "T" revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1" version = "v1.4.0" [[projects]] branch = "master" + digest = "1:8c7bf8f974d0b63a83834e83b6dd39c2b40d61d409d76172c81d67eba8fee4a8" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "T" revision = "bd9b4fb69e2ffd37621a6caa54dcbead29b546f2" [[projects]] + digest = "1:c32026b4d2b9f7240ad72edf0dffca4e5863d5edc1ea25416d64929926b5ac67" name = "google.golang.org/grpc" packages = [ ".", @@ -975,50 +1232,60 @@ "resolver/passthrough", "stats", "status", - "tap" + "tap", ] + pruneopts = "T" revision = "df014850f6dee74ba2fc94874043a9f3f75fbfd8" version = "v1.17.0" [[projects]] + digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" name = "gopkg.in/inf.v0" packages = ["."] + pruneopts = "T" revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" source = "https://github.com/go-inf/inf.git" version = "v0.9.1" [[projects]] + digest = "1:12f4009e9a437d974387eaf60699ac6a401d146fe8560b01c16478c629af59b4" name = "gopkg.in/square/go-jose.v1" packages = [ ".", "cipher", - "json" + "json", ] + pruneopts = "T" revision = "56062818b5e15ee405eb8363f9498c7113e98337" source = "https://github.com/square/go-jose.git" version = "v1.1.2" [[projects]] + digest = "1:3effe4e6f8af2d27c9cd6070c7c332344490cbeeaa5476ae2bc9e05eb1079f0c" name = "gopkg.in/square/go-jose.v2" packages = [ ".", "cipher", "json", - "jwt" + "jwt", ] + pruneopts = "T" revision = "628223f44a71f715d2881ea69afc795a1e9c01be" source = "https://github.com/square/go-jose.git" version = "v2.3.0" [[projects]] + digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "T" revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" source = "https://github.com/go-yaml/yaml" version = "v2.2.2" [[projects]] branch = "release-1.15" + digest = "1:0f34ccf9357fb875ee20b8ba48a240576dfbb1a247d0f1c763f2fec6e93d2e32" name = "k8s.io/api" packages = [ "admission/v1beta1", @@ -1058,18 +1325,22 @@ "settings/v1alpha1", "storage/v1", "storage/v1alpha1", - "storage/v1beta1" + "storage/v1beta1", ] + pruneopts = "T" revision = "1634385ce4626e4da21367d139c4ee5d72437e3b" [[projects]] branch = "release-1.15" + digest = "1:dffbde7aabb4d8c613f9dd53317fd5b3aa0b2722cd4d7159772be68637116793" name = "k8s.io/apiextensions-apiserver" packages = ["pkg/features"] + pruneopts = "T" revision = "23f08c7096c0273b53178de488b95473d5cd3808" [[projects]] branch = "release-1.15" + digest = "1:2656a0f23465fb97265dd7dc176fada8e954dd191f198aa32e6bb52597514aa4" name = "k8s.io/apimachinery" packages = [ "pkg/api/equality", @@ -1124,24 +1395,28 @@ "pkg/watch", "third_party/forked/golang/json", "third_party/forked/golang/netutil", - "third_party/forked/golang/reflect" + "third_party/forked/golang/reflect", ] + pruneopts = "T" revision = "1799e75a07195de9460b8ef7300883499f12127b" [[projects]] branch = "release-1.15" + digest = "1:9e18a8310252d4101ea877fb517b52ca76975742065d86e9cf525f3ddda38b7e" name = "k8s.io/apiserver" packages = [ "pkg/authentication/authenticator", "pkg/authentication/serviceaccount", "pkg/authentication/user", "pkg/features", - "pkg/util/feature" + "pkg/util/feature", ] + pruneopts = "T" revision = "07da2c5601ffacb40aecb4ad92adea2c775d1dd9" [[projects]] branch = "master" + digest = "1:cd2774546a9f0db8e29e6a9792a1b5a7fabf7c0091f7b2845ad048652d74fcc2" name = "k8s.io/cli-runtime" packages = [ "pkg/genericclioptions", @@ -1155,11 +1430,13 @@ "pkg/kustomize/k8sdeps/transformer/patch", "pkg/kustomize/k8sdeps/validator", "pkg/printers", - "pkg/resource" + "pkg/resource", ] + pruneopts = "T" revision = "44a48934c135b31e4f1c0d12e91d384e1cb2304c" [[projects]] + digest = "1:0b9d76929add96339229991d4aeabf4ad1dc716bc8e1353e17a3d3d543480070" name = "k8s.io/client-go" packages = [ "discovery", @@ -1373,36 +1650,44 @@ "util/homedir", "util/jsonpath", "util/keyutil", - "util/retry" + "util/retry", ] + pruneopts = "T" revision = "8e956561bbf57253b1d19c449d0f24e8cb18d467" version = "kubernetes-1.15.1" [[projects]] branch = "master" + digest = "1:bde2bf8d78ad97dbe011a98ed73e0706f2e2d8c80e80caf90f35c6a9620af623" name = "k8s.io/component-base" packages = ["featuregate"] + pruneopts = "T" revision = "b4f50308a6168b3e1e8687b3fb46e9bf1a112ee5" [[projects]] + digest = "1:7a3ef99d492d30157b8e933624a8f0292b4cee5934c23269f7640c8030eb83cd" name = "k8s.io/klog" packages = ["."] + pruneopts = "T" revision = "a5bc97fbc634d635061f3146511332c7e313a55a" version = "v0.1.0" [[projects]] branch = "master" + digest = "1:90f16a49f856e6d94089444e487c535f4cd41f59a1e90c51deb9dcf965f3c50b" name = "k8s.io/kube-openapi" packages = [ "pkg/common", "pkg/util/proto", "pkg/util/proto/testing", - "pkg/util/proto/validation" + "pkg/util/proto/validation", ] + pruneopts = "T" revision = "0317810137be915b9cf888946c6e115c1bfac693" [[projects]] branch = "release-1.15" + digest = "1:06497e8bfb946cef502730eb1ab10dc4e60bac72123f634eef162aaf44999474" name = "k8s.io/kubernetes" packages = [ "pkg/api/legacyscheme", @@ -1456,12 +1741,14 @@ "pkg/util/hash", "pkg/util/labels", "pkg/util/parsers", - "pkg/util/taints" + "pkg/util/taints", ] + pruneopts = "T" revision = "92b2e906d7aa618588167817feaed137a44e6d92" [[projects]] branch = "master" + digest = "1:50d36d11cbcdc86bca9c3eede8bf7bee9947e2f350b3013a28282edf8d6e8b58" name = "k8s.io/utils" packages = [ "buffer", @@ -1470,18 +1757,22 @@ "net", "path", "pointer", - "trace" + "trace", ] + pruneopts = "T" revision = "21c4ce38f2a793ec01e925ddc31216500183b773" [[projects]] branch = "master" + digest = "1:15fbb9f95a13abe2be748b1159b491369d46a2ccc3f378e0f93c391f89608929" name = "rsc.io/letsencrypt" packages = ["."] + pruneopts = "T" revision = "1847a81d2087eba73081db43989e54dabe0768cd" source = "https://github.com/dmcgowan/letsencrypt.git" [[projects]] + digest = "1:663cc8454702691d4d089e6df5acc61d87b9c9a933a28b7615200e5b5a4f0cfd" name = "sigs.k8s.io/kustomize" packages = [ "pkg/commands/build", @@ -1505,20 +1796,105 @@ "pkg/transformers", "pkg/transformers/config", "pkg/transformers/config/defaultconfig", - "pkg/types" + "pkg/types", ] + pruneopts = "T" revision = "a6f65144121d1955266b0cd836ce954c04122dc8" version = "v2.0.3" [[projects]] + digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" name = "sigs.k8s.io/yaml" packages = ["."] + pruneopts = "T" revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" version = "v1.1.0" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "41fffcba41c216580ccc31a7a09f407955aac28550c9fad6f05d44a5db9dc59b" + input-imports = [ + "github.com/BurntSushi/toml", + "github.com/Masterminds/semver", + "github.com/Masterminds/sprig", + "github.com/Masterminds/vcs", + "github.com/asaskevich/govalidator", + "github.com/containerd/containerd/content", + "github.com/containerd/containerd/errdefs", + "github.com/containerd/containerd/reference", + "github.com/containerd/containerd/remotes", + "github.com/deislabs/oras/pkg/auth", + "github.com/deislabs/oras/pkg/auth/docker", + "github.com/deislabs/oras/pkg/content", + "github.com/deislabs/oras/pkg/context", + "github.com/deislabs/oras/pkg/oras", + "github.com/docker/distribution/configuration", + "github.com/docker/distribution/registry", + "github.com/docker/distribution/registry/auth/htpasswd", + "github.com/docker/distribution/registry/storage/driver/inmemory", + "github.com/docker/docker/pkg/term", + "github.com/docker/go-units", + "github.com/evanphx/json-patch", + "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", + "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/require", + "github.com/stretchr/testify/suite", + "github.com/xeipuuv/gojsonschema", + "golang.org/x/crypto/bcrypt", + "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", + "k8s.io/api/batch/v1", + "k8s.io/api/core/v1", + "k8s.io/api/extensions/v1beta1", + "k8s.io/apimachinery/pkg/api/errors", + "k8s.io/apimachinery/pkg/api/meta", + "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/labels", + "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/runtime/schema", + "k8s.io/apimachinery/pkg/types", + "k8s.io/apimachinery/pkg/util/intstr", + "k8s.io/apimachinery/pkg/util/strategicpatch", + "k8s.io/apimachinery/pkg/util/validation", + "k8s.io/apimachinery/pkg/util/wait", + "k8s.io/apimachinery/pkg/watch", + "k8s.io/cli-runtime/pkg/genericclioptions", + "k8s.io/cli-runtime/pkg/resource", + "k8s.io/client-go/discovery", + "k8s.io/client-go/kubernetes", + "k8s.io/client-go/kubernetes/fake", + "k8s.io/client-go/kubernetes/scheme", + "k8s.io/client-go/kubernetes/typed/core/v1", + "k8s.io/client-go/plugin/pkg/client/auth", + "k8s.io/client-go/rest", + "k8s.io/client-go/rest/fake", + "k8s.io/client-go/tools/clientcmd", + "k8s.io/client-go/tools/watch", + "k8s.io/client-go/util/homedir", + "k8s.io/klog", + "k8s.io/kubernetes/pkg/controller/deployment/util", + "k8s.io/kubernetes/pkg/kubectl/cmd/testing", + "k8s.io/kubernetes/pkg/kubectl/cmd/util", + "k8s.io/kubernetes/pkg/kubectl/validation", + "sigs.k8s.io/yaml", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index bd863f24a..0c9e76978 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -44,7 +44,7 @@ [[constraint]] name = "github.com/deislabs/oras" - version = "0.5.0" + version = "0.6.0" [[constraint]] name = "github.com/sirupsen/logrus" diff --git a/cmd/helm/root.go b/cmd/helm/root.go index f8d91e764..83117b87d 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -17,17 +17,13 @@ limitations under the License. package main // import "helm.sh/helm/cmd/helm" import ( - "context" "io" - "path/filepath" - auth "github.com/deislabs/oras/pkg/auth/docker" "github.com/spf13/cobra" "helm.sh/helm/cmd/helm/require" "helm.sh/helm/internal/experimental/registry" "helm.sh/helm/pkg/action" - "helm.sh/helm/pkg/helmpath" ) const ( @@ -132,30 +128,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string // set defaults from environment settings.Init(flags) - // Add the registry client based on settings - // TODO: Move this elsewhere (first, settings.Init() must move) - // TODO: handle errors, dont panic - credentialsFile := filepath.Join(helmpath.Registry(), registry.CredentialsFileBasename) - client, err := auth.NewClient(credentialsFile) - if err != nil { - panic(err) - } - resolver, err := client.Resolver(context.Background()) - if err != nil { - panic(err) - } - actionConfig.RegistryClient = registry.NewClient(®istry.ClientOptions{ - Debug: settings.Debug, - Out: out, - Authorizer: registry.Authorizer{ - Client: client, - }, - Resolver: registry.Resolver{ - Resolver: resolver, - }, - CacheRootDir: helmpath.Registry(), - }) - + // Add subcommands cmd.AddCommand( // chart commands newCreateCmd(out), @@ -168,10 +141,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string newSearchCmd(out), newVerifyCmd(out), - // registry/chart cache commands - newRegistryCmd(actionConfig, out), - newChartCmd(actionConfig, out), - // release commands newGetCmd(actionConfig, out), newHistoryCmd(actionConfig, out), @@ -193,6 +162,21 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string newDocsCmd(out), ) + // Add *experimental* subcommands + registryClient, err := registry.NewClient( + registry.ClientOptDebug(settings.Debug), + registry.ClientOptWriter(out), + ) + if err != nil { + // TODO: dont panic here, refactor newRootCmd to return error + panic(err) + } + actionConfig.RegistryClient = registryClient + cmd.AddCommand( + newRegistryCmd(actionConfig, out), + newChartCmd(actionConfig, out), + ) + // Find and add plugins loadPlugins(cmd, out) diff --git a/internal/experimental/registry/cache.go b/internal/experimental/registry/cache.go index 57bf562fa..107affcce 100644 --- a/internal/experimental/registry/cache.go +++ b/internal/experimental/registry/cache.go @@ -24,13 +24,13 @@ import ( "io/ioutil" "os" "path/filepath" - "sort" - "strings" "time" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" orascontent "github.com/deislabs/oras/pkg/content" - units "github.com/docker/go-units" - checksum "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -39,451 +39,324 @@ import ( "helm.sh/helm/pkg/chartutil" ) -var ( - tableHeaders = []string{"name", "version", "digest", "size", "created"} +const ( + // CacheRootDir is the root directory for a cache + CacheRootDir = "cache" ) type ( - filesystemCache struct { - out io.Writer - rootDir string - store *orascontent.Memorystore + // Cache handles local/in-memory storage of Helm charts, compliant with OCI Layout + Cache struct { + debug bool + out io.Writer + rootDir string + ociStore *orascontent.OCIStore + memoryStore *orascontent.Memorystore + } + + // CacheRefSummary contains as much info as available describing a chart reference in cache + // Note: fields here are sorted by the order in which they are set in FetchReference method + CacheRefSummary struct { + Name string + Repo string + Tag string + Exists bool + Manifest *ocispec.Descriptor + Config *ocispec.Descriptor + ContentLayer *ocispec.Descriptor + Size int64 + Digest digest.Digest + CreatedAt time.Time + Chart *chart.Chart } ) -func (cache *filesystemCache) LayersToChart(layers []ocispec.Descriptor) (*chart.Chart, error) { - metaLayer, contentLayer, err := extractLayers(layers) - if err != nil { - return nil, err +// NewCache returns a new OCI Layout-compliant cache with config +func NewCache(opts ...CacheOption) (*Cache, error) { + cache := &Cache{ + out: ioutil.Discard, } - - name, version, err := extractChartNameVersionFromLayer(contentLayer) - if err != nil { - return nil, err + for _, opt := range opts { + opt(cache) } - - // Obtain raw chart meta content (json) - _, metaJSONRaw, ok := cache.store.Get(metaLayer) - if !ok { - return nil, errors.New("error retrieving meta layer") + // validate + if cache.rootDir == "" { + return nil, errors.New("must set cache root dir on initialization") } + return cache, nil +} - // Construct chart metadata object - metadata := chart.Metadata{} - err = json.Unmarshal(metaJSONRaw, &metadata) - if err != nil { +// FetchReference retrieves a chart ref from cache +func (cache *Cache) FetchReference(ref *Reference) (*CacheRefSummary, error) { + if err := cache.init(); err != nil { return nil, err } - metadata.APIVersion = chart.APIVersionV1 - 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 + r := CacheRefSummary{ + Name: ref.FullName(), + Repo: ref.Repo, + Tag: ref.Tag, + } + for _, desc := range cache.ociStore.ListReferences() { + if desc.Annotations[ocispec.AnnotationRefName] == r.Name { + r.Exists = true + manifestBytes, err := cache.fetchBlob(&desc) + if err != nil { + return &r, err + } + var manifest ocispec.Manifest + err = json.Unmarshal(manifestBytes, &manifest) + if err != nil { + return &r, err + } + r.Manifest = &desc + r.Config = &manifest.Config + numLayers := len(manifest.Layers) + if numLayers != 1 { + return &r, errors.New( + fmt.Sprintf("manifest does not contain exactly 1 layer (total: %d)", numLayers)) + } + var contentLayer *ocispec.Descriptor + for _, layer := range manifest.Layers { + switch layer.MediaType { + case HelmChartContentLayerMediaType: + contentLayer = &layer + } + } + if contentLayer.Size == 0 { + return &r, errors.New( + fmt.Sprintf("manifest does not contain a layer with mediatype %s", HelmChartContentLayerMediaType)) + } + r.ContentLayer = contentLayer + info, err := cache.ociStore.Info(ctx(cache.out, cache.debug), contentLayer.Digest) + if err != nil { + return &r, err + } + r.Size = info.Size + r.Digest = info.Digest + r.CreatedAt = info.CreatedAt + contentBytes, err := cache.fetchBlob(contentLayer) + if err != nil { + return &r, err + } + ch, err := loader.LoadArchive(bytes.NewBuffer(contentBytes)) + if err != nil { + return &r, err + } + r.Chart = ch + } } - ch.Metadata = &metadata - - return ch, nil + return &r, nil } -func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descriptor, error) { - - // extract/separate the name and version from other metadata - if err := ch.Validate(); err != nil { +// StoreReference stores a chart ref in cache +func (cache *Cache) StoreReference(ref *Reference, ch *chart.Chart) (*CacheRefSummary, error) { + if err := cache.init(); err != nil { return nil, err } - 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, HelmChartMetaLayerMediaType, metaJSONRaw) - - // Create content layer - // TODO: something better than this hack. Currently needed for chartutil.Save() - // If metadata does not contain Name or Version, an error is returned - // such as "no chart name specified (Chart.yaml)" - ch.Metadata = &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "-", - Version: "0.1.0", - } - 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") + r := CacheRefSummary{ + Name: ref.FullName(), + Repo: ref.Repo, + Tag: ref.Tag, + Chart: ch, } - contentRaw, err := ioutil.ReadFile(tmpFile) + existing, _ := cache.FetchReference(ref) + r.Exists = existing.Exists + config, _, err := cache.saveChartConfig(ch) if err != nil { - return nil, err + return &r, err } - contentLayer := cache.store.Add(HelmChartContentFileName, HelmChartContentLayerMediaType, 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", escape(ref.Repo), "tags", ref.Tag) - - // add meta layer - metaJSONRaw, err := getSymlinkDestContent(filepath.Join(tagDir, "meta")) + r.Config = config + contentLayer, _, err := cache.saveChartContentLayer(ch) if err != nil { - return nil, err + return &r, err } - metaLayer := cache.store.Add(HelmChartMetaFileName, HelmChartMetaLayerMediaType, metaJSONRaw) - - // add content layer - contentRaw, err := getSymlinkDestContent(filepath.Join(tagDir, "content")) + r.ContentLayer = contentLayer + info, err := cache.ociStore.Info(ctx(cache.out, cache.debug), contentLayer.Digest) if err != nil { - return nil, err + return &r, err } - contentLayer := cache.store.Add(HelmChartContentFileName, HelmChartContentLayerMediaType, contentRaw) - - // set annotations on content layer (chart name and version) - err = setLayerAnnotationsFromChartLink(contentLayer, filepath.Join(tagDir, "chart")) + r.Size = info.Size + r.Digest = info.Digest + r.CreatedAt = info.CreatedAt + manifest, _, err := cache.saveChartManifest(config, contentLayer) if err != nil { - return nil, err + return &r, err } - - printChartSummary(cache.out, metaLayer, contentLayer) - layers := []ocispec.Descriptor{metaLayer, contentLayer} - return layers, nil + r.Manifest = manifest + return &r, nil } -func (cache *filesystemCache) StoreReference(ref *Reference, layers []ocispec.Descriptor) (bool, error) { - tagDir := mkdir(filepath.Join(cache.rootDir, "refs", escape(ref.Repo), "tags", ref.Tag)) - - // Retrieve just the meta and content layers - metaLayer, contentLayer, err := extractLayers(layers) - if err != nil { - return false, err - } - - // Extract chart name and version - name, version, err := extractChartNameVersionFromLayer(contentLayer) - if err != nil { - return false, err +// DeleteReference deletes a chart ref from cache +// TODO: garbage collection, only manifest removed +func (cache *Cache) DeleteReference(ref *Reference) (*CacheRefSummary, error) { + if err := cache.init(); err != nil { + return nil, err } - - // Create chart file - chartPath, err := createChartFile(filepath.Join(cache.rootDir, "charts"), name, version) - if err != nil { - return false, err + r, err := cache.FetchReference(ref) + if err != nil || !r.Exists { + return r, err } + cache.ociStore.DeleteReference(r.Name) + err = cache.ociStore.SaveIndex() + return r, err +} - // Create chart symlink - err = createSymlink(chartPath, filepath.Join(tagDir, "chart")) - if err != nil { - return false, err +// ListReferences lists all chart refs in a cache +func (cache *Cache) ListReferences() ([]*CacheRefSummary, error) { + if err := cache.init(); err != nil { + return nil, err } - - // Save meta blob - metaExists, metaPath := digestPath(filepath.Join(cache.rootDir, "blobs"), metaLayer.Digest) - if !metaExists { - fmt.Fprintf(cache.out, "%s: Saving meta (%s)\n", - shortDigest(metaLayer.Digest.Hex()), byteCountBinary(metaLayer.Size)) - _, metaJSONRaw, ok := cache.store.Get(metaLayer) - if !ok { - return false, errors.New("error retrieving meta layer") + var rr []*CacheRefSummary + for _, desc := range cache.ociStore.ListReferences() { + name := desc.Annotations[ocispec.AnnotationRefName] + if name == "" { + if cache.debug { + fmt.Fprintf(cache.out, "warning: found manifest without name: %s", desc.Digest.Hex()) + } + continue } - err = writeFile(metaPath, metaJSONRaw) + ref, err := ParseReference(name) if err != nil { - return false, err + return rr, err } - } - - // Create meta symlink - err = createSymlink(metaPath, filepath.Join(tagDir, "meta")) - if err != nil { - return false, err - } - - // Save content blob - contentExists, contentPath := digestPath(filepath.Join(cache.rootDir, "blobs"), contentLayer.Digest) - if !contentExists { - fmt.Fprintf(cache.out, "%s: Saving content (%s)\n", - shortDigest(contentLayer.Digest.Hex()), byteCountBinary(contentLayer.Size)) - _, contentRaw, ok := cache.store.Get(contentLayer) - if !ok { - return false, errors.New("error retrieving content layer") - } - err = writeFile(contentPath, contentRaw) + r, err := cache.FetchReference(ref) if err != nil { - return false, err + return rr, err } + rr = append(rr, r) } - - // Create content symlink - err = createSymlink(contentPath, filepath.Join(tagDir, "content")) - if err != nil { - return false, err - } - - printChartSummary(cache.out, metaLayer, contentLayer) - return metaExists && contentExists, nil + return rr, nil } -func (cache *filesystemCache) DeleteReference(ref *Reference) error { - tagDir := filepath.Join(cache.rootDir, "refs", escape(ref.Repo), "tags", ref.Tag) - if _, err := os.Stat(tagDir); os.IsNotExist(err) { - return errors.New("ref not found") +// AddManifest provides a manifest to the cache index.json +func (cache *Cache) AddManifest(ref *Reference, manifest *ocispec.Descriptor) error { + if err := cache.init(); err != nil { + return err } - return os.RemoveAll(tagDir) -} - -func (cache *filesystemCache) TableRows() ([][]interface{}, error) { - return getRefsSorted(filepath.Join(cache.rootDir, "refs")) + cache.ociStore.AddReference(ref.FullName(), *manifest) + err := cache.ociStore.SaveIndex() + return err } -// escape sanitizes a registry URL to remove characters such as ":" -// which are illegal on windows -func escape(s string) string { - return strings.ReplaceAll(s, ":", "_") +// Provider provides a valid containerd Provider +func (cache *Cache) Provider() content.Provider { + return content.Provider(cache.ociStore) } -// escape reverses escape -func unescape(s string) string { - return strings.ReplaceAll(s, "_", ":") +// Ingester provides a valid containerd Ingester +func (cache *Cache) Ingester() content.Ingester { + return content.Ingester(cache.ociStore) } -// printChartSummary prints details about a chart layers -func printChartSummary(out io.Writer, metaLayer ocispec.Descriptor, contentLayer ocispec.Descriptor) { - fmt.Fprintf(out, "Name: %s\n", contentLayer.Annotations[HelmChartNameAnnotation]) - fmt.Fprintf(out, "Version: %s\n", contentLayer.Annotations[HelmChartVersionAnnotation]) - fmt.Fprintf(out, "Meta: %s\n", metaLayer.Digest) - fmt.Fprintf(out, "Content: %s\n", contentLayer.Digest) +// ProvideIngester provides a valid oras ProvideIngester +func (cache *Cache) ProvideIngester() orascontent.ProvideIngester { + return orascontent.ProvideIngester(cache.ociStore) } -// fileExists determines if a file exists -func fileExists(path string) bool { - if _, err := os.Stat(path); os.IsNotExist(err) { - return false +// init creates files needed necessary for OCI layout store +func (cache *Cache) init() error { + if cache.ociStore == nil { + ociStore, err := orascontent.NewOCIStore(cache.rootDir) + if err != nil { + return err + } + cache.ociStore = ociStore + cache.memoryStore = orascontent.NewMemoryStore() } - return true -} - -// 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 + return nil } -// getSymlinkDestContent returns the file contents of a symlink's destination -func getSymlinkDestContent(linkPath string) ([]byte, error) { - src, err := os.Readlink(linkPath) +// saveChartConfig stores the Chart.yaml as json blob and returns a descriptor +func (cache *Cache) saveChartConfig(ch *chart.Chart) (*ocispec.Descriptor, bool, error) { + configBytes, err := json.Marshal(ch.Metadata) if err != nil { - return nil, err + return nil, false, err } - return ioutil.ReadFile(src) + configExists, err := cache.storeBlob(configBytes) + if err != nil { + return nil, configExists, err + } + descriptor := cache.memoryStore.Add("", HelmChartConfigMediaType, configBytes) + return &descriptor, configExists, nil } -// 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) +// saveChartContentLayer stores the chart as tarball blob and returns a descriptor +func (cache *Cache) saveChartContentLayer(ch *chart.Chart) (*ocispec.Descriptor, bool, error) { + destDir := filepath.Join(cache.rootDir, ".build") + os.MkdirAll(destDir, 0755) + tmpFile, err := chartutil.Save(ch, destDir) + defer os.Remove(tmpFile) if err != nil { - return err + return nil, false, errors.Wrap(err, "failed to save") } - // 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 + contentBytes, err := ioutil.ReadFile(tmpFile) + if err != nil { + return nil, false, err + } + contentExists, err := cache.storeBlob(contentBytes) + if err != nil { + return nil, contentExists, err + } + descriptor := cache.memoryStore.Add("", HelmChartContentLayerMediaType, contentBytes) + return &descriptor, contentExists, 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") +// saveChartManifest stores the chart manifest as json blob and returns a descriptor +func (cache *Cache) saveChartManifest(config *ocispec.Descriptor, contentLayer *ocispec.Descriptor) (*ocispec.Descriptor, bool, error) { + manifest := ocispec.Manifest{ + Versioned: specs.Versioned{SchemaVersion: 2}, + Config: *config, + Layers: []ocispec.Descriptor{*contentLayer}, } - - for _, layer := range layers { - switch layer.MediaType { - case HelmChartMetaLayerMediaType: - metaLayer = layer - case HelmChartContentLayerMediaType: - contentLayer = layer - } + manifestBytes, err := json.Marshal(manifest) + if err != nil { + return nil, false, err } - - if metaLayer.Size == 0 { - return metaLayer, contentLayer, errors.New("manifest does not contain a Helm chart meta layer") + manifestExists, err := cache.storeBlob(manifestBytes) + if err != nil { + return nil, manifestExists, err } - - if contentLayer.Size == 0 { - return metaLayer, contentLayer, errors.New("manifest does not contain a Helm chart content layer") + descriptor := ocispec.Descriptor{ + MediaType: ocispec.MediaTypeImageManifest, + Digest: digest.FromBytes(manifestBytes), + Size: int64(len(manifestBytes)), } - - return metaLayer, contentLayer, nil + return &descriptor, manifestExists, 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") +// storeBlob stores a blob on filesystem +func (cache *Cache) storeBlob(blobBytes []byte) (bool, error) { + var exists bool + writer, err := cache.ociStore.Store.Writer(ctx(cache.out, cache.debug), + content.WithRef(digest.FromBytes(blobBytes).Hex())) + if err != nil { + return exists, err } - version, ok := layer.Annotations[HelmChartVersionAnnotation] - if !ok { - return "", "", errors.New("could not find chart version in annotations") + _, err = writer.Write(blobBytes) + if err != nil { + return exists, err } - 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 + err = writer.Commit(ctx(cache.out, cache.debug), 0, writer.Digest()) + if err != nil { + if !errdefs.IsAlreadyExists(err) { + return exists, err } + exists = true } - return chartPath, nil -} - -// digestPath returns the path to addressable content, and whether the file exists -func digestPath(rootDir string, digest checksum.Digest) (bool, string) { - path := filepath.Join(rootDir, "sha256", digest.Hex()) - exists := fileExists(path) - return exists, path -} - -// writeFile creates a path, ensuring parent directory -func writeFile(path string, c []byte) error { - os.MkdirAll(filepath.Dir(path), 0755) - return ioutil.WriteFile(path, c, 0644) -} - -// 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]) + err = writer.Close() + return exists, err } -// shortDigest returns first 7 characters of a sha256 digest -func shortDigest(digest string) string { - if len(digest) == 64 { - return digest[:7] - } - return digest -} - -// getRefsSorted returns a map of all refs stored in a refsRootDir -func getRefsSorted(refsRootDir string) ([][]interface{}, 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 - repo := unescape(strings.TrimLeft( - strings.TrimPrefix(filepath.Dir(filepath.Dir(tagDir)), refsRootDir), "/\\")) - tag := filepath.Base(tagDir) - ref := fmt.Sprintf("%s:%s", repo, tag) - - // 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": - - // Make sure the filename looks like a sha256 digest (64 chars) - digest := destFileInfo.Name() - if len(digest) == 64 { - refsMap[ref]["digest"] = shortDigest(digest) - 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 tableHeaders { - if _, ok := ref[v]; !ok { - allKeysFound = false - break - } - } - if !allKeysFound { - delete(refsMap, k) - } +// fetchBlob retrieves a blob from filesystem +func (cache *Cache) fetchBlob(desc *ocispec.Descriptor) ([]byte, error) { + reader, err := cache.ociStore.ReaderAt(ctx(cache.out, cache.debug), *desc) + if err != nil { + return nil, err } - - // Sort and convert to format expected by uitable - refs := make([][]interface{}, len(refsMap)) - keys := make([]string, 0, len(refsMap)) - for key := range refsMap { - keys = append(keys, key) - } - sort.Strings(keys) - for i, key := range keys { - refs[i] = make([]interface{}, len(tableHeaders)+1) - refs[i][0] = key - ref := refsMap[key] - for j, k := range tableHeaders { - refs[i][j+1] = ref[k] - } + bytes := make([]byte, desc.Size) + _, err = reader.ReadAt(bytes, 0) + if err != nil { + return nil, err } - - return refs, err + return bytes, nil } diff --git a/internal/experimental/registry/cache_opts.go b/internal/experimental/registry/cache_opts.go new file mode 100644 index 000000000..51225e7db --- /dev/null +++ b/internal/experimental/registry/cache_opts.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 "helm.sh/helm/internal/experimental/registry" + +import ( + "io" +) + +type ( + // CacheOption allows specifying various settings configurable by the user for overriding the defaults + // used when creating a new default cache + CacheOption func(*Cache) +) + +// CacheOptDebug returns a function that sets the debug setting on cache options set +func CacheOptDebug(debug bool) CacheOption { + return func(cache *Cache) { + cache.debug = debug + } +} + +// CacheOptWriter returns a function that sets the writer setting on cache options set +func CacheOptWriter(out io.Writer) CacheOption { + return func(cache *Cache) { + cache.out = out + } +} + +// CacheOptRoot returns a function that sets the root directory setting on cache options set +func CacheOptRoot(rootDir string) CacheOption { + return func(cache *Cache) { + cache.rootDir = rootDir + } +} diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go index 570b01bd2..ba413a830 100644 --- a/internal/experimental/registry/client.go +++ b/internal/experimental/registry/client.go @@ -20,147 +20,201 @@ import ( "context" "fmt" "io" + "io/ioutil" + "path/filepath" + "sort" - orascontent "github.com/deislabs/oras/pkg/content" - orascontext "github.com/deislabs/oras/pkg/context" + auth "github.com/deislabs/oras/pkg/auth/docker" "github.com/deislabs/oras/pkg/oras" "github.com/gosuri/uitable" - "github.com/sirupsen/logrus" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" "helm.sh/helm/pkg/chart" + "helm.sh/helm/pkg/helmpath" ) const ( + // CredentialsFileBasename is the filename for auth credentials file CredentialsFileBasename = "config.json" ) type ( - // ClientOptions is used to construct a new client - ClientOptions struct { - Debug bool - Out io.Writer - Authorizer Authorizer - Resolver Resolver - CacheRootDir string - } - // Client works with OCI-compliant registries and local Helm chart cache Client struct { debug bool out io.Writer - authorizer Authorizer - resolver Resolver - cache *filesystemCache // TODO: something more robust + authorizer *Authorizer + resolver *Resolver + cache *Cache } ) // NewClient returns a new registry client with config -func NewClient(options *ClientOptions) *Client { - return &Client{ - debug: options.Debug, - out: options.Out, - resolver: options.Resolver, - authorizer: options.Authorizer, - cache: &filesystemCache{ - out: options.Out, - rootDir: options.CacheRootDir, - store: orascontent.NewMemoryStore(), - }, +func NewClient(opts ...ClientOption) (*Client, error) { + client := &Client{ + out: ioutil.Discard, + } + for _, opt := range opts { + opt(client) + } + // set defaults if fields are missing + if client.authorizer == nil { + credentialsFile := filepath.Join(helmpath.Registry(), CredentialsFileBasename) + authClient, err := auth.NewClient(credentialsFile) + if err != nil { + return nil, err + } + client.authorizer = &Authorizer{ + Client: authClient, + } } + if client.resolver == nil { + resolver, err := client.authorizer.Resolver(context.Background()) + if err != nil { + return nil, err + } + client.resolver = &Resolver{ + Resolver: resolver, + } + } + if client.cache == nil { + cache, err := NewCache( + CacheOptDebug(client.debug), + CacheOptWriter(client.out), + CacheOptRoot(filepath.Join(helmpath.Registry(), CacheRootDir)), + ) + if err != nil { + return nil, err + } + client.cache = cache + } + return client, nil } // Login logs into a registry func (c *Client) Login(hostname string, username string, password string) error { - err := c.authorizer.Login(c.newContext(), hostname, username, password) + err := c.authorizer.Login(ctx(c.out, c.debug), hostname, username, password) if err != nil { return err } - fmt.Fprint(c.out, "Login succeeded\n") + fmt.Fprintf(c.out, "Login succeeded\n") return nil } // Logout logs out of a registry func (c *Client) Logout(hostname string) error { - err := c.authorizer.Logout(c.newContext(), hostname) + err := c.authorizer.Logout(ctx(c.out, c.debug), hostname) if err != nil { return err } - fmt.Fprint(c.out, "Logout succeeded\n") + fmt.Fprintln(c.out, "Logout succeeded") return nil } // PushChart uploads a chart to a registry func (c *Client) PushChart(ref *Reference) error { - fmt.Fprintf(c.out, "The push refers to repository [%s]\n", ref.Repo) - layers, err := c.cache.LoadReference(ref) + r, err := c.cache.FetchReference(ref) if err != nil { return err } - _, err = oras.Push(c.newContext(), c.resolver, ref.String(), c.cache.store, layers, - oras.WithConfigMediaType(HelmChartConfigMediaType)) + if !r.Exists { + return errors.New(fmt.Sprintf("Chart not found: %s", r.Name)) + } + fmt.Fprintf(c.out, "The push refers to repository [%s]\n", r.Repo) + c.printCacheRefSummary(r) + layers := []ocispec.Descriptor{*r.ContentLayer} + _, err = oras.Push(ctx(c.out, c.debug), c.resolver, r.Name, c.cache.Provider(), layers, + oras.WithConfig(*r.Config), oras.WithNameValidation(nil)) if err != nil { return err } - var totalSize int64 - for _, layer := range layers { - totalSize += layer.Size + s := "" + numLayers := len(layers) + if 1 < numLayers { + s = "s" } fmt.Fprintf(c.out, - "%s: pushed to remote (%d layers, %s total)\n", ref.Tag, len(layers), byteCountBinary(totalSize)) + "%s: pushed to remote (%d layer%s, %s total)\n", r.Tag, numLayers, s, byteCountBinary(r.Size)) return nil } // PullChart downloads a chart from a registry func (c *Client) PullChart(ref *Reference) error { + if ref.Tag == "" { + return errors.New("tag explicitly required") + } + existing, err := c.cache.FetchReference(ref) + if err != nil { + return err + } fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Tag, ref.Repo) - _, layers, err := oras.Pull(c.newContext(), c.resolver, ref.String(), c.cache.store, oras.WithAllowedMediaTypes(KnownMediaTypes())) + manifest, _, err := oras.Pull(ctx(c.out, c.debug), c.resolver, ref.FullName(), c.cache.Ingester(), + oras.WithPullEmptyNameAllowed(), + oras.WithAllowedMediaTypes(KnownMediaTypes()), + oras.WithContentProvideIngester(c.cache.ProvideIngester())) + if err != nil { + return err + } + err = c.cache.AddManifest(ref, &manifest) if err != nil { return err } - exists, err := c.cache.StoreReference(ref, layers) + r, err := c.cache.FetchReference(ref) if err != nil { return err } - if !exists { - fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s:%s\n", ref.Repo, ref.Tag) + if !r.Exists { + return errors.New(fmt.Sprintf("Chart not found: %s", r.Name)) + } + c.printCacheRefSummary(r) + if !existing.Exists { + fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s\n", ref.FullName()) } else { - fmt.Fprintf(c.out, "Status: Chart is up to date for %s:%s\n", ref.Repo, ref.Tag) + fmt.Fprintf(c.out, "Status: Chart is up to date for %s\n", ref.FullName()) } - return nil + 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) + r, err := c.cache.StoreReference(ref, ch) if err != nil { return err } - _, err = c.cache.StoreReference(ref, layers) + c.printCacheRefSummary(r) + err = c.cache.AddManifest(ref, r.Manifest) if err != nil { return err } - fmt.Fprintf(c.out, "%s: saved\n", ref.Tag) + fmt.Fprintf(c.out, "%s: saved\n", r.Tag) return nil } // LoadChart retrieves a chart object by reference func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) { - layers, err := c.cache.LoadReference(ref) + r, err := c.cache.FetchReference(ref) if err != nil { return nil, err } - ch, err := c.cache.LayersToChart(layers) - return ch, err + if !r.Exists { + return nil, errors.New(fmt.Sprintf("Chart not found: %s", ref.FullName())) + } + c.printCacheRefSummary(r) + return r.Chart, nil } // RemoveChart deletes a locally saved chart func (c *Client) RemoveChart(ref *Reference) error { - err := c.cache.DeleteReference(ref) + r, err := c.cache.DeleteReference(ref) if err != nil { return err } - fmt.Fprintf(c.out, "%s: removed\n", ref.Tag) - return err + if !r.Exists { + return errors.New(fmt.Sprintf("Chart not found: %s", ref.FullName())) + } + fmt.Fprintf(c.out, "%s: removed\n", r.Tag) + return nil } // PrintChartTable prints a list of locally stored charts @@ -168,7 +222,7 @@ func (c *Client) PrintChartTable() error { table := uitable.New() table.MaxColWidth = 60 table.AddRow("REF", "NAME", "VERSION", "DIGEST", "SIZE", "CREATED") - rows, err := c.cache.TableRows() + rows, err := c.getChartTableRows() if err != nil { return err } @@ -179,12 +233,45 @@ func (c *Client) PrintChartTable() error { return nil } -// disable verbose logging coming from ORAS unless debug is enabled -func (c *Client) newContext() context.Context { - if !c.debug { - return orascontext.Background() +// printCacheRefSummary prints out chart ref summary +func (c *Client) printCacheRefSummary(r *CacheRefSummary) { + fmt.Fprintf(c.out, "ref: %s\n", r.Name) + fmt.Fprintf(c.out, "digest: %s\n", r.Digest.Hex()) + fmt.Fprintf(c.out, "size: %s\n", byteCountBinary(r.Size)) + fmt.Fprintf(c.out, "name: %s\n", r.Chart.Metadata.Name) + fmt.Fprintf(c.out, "version: %s\n", r.Chart.Metadata.Version) +} + +// getChartTableRows returns rows in uitable-friendly format +func (c *Client) getChartTableRows() ([][]interface{}, error) { + rr, err := c.cache.ListReferences() + if err != nil { + return nil, err + } + refsMap := map[string]map[string]string{} + for _, r := range rr { + refsMap[r.Name] = map[string]string{ + "name": r.Chart.Metadata.Name, + "version": r.Chart.Metadata.Version, + "digest": shortDigest(r.Digest.Hex()), + "size": byteCountBinary(r.Size), + "created": timeAgo(r.CreatedAt), + } + } + // Sort and convert to format expected by uitable + rows := make([][]interface{}, len(refsMap)) + keys := make([]string, 0, len(refsMap)) + for key := range refsMap { + keys = append(keys, key) + } + sort.Strings(keys) + for i, key := range keys { + rows[i] = make([]interface{}, 6) + rows[i][0] = key + ref := refsMap[key] + for j, k := range []string{"name", "version", "digest", "size", "created"} { + rows[i][j+1] = ref[k] + } } - ctx := orascontext.WithLoggerFromWriter(context.Background(), c.out) - orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel) - return ctx + return rows, nil } diff --git a/internal/experimental/registry/client_opts.go b/internal/experimental/registry/client_opts.go new file mode 100644 index 000000000..0eaa84346 --- /dev/null +++ b/internal/experimental/registry/client_opts.go @@ -0,0 +1,62 @@ +/* +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 "helm.sh/helm/internal/experimental/registry" + +import ( + "io" +) + +type ( + // ClientOption allows specifying various settings configurable by the user for overriding the defaults + // used when creating a new default client + ClientOption func(*Client) +) + +// ClientOptDebug returns a function that sets the debug setting on client options set +func ClientOptDebug(debug bool) ClientOption { + return func(client *Client) { + client.debug = debug + } +} + +// ClientOptWriter returns a function that sets the writer setting on client options set +func ClientOptWriter(out io.Writer) ClientOption { + return func(client *Client) { + client.out = out + } +} + +// ClientOptResolver returns a function that sets the resolver setting on client options set +func ClientOptResolver(resolver *Resolver) ClientOption { + return func(client *Client) { + client.resolver = resolver + } +} + +// ClientOptAuthorizer returns a function that sets the authorizer setting on client options set +func ClientOptAuthorizer(authorizer *Authorizer) ClientOption { + return func(client *Client) { + client.authorizer = authorizer + } +} + +// ClientOptCache returns a function that sets the cache setting on a client options set +func ClientOptCache(cache *Cache) ClientOption { + return func(client *Client) { + client.cache = cache + } +} diff --git a/internal/experimental/registry/client_test.go b/internal/experimental/registry/client_test.go index 5b2f06eb5..fcfb54a9d 100644 --- a/internal/experimental/registry/client_test.go +++ b/internal/experimental/registry/client_test.go @@ -69,17 +69,27 @@ func (suite *RegistryClientTestSuite) SetupSuite() { resolver, err := client.Resolver(context.Background()) suite.Nil(err, "no error creating resolver") - // Init test client - suite.RegistryClient = NewClient(&ClientOptions{ - Out: suite.Out, - Authorizer: Authorizer{ + // create cache + cache, err := NewCache( + CacheOptDebug(true), + CacheOptWriter(suite.Out), + CacheOptRoot(filepath.Join(suite.CacheRootDir, CacheRootDir)), + ) + suite.Nil(err, "no error creating cache") + + // init test client + suite.RegistryClient, err = NewClient( + ClientOptDebug(true), + ClientOptWriter(suite.Out), + ClientOptAuthorizer(&Authorizer{ Client: client, - }, - Resolver: Resolver{ + }), + ClientOptResolver(&Resolver{ Resolver: resolver, - }, - CacheRootDir: suite.CacheRootDir, - }) + }), + ClientOptCache(cache), + ) + suite.Nil(err, "no error creating registry client") // create htpasswd file (w BCrypt, which is required) pwBytes, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost) diff --git a/internal/experimental/registry/constants.go b/internal/experimental/registry/constants.go index 24583b5b5..2f1025efe 100644 --- a/internal/experimental/registry/constants.go +++ b/internal/experimental/registry/constants.go @@ -20,29 +20,14 @@ const ( // HelmChartConfigMediaType is the reserved media type for the Helm chart manifest config HelmChartConfigMediaType = "application/vnd.cncf.helm.config.v1+json" - // HelmChartMetaLayerMediaType is the reserved media type for Helm chart metadata - HelmChartMetaLayerMediaType = "application/vnd.cncf.helm.chart.meta.layer.v1+json" - // HelmChartContentLayerMediaType is the reserved media type for Helm chart package content HelmChartContentLayerMediaType = "application/vnd.cncf.helm.chart.content.layer.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{ - HelmChartMetaLayerMediaType, + HelmChartConfigMediaType, HelmChartContentLayerMediaType, } } diff --git a/internal/experimental/registry/constants_test.go b/internal/experimental/registry/constants_test.go index d58554619..9f078e632 100644 --- a/internal/experimental/registry/constants_test.go +++ b/internal/experimental/registry/constants_test.go @@ -24,6 +24,6 @@ import ( func TestConstants(t *testing.T) { knownMediaTypes := KnownMediaTypes() - assert.Contains(t, knownMediaTypes, HelmChartMetaLayerMediaType) + assert.Contains(t, knownMediaTypes, HelmChartConfigMediaType) assert.Contains(t, knownMediaTypes, HelmChartContentLayerMediaType) } diff --git a/internal/experimental/registry/reference.go b/internal/experimental/registry/reference.go index 12abe0260..6639d8350 100644 --- a/internal/experimental/registry/reference.go +++ b/internal/experimental/registry/reference.go @@ -18,6 +18,7 @@ package registry // import "helm.sh/helm/internal/experimental/registry" import ( "errors" + "fmt" "regexp" "strings" @@ -59,6 +60,14 @@ func ParseReference(s string) (*Reference, error) { return &ref, nil } +// FullName the full name of a reference (repo:tag) +func (ref *Reference) FullName() string { + if ref.Tag == "" { + return ref.Repo + } + return fmt.Sprintf("%s:%s", ref.Repo, ref.Tag) +} + // setExtraFields adds the Repo and Tag fields to a Reference func (ref *Reference) setExtraFields() { ref.Tag = ref.Object diff --git a/internal/experimental/registry/reference_test.go b/internal/experimental/registry/reference_test.go index 7f0299341..86f707fb6 100644 --- a/internal/experimental/registry/reference_test.go +++ b/internal/experimental/registry/reference_test.go @@ -44,46 +44,54 @@ func TestParseReference(t *testing.T) { is.NoError(err) is.Equal("mychart", ref.Repo) is.Equal("", ref.Tag) + is.Equal("mychart", ref.FullName()) s = "mychart:1.5.0" ref, err = ParseReference(s) is.NoError(err) is.Equal("mychart", ref.Repo) is.Equal("1.5.0", ref.Tag) + is.Equal("mychart:1.5.0", ref.FullName()) s = "myrepo/mychart" ref, err = ParseReference(s) is.NoError(err) is.Equal("myrepo/mychart", ref.Repo) is.Equal("", ref.Tag) + is.Equal("myrepo/mychart", ref.FullName()) s = "myrepo/mychart:1.5.0" ref, err = ParseReference(s) is.NoError(err) is.Equal("myrepo/mychart", ref.Repo) is.Equal("1.5.0", ref.Tag) + is.Equal("myrepo/mychart:1.5.0", ref.FullName()) s = "mychart:5001:1.5.0" ref, err = ParseReference(s) is.NoError(err) is.Equal("mychart:5001", ref.Repo) is.Equal("1.5.0", ref.Tag) + is.Equal("mychart:5001:1.5.0", ref.FullName()) s = "myrepo:5001/mychart:1.5.0" ref, err = ParseReference(s) is.NoError(err) is.Equal("myrepo:5001/mychart", ref.Repo) is.Equal("1.5.0", ref.Tag) + is.Equal("myrepo:5001/mychart:1.5.0", ref.FullName()) s = "localhost:5000/mychart:latest" ref, err = ParseReference(s) is.NoError(err) is.Equal("localhost:5000/mychart", ref.Repo) is.Equal("latest", ref.Tag) + is.Equal("localhost:5000/mychart:latest", ref.FullName()) s = "my.host.com/my/nested/repo:1.2.3" ref, err = ParseReference(s) is.NoError(err) is.Equal("my.host.com/my/nested/repo", ref.Repo) is.Equal("1.2.3", ref.Tag) + is.Equal("my.host.com/my/nested/repo:1.2.3", ref.FullName()) } diff --git a/internal/experimental/registry/util.go b/internal/experimental/registry/util.go new file mode 100644 index 000000000..3af6318e0 --- /dev/null +++ b/internal/experimental/registry/util.go @@ -0,0 +1,66 @@ +/* +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 "helm.sh/helm/internal/experimental/registry" + +import ( + "context" + "fmt" + "io" + "time" + + orascontext "github.com/deislabs/oras/pkg/context" + units "github.com/docker/go-units" + "github.com/sirupsen/logrus" +) + +// 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]) +} + +// shortDigest returns first 7 characters of a sha256 digest +func shortDigest(digest string) string { + if len(digest) == 64 { + return digest[:7] + } + return digest +} + +// timeAgo returns a human-readable timestamp respresenting time that has passed +func timeAgo(t time.Time) string { + return units.HumanDuration(time.Now().UTC().Sub(t)) +} + +// ctx retrieves a fresh context. +// disable verbose logging coming from ORAS (unless debug is enabled) +func ctx(out io.Writer, debug bool) context.Context { + if !debug { + return orascontext.Background() + } + ctx := orascontext.WithLoggerFromWriter(context.Background(), out) + orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel) + return ctx +} diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index 94efa4aa4..becbc9ed0 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -19,6 +19,7 @@ import ( "context" "flag" "io/ioutil" + "path/filepath" "testing" "time" @@ -54,20 +55,32 @@ func actionConfigFixture(t *testing.T) *Configuration { t.Fatal(err) } - return &Configuration{ - Releases: storage.Init(driver.NewMemory()), - KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: ioutil.Discard}}, - Capabilities: chartutil.DefaultCapabilities, - RegistryClient: registry.NewClient(®istry.ClientOptions{ - Out: ioutil.Discard, - Authorizer: registry.Authorizer{ - Client: client, - }, - Resolver: registry.Resolver{ - Resolver: resolver, - }, - CacheRootDir: tdir, + cache, err := registry.NewCache( + registry.CacheOptDebug(true), + registry.CacheOptRoot(filepath.Join(tdir, registry.CacheRootDir)), + ) + if err != nil { + t.Fatal(err) + } + + registryClient, err := registry.NewClient( + registry.ClientOptAuthorizer(®istry.Authorizer{ + Client: client, }), + registry.ClientOptResolver(®istry.Resolver{ + Resolver: resolver, + }), + registry.ClientOptCache(cache), + ) + if err != nil { + t.Fatal(err) + } + + return &Configuration{ + Releases: storage.Init(driver.NewMemory()), + KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: ioutil.Discard}}, + Capabilities: chartutil.DefaultCapabilities, + RegistryClient: registryClient, Log: func(format string, v ...interface{}) { t.Helper() if *verbose {