Helm 3: initial registry support (#5243)

* initial registry support

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* fix dependency mess

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* add extra chart command output

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* sanitize registry path (windows fix)

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* store all sha256 blobs in same dir

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* switch to use chartutil.SaveDir

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* populate chart command long descriptions

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* remove test cache dir in teardown

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* add long description of chart export

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>

* clean up table rows code

Signed-off-by: Josh Dolitsky <jdolitsky@gmail.com>
pull/5280/head
Josh Dolitsky 6 years ago committed by GitHub
parent 612d3a9f27
commit a32f8ebb37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

452
Gopkg.lock generated

@ -100,12 +100,12 @@
revision = "de5bf2ad457846296e2031421a34e2568e304e35" revision = "de5bf2ad457846296e2031421a34e2568e304e35"
[[projects]] [[projects]]
digest = "1:69b1cc331fca23d702bd72f860c6a647afd0aa9fcbc1d0659b1365e26546dd70" branch = "master"
name = "github.com/Sirupsen/logrus" digest = "1:5b8a3b9e8d146a93f6d0538d3be408c9adff07fd694d4094b814a376b4727b14"
name = "github.com/Shopify/logrus-bugsnag"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95" revision = "577dee27f20dd8f1a529f82210094af593be12bd"
version = "v1.2.0"
[[projects]] [[projects]]
digest = "1:8f5416c7f59da8600725ae1ff00a99af1da8b04c211ae6f3c8f8bcab0164f650" digest = "1:8f5416c7f59da8600725ae1ff00a99af1da8b04c211ae6f3c8f8bcab0164f650"
@ -123,6 +123,43 @@
revision = "7664702784775e51966f0885f5cd27435916517b" revision = "7664702784775e51966f0885f5cd27435916517b"
version = "v4" 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]] [[projects]]
digest = "1:65b0d980b428a6ad4425f2df4cd5410edd81f044cf527bd1c345368444649e58" digest = "1:65b0d980b428a6ad4425f2df4cd5410edd81f044cf527bd1c345368444649e58"
name = "github.com/census-instrumentation/opencensus-proto" name = "github.com/census-instrumentation/opencensus-proto"
@ -149,6 +186,23 @@
pruneopts = "UT" pruneopts = "UT"
revision = "bf70f2a70fb1b1f36d90d671a72795984eab0fcb" 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]] [[projects]]
digest = "1:7cb4fdca4c251b3ef8027c90ea35f70c7b661a593b9eeae34753c65499098bb1" digest = "1:7cb4fdca4c251b3ef8027c90ea35f70c7b661a593b9eeae34753c65499098bb1"
name = "github.com/cpuguy83/go-md2man" name = "github.com/cpuguy83/go-md2man"
@ -165,6 +219,17 @@
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1" 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]] [[projects]]
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55" digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
name = "github.com/dgrijalva/jwt-go" name = "github.com/dgrijalva/jwt-go"
@ -174,26 +239,87 @@
version = "v3.2.0" version = "v3.2.0"
[[projects]] [[projects]]
digest = "1:4ddc17aeaa82cb18c5f0a25d7c253a10682f518f4b2558a82869506eec223d76" digest = "1:888aaacf886021e4a0fa6b09a61f1158063bd6c2e2ddefe14f3a7ccbc93ffe27"
name = "github.com/docker/distribution" name = "github.com/docker/distribution"
packages = [ packages = [
".",
"configuration",
"context",
"digestset", "digestset",
"health",
"health/checks",
"manifest",
"manifest/manifestlist",
"manifest/ocischema",
"manifest/schema1",
"manifest/schema2",
"metrics",
"notifications",
"reference", "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" pruneopts = "UT"
revision = "40b7b5830a2337bb07627617740c0e39eb92800c" revision = "40b7b5830a2337bb07627617740c0e39eb92800c"
version = "v2.7.0" version = "v2.7.0"
[[projects]] [[projects]]
digest = "1:53e99d883df3e940f5f0223795f300eb32b8c044f226132bfc0e74930f24ea4b" branch = "master"
digest = "1:8da8bb2b12c31c632e96ca6f15666a36c36cd390326b6c5e1c5e309cf4b5419a"
name = "github.com/docker/docker" name = "github.com/docker/docker"
packages = [ packages = [
"pkg/term", "pkg/term",
"pkg/term/windows", "pkg/term/windows",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "092cba3727bb9b4a2f0e922cd6c0f93ea270e363" revision = "2cb26cfe9cbf8a64c5046c74d65f4528b22e67f4"
version = "v1.13.1"
[[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]] [[projects]]
branch = "master" branch = "master"
@ -207,12 +333,23 @@
revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85" revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85"
[[projects]] [[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" name = "github.com/evanphx/json-patch"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "72bf35d0ff611848c1dc9df0f976c81192392fa5" revision = "afac545df32f2287a079e2dfb7ba2745a643747e"
version = "v4.1.0" version = "v3.0.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -230,6 +367,17 @@
revision = "44e46d280b43ec1531bb25252440e34f1b800b65" revision = "44e46d280b43ec1531bb25252440e34f1b800b65"
version = "v1.0.0" 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]] [[projects]]
digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda" digest = "1:2cd7915ab26ede7d95b8749e6b1f933f1c6d5398030684e6505940a10f31cfda"
name = "github.com/ghodss/yaml" name = "github.com/ghodss/yaml"
@ -288,15 +436,23 @@
version = "v0.2.3" version = "v0.2.3"
[[projects]] [[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" name = "github.com/gogo/protobuf"
packages = [ packages = [
"proto", "proto",
"sortkeys", "sortkeys",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "636bf0302bc95575d69441b25a2603156ffdddf1" revision = "4cbf7e384e768b4e01799441fdf2a706a5635ae7"
version = "v1.1.1" version = "v1.2.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -359,7 +515,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:8105da6944d9227dd7c47cf290fbeafeb0b8f3b7f77a89e20b6ae4da7c71bb46" digest = "1:d47549022c929925679aec031329f59f250d704f69ee44a194998cdb8d873393"
name = "github.com/gophercloud/gophercloud" name = "github.com/gophercloud/gophercloud"
packages = [ packages = [
".", ".",
@ -371,7 +527,23 @@
"pagination", "pagination",
] ]
pruneopts = "UT" 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]] [[projects]]
branch = "master" branch = "master"
@ -439,6 +611,14 @@
revision = "1624edc4454b8682399def8740d46db5e4362ba4" revision = "1624edc4454b8682399def8740d46db5e4362ba4"
version = "v1.1.5" version = "v1.1.5"
[[projects]]
branch = "master"
digest = "1:caf6db28595425c0e0f2301a00257d11712f65c1878e12cffc42f6b9a9cf3f23"
name = "github.com/kardianos/osext"
packages = ["."]
pruneopts = "UT"
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
[[projects]] [[projects]]
digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8" digest = "1:0a69a1c0db3591fcefb47f115b224592c8dfa4368b7ba9fae509d5e16cdc95c8"
name = "github.com/konsorten/go-windows-terminal-sequences" name = "github.com/konsorten/go-windows-terminal-sequences"
@ -460,12 +640,12 @@
revision = "60711f1a8329503b04e1c88535f419d0bb440bff" revision = "60711f1a8329503b04e1c88535f419d0bb440bff"
[[projects]] [[projects]]
digest = "1:cdb899c199f907ac9fb50495ec71212c95cb5b0e0a8ee0800da0238036091033" digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7"
name = "github.com/mattn/go-runewidth" name = "github.com/mattn/go-runewidth"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb" revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3"
version = "v0.0.3" version = "v0.0.4"
[[projects]] [[projects]]
digest = "1:7efe48dea4db6b35dcc15e15394b627247e5b3fb814242de986b746ba8e0abf0" digest = "1:7efe48dea4db6b35dcc15e15394b627247e5b3fb814242de986b746ba8e0abf0"
@ -475,6 +655,21 @@
revision = "02e3cf038dcea8290e44424da473dd12be796a8a" revision = "02e3cf038dcea8290e44424da473dd12be796a8a"
version = "v1.0.3" 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]] [[projects]]
digest = "1:abf08734a6527df70ed361d7c369fb580e6840d8f7a6012e5f609fdfd93b4e48" digest = "1:abf08734a6527df70ed361d7c369fb580e6840d8f7a6012e5f609fdfd93b4e48"
name = "github.com/mitchellh/go-wordwrap" name = "github.com/mitchellh/go-wordwrap"
@ -507,6 +702,17 @@
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
version = "v1.0.0-rc1" 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]] [[projects]]
branch = "master" branch = "master"
digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2"
@ -539,6 +745,51 @@
revision = "792786c7400a136282c1664665ae0a8db921c6c2" revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0" 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]] [[projects]]
digest = "1:b36a0ede02c4c2aef7df7f91cbbb7bb88a98b5d253509d4f997dda526e50c88c" digest = "1:b36a0ede02c4c2aef7df7f91cbbb7bb88a98b5d253509d4f997dda526e50c88c"
name = "github.com/russross/blackfriday" name = "github.com/russross/blackfriday"
@ -547,6 +798,14 @@
revision = "05f3235734ad95d0016f6a23902f06461fcf567a" revision = "05f3235734ad95d0016f6a23902f06461fcf567a"
version = "v1.5.2" version = "v1.5.2"
[[projects]]
digest = "1:69b1cc331fca23d702bd72f860c6a647afd0aa9fcbc1d0659b1365e26546dd70"
name = "github.com/sirupsen/logrus"
packages = ["."]
pruneopts = "UT"
revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95"
version = "v1.2.0"
[[projects]] [[projects]]
digest = "1:e01b05ba901239c783dfe56450bcde607fc858908529868259c9a8765dc176d0" digest = "1:e01b05ba901239c783dfe56450bcde607fc858908529868259c9a8765dc176d0"
name = "github.com/spf13/cobra" name = "github.com/spf13/cobra"
@ -569,11 +828,45 @@
[[projects]] [[projects]]
digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759" digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759"
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"
packages = ["assert"] packages = [
"assert",
"require",
"suite",
]
pruneopts = "UT" pruneopts = "UT"
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0" version = "v1.3.0"
[[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]] [[projects]]
digest = "1:2ae8314c44cd413cfdb5b1df082b350116dd8d2fff973e62c01b285b7affd89e" digest = "1:2ae8314c44cd413cfdb5b1df082b350116dd8d2fff973e62c01b285b7affd89e"
name = "go.opencensus.io" name = "go.opencensus.io"
@ -600,12 +893,13 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:77b908a63f61c2e63c69b4b8f89bbecb0138899d236f656f1d255d74249531cb" digest = "1:599ef9ff10026292c425292ab1d2bb1521cd671fe89a6034df07bf1411daa44b"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"cast5", "cast5",
"ed25519", "ed25519",
"ed25519/internal/edwards25519", "ed25519/internal/edwards25519",
"ocsp",
"openpgp", "openpgp",
"openpgp/armor", "openpgp/armor",
"openpgp/clearsign", "openpgp/clearsign",
@ -622,20 +916,26 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:29fe5460430a338b64f4a0259a6c59a1e2350bbcff54fa66f906fa8d10515c4d" digest = "1:647b0128e9a9886335bfb6c9a1fc97758b7f846ec42f222933f6fee6730c96e2"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = [ packages = [
"bpf",
"context", "context",
"context/ctxhttp", "context/ctxhttp",
"http/httpguts", "http/httpguts",
"http2", "http2",
"http2/hpack", "http2/hpack",
"idna", "idna",
"internal/iana",
"internal/socket",
"internal/timeseries", "internal/timeseries",
"ipv4",
"ipv6",
"publicsuffix",
"trace", "trace",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "610586996380ceef02dd726cc09df7e00a3f8e56" revision = "927f97764cc334a6575f4b7a1584a147864d5723"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -653,22 +953,25 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:5e4d81c50cffcb124b899e4f3eabec3930c73532f0096c27f94476728ba03028" digest = "1:04a5b0e4138f98eef79ce12a955a420ee358e9f787044cc3a553ac3c3ade997e"
name = "golang.org/x/sync" name = "golang.org/x/sync"
packages = ["semaphore"] packages = [
"errgroup",
"semaphore",
]
pruneopts = "UT" pruneopts = "UT"
revision = "42b317875d0fa942474b76e1b46a6060d720ae6e" revision = "37e7f081c4d4c64e13b10787722085407fe5d15f"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:b9dceb1408ba5105803d5859193bc7d89ac3199b611cf8681dbaa0aa09c10d9c" digest = "1:3d5e79e10549fd9119cbefd614b6d351ef5bd0be2f2b103a4199788e784cbc68"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = [ packages = [
"unix", "unix",
"windows", "windows",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "70b957f3b65e069b4930ea94e2721eefa0f8f695" revision = "b4a75ba826a64a70990f11a225237acd6ef35c9f"
[[projects]] [[projects]]
digest = "1:28756bf526c1af662d24519f2fa7abca7237bebb06e3e02941b2b6e5b6ceb7b9" digest = "1:28756bf526c1af662d24519f2fa7abca7237bebb06e3e02941b2b6e5b6ceb7b9"
@ -714,10 +1017,10 @@
name = "google.golang.org/api" name = "google.golang.org/api"
packages = ["support/bundler"] packages = ["support/bundler"]
pruneopts = "UT" pruneopts = "UT"
revision = "1a5ef82f9af45ef51c486291ef2b0a16d82fdb95" revision = "65a46cafb132eff435c7d1e0f439cc73c8eebb85"
[[projects]] [[projects]]
digest = "1:d2a8db567a76203e3b41c1f632d86485ffd57f8e650a0d1b19d240671c2fddd7" digest = "1:fa026a5c59bd2df343ec4a3538e6288dcf4e2ec5281d743ae82c120affe6926a"
name = "google.golang.org/appengine" name = "google.golang.org/appengine"
packages = [ packages = [
".", ".",
@ -732,8 +1035,8 @@
"urlfetch", "urlfetch",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "4a4468ece617fc8205e99368fa2200e9d1fad421" revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
version = "v1.3.0" version = "v1.4.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -741,7 +1044,7 @@
name = "google.golang.org/genproto" name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"] packages = ["googleapis/rpc/status"]
pruneopts = "UT" pruneopts = "UT"
revision = "bd91e49a0898e27abb88c339b432fa53d7497ac0" revision = "bd9b4fb69e2ffd37621a6caa54dcbead29b546f2"
[[projects]] [[projects]]
digest = "1:9edd250a3c46675d0679d87540b30c9ed253b19bd1fd1af08f4f5fb3c79fc487" digest = "1:9edd250a3c46675d0679d87540b30c9ed253b19bd1fd1af08f4f5fb3c79fc487"
@ -791,6 +1094,18 @@
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
version = "v0.9.1" 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]] [[projects]]
digest = "1:550221cdc42e1ad44a1942d01c4135c30f6808b61dbad3c52d325e01d8e7cc07" digest = "1:550221cdc42e1ad44a1942d01c4135c30f6808b61dbad3c52d325e01d8e7cc07"
name = "gopkg.in/square/go-jose.v2" name = "gopkg.in/square/go-jose.v2"
@ -937,19 +1252,26 @@
"pkg/util/feature", "pkg/util/feature",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "6e69081b521942e4f4e46a657140d81bc913df73" revision = "c3083108fa3bda1f3da8742881d36d20330d07a3"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:63793246976569a95e534c731e79cc555dabee6f8efa29a0b28ca33f23b7e28b" digest = "1:79d60410b8f3339b77a36d6096cea81ebe4974b7cb8108446a121266b58bff01"
name = "k8s.io/cli-runtime" name = "k8s.io/cli-runtime"
packages = [ packages = [
"pkg/genericclioptions", "pkg/genericclioptions",
"pkg/genericclioptions/printers", "pkg/genericclioptions/printers",
"pkg/genericclioptions/resource", "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" pruneopts = "UT"
revision = "2f0d1d0a58f22eae7a0ec3d6cf01f4a122a57dae" revision = "8abb1aeb8307ee1f2335c4331d850a232efb1cbd"
[[projects]] [[projects]]
digest = "1:ba0a20ca14958ddb44159a08daf69b6acbdc0ef99f449ec7035759e9362bc058" digest = "1:ba0a20ca14958ddb44159a08daf69b6acbdc0ef99f449ec7035759e9362bc058"
@ -1088,9 +1410,10 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:4b8768c5b052c2c2a2ba468ec9392657f8bec881b5a24fd172b4d81b0eac6a55" digest = "1:d8f05387026197d55244f5866ea172e360c3f1602bea51ba90c72a7a43ecdce6"
name = "k8s.io/kube-openapi" name = "k8s.io/kube-openapi"
packages = [ packages = [
"pkg/common",
"pkg/util/proto", "pkg/util/proto",
"pkg/util/proto/testing", "pkg/util/proto/testing",
"pkg/util/proto/validation", "pkg/util/proto/validation",
@ -1222,7 +1545,7 @@
"pkg/version", "pkg/version",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "f2c8f1cadf1808ec28476682e49a3cce2b09efbf" revision = "598a01989dcd06ec776248506fe6eb32f499bd38"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -1233,7 +1556,45 @@
"pointer", "pointer",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "0d26856f57b32ec3398579285e5c8a2bfe8c5243" revision = "8a16e7dd8fb6d97d1331b0c79a16722f934b00b1"
[[projects]]
branch = "master"
digest = "1:15fbb9f95a13abe2be748b1159b491369d46a2ccc3f378e0f93c391f89608929"
name = "rsc.io/letsencrypt"
packages = ["."]
pruneopts = "UT"
revision = "1847a81d2087eba73081db43989e54dabe0768cd"
source = "https://github.com/dmcgowan/letsencrypt.git"
[[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]] [[projects]]
digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849"
@ -1260,17 +1621,30 @@
"github.com/Masterminds/sprig", "github.com/Masterminds/sprig",
"github.com/Masterminds/vcs", "github.com/Masterminds/vcs",
"github.com/asaskevich/govalidator", "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/evanphx/json-patch",
"github.com/ghodss/yaml", "github.com/ghodss/yaml",
"github.com/gobwas/glob", "github.com/gobwas/glob",
"github.com/gosuri/uitable", "github.com/gosuri/uitable",
"github.com/gosuri/uitable/util/strutil", "github.com/gosuri/uitable/util/strutil",
"github.com/mattn/go-shellwords", "github.com/mattn/go-shellwords",
"github.com/opencontainers/go-digest",
"github.com/opencontainers/image-spec/specs-go/v1",
"github.com/pkg/errors", "github.com/pkg/errors",
"github.com/sirupsen/logrus",
"github.com/spf13/cobra", "github.com/spf13/cobra",
"github.com/spf13/cobra/doc", "github.com/spf13/cobra/doc",
"github.com/spf13/pflag", "github.com/spf13/pflag",
"github.com/stretchr/testify/assert", "github.com/stretchr/testify/assert",
"github.com/stretchr/testify/suite",
"golang.org/x/crypto/openpgp", "golang.org/x/crypto/openpgp",
"golang.org/x/crypto/openpgp/clearsign", "golang.org/x/crypto/openpgp/clearsign",
"golang.org/x/crypto/openpgp/errors", "golang.org/x/crypto/openpgp/errors",

@ -55,6 +55,14 @@
name = "github.com/imdario/mergo" name = "github.com/imdario/mergo"
version = "v0.3.5" 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"
[[constraint]] [[constraint]]
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"
version = "^1.3.0" version = "^1.3.0"
@ -63,3 +71,8 @@
go-tests = true go-tests = true
unused-packages = true unused-packages = true
# This override below necessary for using docker/distribution as a test dependency
[[override]]
name = "rsc.io/letsencrypt"
branch = "master"
source = "https://github.com/dmcgowan/letsencrypt.git"

@ -0,0 +1,55 @@
/*
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 (
"io"
"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, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "chart",
Short: "push, pull, tag, or remove Helm charts",
Long: chartHelp,
}
cmd.AddCommand(
newChartListCmd(cfg, out),
newChartExportCmd(cfg, out),
newChartPullCmd(cfg, out),
newChartPushCmd(cfg, out),
newChartRemoveCmd(cfg, out),
newChartSaveCmd(cfg, out),
)
return cmd
}
// TODO remove once WARN lines removed from oras or containerd
func init() {
logrus.SetLevel(logrus.ErrorLevel)
}

@ -0,0 +1,47 @@
/*
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 (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/action"
)
const chartExportDesc = `
Export a chart stored in local registry cache.
This will create a new directory with the name of
the chart, in a format that developers can modify
and check into source control if desired.
`
func newChartExportCmd(cfg *action.Configuration, out io.Writer) *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(out, ref)
},
}
}

@ -0,0 +1,43 @@
/*
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 (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/action"
)
const chartListDesc = `
List all charts in the local registry cache.
Charts are sorted by ref name, alphabetically.
`
func newChartListCmd(cfg *action.Configuration, out io.Writer) *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(out)
},
}
}

@ -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 main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/action"
)
const chartPullDesc = `
Download a chart from a remote registry.
This will store the chart in the local registry cache to be used later.
`
func newChartPullCmd(cfg *action.Configuration, out io.Writer) *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(out, ref)
},
}
}

@ -0,0 +1,47 @@
/*
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 (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/action"
)
const chartPushDesc = `
Upload a chart to a remote registry.
Note: the ref must already exist in the local registry cache.
Must first run "helm chart save" or "helm chart pull".
`
func newChartPushCmd(cfg *action.Configuration, out io.Writer) *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(out, ref)
},
}
}

@ -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 main
import (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/action"
)
const chartRemoveDesc = `
Remove a chart from the local registry cache.
Note: the chart content will still exist in the cache,
but it will no longer appear in "helm chart list".
To remove all unlinked content, please run "helm chart prune". (TODO)
`
func newChartRemoveCmd(cfg *action.Configuration, out io.Writer) *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(out, ref)
},
}
}

@ -0,0 +1,47 @@
/*
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 (
"io"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/action"
)
const chartSaveDesc = `
Store a copy of chart in local registry cache.
Note: modifying the chart after this operation will
not change the item as it exists in the cache.
`
func newChartSaveCmd(cfg *action.Configuration, out io.Writer) *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(out, path, ref)
},
}
}

@ -19,11 +19,13 @@ package main // import "k8s.io/helm/cmd/helm"
import ( import (
"io" "io"
"github.com/containerd/containerd/remotes/docker"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/require" "k8s.io/helm/cmd/helm/require"
"k8s.io/helm/pkg/action" "k8s.io/helm/pkg/action"
"k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/registry"
) )
var globalUsage = `The Kubernetes package manager var globalUsage = `The Kubernetes package manager
@ -61,6 +63,21 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri
settings.AddFlags(flags) 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(&registry.ClientOptions{
Out: out,
Resolver: registry.Resolver{
Resolver: docker.NewResolver(docker.ResolverOptions{}),
},
CacheRootDir: settings.Home.Registry(),
})
cmd.AddCommand( cmd.AddCommand(
// chart commands // chart commands
newCreateCmd(out), newCreateCmd(out),
@ -72,6 +89,7 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri
newRepoCmd(out), newRepoCmd(out),
newSearchCmd(out), newSearchCmd(out),
newVerifyCmd(out), newVerifyCmd(out),
newChartCmd(actionConfig, out),
// release commands // release commands
newGetCmd(c, out), newGetCmd(c, out),
@ -95,11 +113,6 @@ func newRootCmd(c helm.Interface, actionConfig *action.Configuration, out io.Wri
newDocsCmd(out), newDocsCmd(out),
) )
flags.Parse(args)
// set defaults from environment
settings.Init(flags)
// Find and add plugins // Find and add plugins
loadPlugins(cmd, out) loadPlugins(cmd, out)

@ -23,6 +23,7 @@ import (
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/registry"
"k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/tiller/environment" "k8s.io/helm/pkg/tiller/environment"
) )
@ -40,9 +41,13 @@ type Configuration struct {
// Releases stores records of releases. // Releases stores records of releases.
Releases *storage.Storage Releases *storage.Storage
// KubeClient is a Kubernetes API client. // KubeClient is a Kubernetes API client.
KubeClient environment.KubeClient KubeClient environment.KubeClient
// RegistryClient is a client for working with registries
RegistryClient *registry.Client
Capabilities *chartutil.Capabilities Capabilities *chartutil.Capabilities
Log func(string, ...interface{}) Log func(string, ...interface{})

@ -0,0 +1,60 @@
/*
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 (
"fmt"
"io"
"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(out io.Writer, 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: make destination dir configurable
err = chartutil.SaveDir(ch, ".")
if err != nil {
return err
}
fmt.Fprintf(out, "Exported to %s/\n", ch.Metadata.Name)
return nil
}

@ -0,0 +1,38 @@
/*
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"
)
// 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(out io.Writer) error {
return a.cfg.RegistryClient.PrintChartTable()
}

@ -0,0 +1,44 @@
/*
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"
"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(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err
}
return a.cfg.RegistryClient.PullChart(r)
}

@ -0,0 +1,44 @@
/*
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"
"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(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err
}
return a.cfg.RegistryClient.PushChart(r)
}

@ -0,0 +1,44 @@
/*
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"
"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(out io.Writer, ref string) error {
r, err := registry.ParseReference(ref)
if err != nil {
return err
}
return a.cfg.RegistryClient.RemoveChart(r)
}

@ -0,0 +1,57 @@
/*
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"
"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(out io.Writer, 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)
}

@ -40,6 +40,11 @@ func (h Home) Path(elem ...string) string {
return filepath.Join(p...) 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. // Repository returns the path to the local repository.
func (h Home) Repository() string { func (h Home) Repository() string {
return h.Path("repository") return h.Path("repository")

@ -31,6 +31,7 @@ func TestHelmHome(t *testing.T) {
} }
isEq(t, hh.String(), "/r/users/helmtest") isEq(t, hh.String(), "/r/users/helmtest")
isEq(t, hh.Registry(), "/r/users/helmtest/registry")
isEq(t, hh.Repository(), "/r/users/helmtest/repository") isEq(t, hh.Repository(), "/r/users/helmtest/repository")
isEq(t, hh.RepositoryFile(), "/r/users/helmtest/repository/repositories.yaml") isEq(t, hh.RepositoryFile(), "/r/users/helmtest/repository/repositories.yaml")
isEq(t, hh.Cache(), "/r/users/helmtest/repository/cache") isEq(t, hh.Cache(), "/r/users/helmtest/repository/cache")

@ -28,6 +28,7 @@ func TestHelmHome(t *testing.T) {
} }
isEq(t, hh.String(), "r:\\users\\helmtest") isEq(t, hh.String(), "r:\\users\\helmtest")
isEq(t, hh.Registry(), "r:\\users\\helmtest\\registry")
isEq(t, hh.Repository(), "r:\\users\\helmtest\\repository") isEq(t, hh.Repository(), "r:\\users\\helmtest\\repository")
isEq(t, hh.RepositoryFile(), "r:\\users\\helmtest\\repository\\repositories.yaml") isEq(t, hh.RepositoryFile(), "r:\\users\\helmtest\\repository\\repositories.yaml")
isEq(t, hh.Cache(), "r:\\users\\helmtest\\repository\\cache") isEq(t, hh.Cache(), "r:\\users\\helmtest\\repository\\cache")

@ -0,0 +1,493 @@
/*
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"
)
var (
tableHeaders = []string{"name", "version", "digest", "size", "created"}
)
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()
// If metadata does not contain Name or Version, an error is returned
// such as "no chart name specified (Chart.yaml)"
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", escape(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
}
printChartSummary(cache.out, metaLayer, contentLayer)
layers := []ocispec.Descriptor{metaLayer, contentLayer}
return layers, nil
}
func (cache *filesystemCache) StoreReference(ref *Reference, layers []ocispec.Descriptor) (bool, error) {
tag := tagOrDefault(ref.Object)
tagDir := mkdir(filepath.Join(cache.rootDir, "refs", escape(ref.Locator), "tags", 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
}
// Create chart file
chartPath, err := createChartFile(filepath.Join(cache.rootDir, "charts"), name, version)
if err != nil {
return false, err
}
// Create chart symlink
err = createSymlink(chartPath, filepath.Join(tagDir, "chart"))
if err != nil {
return false, 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")
}
err = writeFile(metaPath, metaJSONRaw)
if err != nil {
return false, 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)
if err != nil {
return false, err
}
}
// 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
}
func (cache *filesystemCache) DeleteReference(ref *Reference) error {
tagDir := filepath.Join(cache.rootDir, "refs", escape(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() ([][]interface{}, error) {
return getRefsSorted(filepath.Join(cache.rootDir, "refs"))
}
// escape sanitizes a registry URL to remove characters such as ":"
// which are illegal on windows
func escape(s string) string {
return strings.Replace(s, ":", "_", -1)
}
// escape reverses escape
func unescape(s string) string {
return strings.Replace(s, "_", ":", -1)
}
// 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)
}
// fileExists determines if a file exists
func fileExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
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
}
// 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 "<chartsdir>" 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
}
// 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])
}
// tagOrDefault returns the tag if present, if not the default tag
func tagOrDefault(tag string) string {
if tag != "" {
return tag
}
return HelmChartDefaultTag
}
// 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
locator := unescape(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":
// 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)
}
}
// 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]
}
}
return refs, err
}

@ -0,0 +1,159 @@
/*
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 {
c.setDefaultTag(ref)
fmt.Fprintf(c.out, "The push refers to repository [%s]\n", ref.Locator)
layers, err := c.cache.LoadReference(ref)
if err != nil {
return err
}
err = oras.Push(context.Background(), c.resolver, ref.String(), c.cache.store, layers)
if err != nil {
return err
}
var totalSize int64
for _, layer := range layers {
totalSize += layer.Size
}
fmt.Fprintf(c.out,
"%s: pushed to remote (%d layers, %s total)\n", ref.Object, len(layers), byteCountBinary(totalSize))
return nil
}
// PullChart downloads a chart from a registry
func (c *Client) PullChart(ref *Reference) error {
c.setDefaultTag(ref)
fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Object, ref.Locator)
layers, err := oras.Pull(context.Background(), c.resolver, ref.String(), c.cache.store, KnownMediaTypes()...)
if err != nil {
return err
}
exists, err := c.cache.StoreReference(ref, layers)
if err != nil {
return err
}
if !exists {
fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s:%s\n", ref.Locator, ref.Object)
} else {
fmt.Fprintf(c.out, "Status: Chart is up to date for %s:%s\n", ref.Locator, ref.Object)
}
return nil
}
// SaveChart stores a copy of chart in local cache
func (c *Client) SaveChart(ch *chart.Chart, ref *Reference) error {
c.setDefaultTag(ref)
layers, err := c.cache.ChartToLayers(ch)
if err != nil {
return err
}
_, err = c.cache.StoreReference(ref, layers)
if err != nil {
return err
}
fmt.Fprintf(c.out, "%s: saved\n", ref.Object)
return nil
}
// LoadChart retrieves a chart object by reference
func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) {
c.setDefaultTag(ref)
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 {
c.setDefaultTag(ref)
err := c.cache.DeleteReference(ref)
if err != nil {
return err
}
fmt.Fprintf(c.out, "%s: removed\n", ref.Object)
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 {
table.AddRow(row...)
}
fmt.Fprintln(c.out, table.String())
return nil
}
func (c *Client) setDefaultTag(ref *Reference) {
if ref.Object == "" {
ref.Object = HelmChartDefaultTag
fmt.Fprintf(c.out, "Using default tag: %s\n", HelmChartDefaultTag)
}
}

@ -0,0 +1,187 @@
/*
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"
)
var (
testCacheRootDir = "helm-registry-test"
)
type RegistryClientTestSuite struct {
suite.Suite
Out io.Writer
DockerRegistryHost string
CacheRootDir string
RegistryClient *Client
}
func (suite *RegistryClientTestSuite) SetupSuite() {
suite.CacheRootDir = testCacheRootDir
// 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: suite.CacheRootDir,
})
// 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) TearDownSuite() {
os.RemoveAll(suite.CacheRootDir)
}
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
}

@ -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,
}
}

@ -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)
}

@ -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()), "/")
}

@ -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)
}

@ -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
}
)
Loading…
Cancel
Save